2021-03-09 01:01:26 +03:00
/*
* Copyright ( c ) 2021 , Ben Wiederhake < BenWiederhake . GitHub @ gmx . de >
*
2021-04-22 11:24:48 +03:00
* SPDX - License - Identifier : BSD - 2 - Clause
2021-03-09 01:01:26 +03:00
*/
# include <AK/JsonArray.h>
# include <AK/JsonObject.h>
# include <AK/JsonValue.h>
# include <AK/Optional.h>
2021-05-14 18:17:26 +03:00
# include <AK/Random.h>
2021-03-09 01:01:26 +03:00
# include <AK/Vector.h>
# include <LibCore/ArgsParser.h>
# include <LibCore/DateTime.h>
2023-02-09 05:02:46 +03:00
# include <LibCore/File.h>
2021-11-30 00:23:27 +03:00
# include <LibCore/System.h>
# include <LibMain/Main.h>
2021-03-09 01:01:26 +03:00
# include <stdio.h>
# include <stdlib.h>
2021-03-12 19:29:37 +03:00
# include <unistd.h>
2021-03-09 01:01:26 +03:00
class Quote {
public :
2022-04-01 20:58:27 +03:00
static Optional < Quote > try_parse ( JsonValue const & value )
2021-03-09 01:01:26 +03:00
{
if ( ! value . is_object ( ) )
return { } ;
2021-05-31 19:59:02 +03:00
auto & entry = value . as_object ( ) ;
2021-03-09 01:01:26 +03:00
Quote q ;
2022-12-22 17:27:41 +03:00
if ( ! entry . has_string ( " quote " sv ) | | ! entry . has_string ( " author " sv ) | | ! entry . has_u64 ( " utc_time " sv ) | | ! entry . has_string ( " url " sv ) )
2021-03-09 01:01:26 +03:00
return { } ;
// From here on, trust that it's probably fine.
2023-12-16 17:19:34 +03:00
q . m_quote = entry . get_byte_string ( " quote " sv ) . value ( ) ;
q . m_author = entry . get_byte_string ( " author " sv ) . value ( ) ;
2022-12-22 17:27:41 +03:00
q . m_utc_time = entry . get_u64 ( " utc_time " sv ) . value ( ) ;
2023-12-16 17:19:34 +03:00
q . m_url = entry . get_byte_string ( " url " sv ) . value ( ) ;
2022-07-11 20:32:29 +03:00
if ( entry . has ( " context " sv ) )
2023-12-16 17:19:34 +03:00
q . m_context = entry . get_byte_string ( " context " sv ) . value ( ) ;
2021-03-09 01:01:26 +03:00
return q ;
}
2023-12-16 17:19:34 +03:00
ByteString const & quote ( ) const { return m_quote ; }
ByteString const & author ( ) const { return m_author ; }
2022-04-01 20:58:27 +03:00
u64 const & utc_time ( ) const { return m_utc_time ; }
2023-12-16 17:19:34 +03:00
ByteString const & url ( ) const { return m_url ; }
Optional < ByteString > const & context ( ) const { return m_context ; }
2021-03-09 01:01:26 +03:00
private :
Quote ( ) = default ;
2023-12-16 17:19:34 +03:00
ByteString m_quote ;
ByteString m_author ;
2021-03-09 01:01:26 +03:00
u64 m_utc_time ;
2023-12-16 17:19:34 +03:00
ByteString m_url ;
Optional < ByteString > m_context ;
2021-03-09 01:01:26 +03:00
} ;
2022-04-01 20:58:27 +03:00
static Vector < Quote > parse_all ( JsonArray const & array )
2021-03-09 01:01:26 +03:00
{
Vector < Quote > quotes ;
2021-06-28 12:57:37 +03:00
for ( size_t i = 0 ; i < array . size ( ) ; + + i ) {
2021-03-09 01:01:26 +03:00
Optional < Quote > q = Quote : : try_parse ( array [ i ] ) ;
if ( ! q . has_value ( ) ) {
warnln ( " WARNING: Could not parse quote #{}! " , i ) ;
} else {
quotes . append ( q . value ( ) ) ;
}
}
return quotes ;
}
2021-11-30 00:23:27 +03:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
2021-03-09 01:01:26 +03:00
{
2021-11-30 00:23:27 +03:00
TRY ( Core : : System : : pledge ( " stdio rpath " ) ) ;
2021-03-09 01:01:26 +03:00
2022-09-14 14:35:11 +03:00
StringView path = " /res/fortunes.json " sv ;
2021-03-09 01:01:26 +03:00
2023-08-05 20:38:55 +03:00
Optional < bool > force_color ;
2021-03-09 01:01:26 +03:00
Core : : ArgsParser args_parser ;
2021-04-18 11:30:03 +03:00
args_parser . set_general_help ( " Open a fortune cookie, receive a free quote for the day! " ) ;
2023-08-05 20:38:55 +03:00
args_parser . add_option ( Core : : ArgsParser : : Option {
. argument_mode = Core : : ArgsParser : : OptionArgumentMode : : Required ,
. help_string = " Chose when to color the output. Valid options are always, never and auto (default). When color is set to auto, color codes will be emitted when stdout is a terminal " ,
. long_name = " color " ,
. value_name = " when " ,
. accept_value = [ & force_color ] ( StringView color_when_string ) {
if ( color_when_string . equals_ignoring_ascii_case ( " always " sv ) ) {
force_color = true ;
} else if ( color_when_string . equals_ignoring_ascii_case ( " never " sv ) ) {
force_color = false ;
} else if ( ! color_when_string . equals_ignoring_ascii_case ( " auto " sv ) ) {
warnln ( " Unknown argument '{}'. Valid arguments for --color are always, never and auto " , color_when_string ) ;
return false ;
}
return true ;
} ,
} ) ;
2021-03-09 01:01:26 +03:00
args_parser . add_positional_argument ( path , " Path to JSON file with quotes (/res/fortunes.json by default) " , " path " , Core : : ArgsParser : : Required : : No ) ;
2021-11-30 00:23:27 +03:00
args_parser . parse ( arguments ) ;
2021-03-09 01:01:26 +03:00
2023-02-09 05:02:46 +03:00
auto file = TRY ( Core : : File : : open ( path , Core : : File : : OpenMode : : Read ) ) ;
2021-03-09 01:01:26 +03:00
2022-01-20 20:27:56 +03:00
TRY ( Core : : System : : unveil ( " /etc/timezone " , " r " ) ) ;
2021-11-30 00:23:27 +03:00
TRY ( Core : : System : : unveil ( nullptr , nullptr ) ) ;
2021-03-09 01:01:26 +03:00
2022-12-11 19:49:00 +03:00
auto file_contents = TRY ( file - > read_until_eof ( ) ) ;
2021-11-30 00:23:27 +03:00
auto json = TRY ( JsonValue : : from_string ( file_contents ) ) ;
if ( ! json . is_array ( ) ) {
2021-03-09 01:01:26 +03:00
warnln ( " {} does not contain an array of quotes " , path ) ;
return 1 ;
}
2022-04-01 20:58:27 +03:00
auto const quotes = parse_all ( json . as_array ( ) ) ;
2021-03-09 01:01:26 +03:00
if ( quotes . is_empty ( ) ) {
warnln ( " {} does not contain any valid quotes " , path ) ;
return 1 ;
}
2021-05-14 18:17:26 +03:00
u32 i = get_random_uniform ( quotes . size ( ) ) ;
2022-04-01 20:58:27 +03:00
auto const & chosen_quote = quotes [ i ] ;
2021-03-09 01:01:26 +03:00
auto datetime = Core : : DateTime : : from_timestamp ( chosen_quote . utc_time ( ) ) ;
2023-08-05 20:38:55 +03:00
auto stdout_is_tty = TRY ( Core : : System : : isatty ( STDOUT_FILENO ) ) ;
auto show_color = force_color . has_value ( ) ? force_color . value ( ) : stdout_is_tty ;
if ( stdout_is_tty ) {
outln ( ) ; // Tasteful spacing
out ( " \033 ]8;;{} \033 \\ " , chosen_quote . url ( ) ) ; // Begin link
}
if ( show_color ) {
2023-12-16 17:19:34 +03:00
out ( " \033 [34m({}) \033 [m " , datetime . to_byte_string ( ) ) ;
2023-08-05 20:38:55 +03:00
out ( " \033 [34;1m<{}> \033 [m " , chosen_quote . author ( ) ) ;
out ( " \033 [32m{} \033 [m " , chosen_quote . quote ( ) ) ;
} else {
2023-12-16 17:19:34 +03:00
out ( " ({}) " , datetime . to_byte_string ( ) ) ;
2023-08-05 20:38:55 +03:00
out ( " <{}> " , chosen_quote . author ( ) ) ;
out ( " {} " , chosen_quote . quote ( ) ) ;
}
2021-03-09 01:01:26 +03:00
2023-08-05 20:38:55 +03:00
if ( stdout_is_tty )
out ( " \033 ]8;; \033 \\ " ) ; // End link
2021-03-09 01:01:26 +03:00
outln ( ) ;
if ( chosen_quote . context ( ) . has_value ( ) )
2023-08-05 20:38:55 +03:00
outln ( " {} " , chosen_quote . context ( ) . value ( ) ) ;
2021-03-09 01:01:26 +03:00
2023-08-05 20:38:55 +03:00
if ( stdout_is_tty )
outln ( ) ; // Tasteful spacing
2021-03-09 01:01:26 +03:00
return 0 ;
}