2022-03-08 15:00:27 +00:00
/*
* Copyright ( c ) 2022 , Sam Atkins < atkinssj @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include "GeneratorUtil.h"
# include <AK/SourceGenerator.h>
# include <AK/StringBuilder.h>
2022-04-01 17:19:09 +01:00
# include <LibCore/ArgsParser.h>
2022-03-08 15:00:27 +00:00
# include <LibMain/Main.h>
2023-02-09 03:02:46 +01:00
ErrorOr < void > generate_header_file ( JsonObject & media_feature_data , Core : : File & file ) ;
ErrorOr < void > generate_implementation_file ( JsonObject & media_feature_data , Core : : File & file ) ;
2022-04-01 17:19:09 +01:00
2022-03-08 15:00:27 +00:00
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
{
2022-04-01 17:19:09 +01:00
StringView generated_header_path ;
StringView generated_implementation_path ;
StringView media_features_json_path ;
Core : : ArgsParser args_parser ;
args_parser . add_option ( generated_header_path , " Path to the MediaFeatureID header file to generate " , " generated-header-path " , ' h ' , " generated-header-path " ) ;
args_parser . add_option ( generated_implementation_path , " Path to the MediaFeatureID implementation file to generate " , " generated-implementation-path " , ' c ' , " generated-implementation-path " ) ;
args_parser . add_option ( media_features_json_path , " Path to the JSON file to read from " , " json-path " , ' j ' , " json-path " ) ;
args_parser . parse ( arguments ) ;
2022-03-08 15:00:27 +00:00
2022-04-01 17:19:09 +01:00
auto json = TRY ( read_entire_file_as_json ( media_features_json_path ) ) ;
2022-03-08 15:00:27 +00:00
VERIFY ( json . is_object ( ) ) ;
2022-04-01 17:19:09 +01:00
auto media_feature_data = json . as_object ( ) ;
2023-02-09 03:02:46 +01:00
auto generated_header_file = TRY ( Core : : File : : open ( generated_header_path , Core : : File : : OpenMode : : Write ) ) ;
auto generated_implementation_file = TRY ( Core : : File : : open ( generated_implementation_path , Core : : File : : OpenMode : : Write ) ) ;
2022-04-01 17:19:09 +01:00
TRY ( generate_header_file ( media_feature_data , * generated_header_file ) ) ;
TRY ( generate_implementation_file ( media_feature_data , * generated_implementation_file ) ) ;
return 0 ;
}
2023-02-09 03:02:46 +01:00
ErrorOr < void > generate_header_file ( JsonObject & media_feature_data , Core : : File & file )
2022-04-01 17:19:09 +01:00
{
StringBuilder builder ;
SourceGenerator generator { builder } ;
generator . append ( R " ~~~(#pragma once
# include <AK/StringView.h>
# include <AK/Traits.h>
# include <LibWeb/CSS/ValueID.h>
namespace Web : : CSS {
enum class MediaFeatureValueType {
Boolean ,
Integer ,
Length ,
Ratio ,
Resolution ,
} ;
enum class MediaFeatureID { ) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
TRY ( media_feature_data . try_for_each_member ( [ & ] ( auto & name , auto & ) - > ErrorOr < void > {
2023-06-17 13:12:36 +01:00
auto member_generator = TRY ( generator . fork ( ) ) ;
2023-06-17 12:40:43 +01:00
TRY ( member_generator . set ( " name:titlecase " , TRY ( title_casify ( name ) ) ) ) ;
2022-04-01 17:19:09 +01:00
member_generator . append ( R " ~~~(
@ name : titlecase @ , ) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
return { } ;
} ) ) ;
2022-04-01 17:19:09 +01:00
generator . append ( R " ~~~(
} ;
Optional < MediaFeatureID > media_feature_id_from_string ( StringView ) ;
2023-01-06 19:02:26 +01:00
StringView string_from_media_feature_id ( MediaFeatureID ) ;
2022-04-01 17:19:09 +01:00
bool media_feature_type_is_range ( MediaFeatureID ) ;
bool media_feature_accepts_type ( MediaFeatureID , MediaFeatureValueType ) ;
bool media_feature_accepts_identifier ( MediaFeatureID , ValueID ) ;
}
) ~ ~ ~ " );
2023-03-01 16:28:32 +01:00
TRY ( file . write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
2022-04-01 17:19:09 +01:00
return { } ;
}
2022-03-08 15:00:27 +00:00
2023-02-09 03:02:46 +01:00
ErrorOr < void > generate_implementation_file ( JsonObject & media_feature_data , Core : : File & file )
2022-04-01 17:19:09 +01:00
{
2022-03-08 15:00:27 +00:00
StringBuilder builder ;
SourceGenerator generator { builder } ;
2023-02-17 16:43:28 +00:00
generator . append ( R " ~~~(
# include <LibWeb/CSS/MediaFeatureID.h>
# include <LibWeb/Infra/Strings.h>
2022-03-08 15:00:27 +00:00
namespace Web : : CSS {
Optional < MediaFeatureID > media_feature_id_from_string ( StringView string )
{ ) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
TRY ( media_feature_data . try_for_each_member ( [ & ] ( auto & name , auto & ) - > ErrorOr < void > {
2023-06-17 13:12:36 +01:00
auto member_generator = TRY ( generator . fork ( ) ) ;
2022-03-08 15:00:27 +00:00
member_generator . set ( " name " , name ) ;
2023-06-17 12:40:43 +01:00
TRY ( member_generator . set ( " name:titlecase " , TRY ( title_casify ( name ) ) ) ) ;
2022-03-08 15:00:27 +00:00
member_generator . append ( R " ~~~(
2023-02-17 16:43:28 +00:00
if ( Infra : : is_ascii_case_insensitive_match ( string , " @name@ " sv ) )
2022-03-08 15:00:27 +00:00
return MediaFeatureID : : @ name : titlecase @ ;
) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
return { } ;
} ) ) ;
2022-03-08 15:00:27 +00:00
generator . append ( R " ~~~(
return { } ;
}
2023-01-06 19:02:26 +01:00
StringView string_from_media_feature_id ( MediaFeatureID media_feature_id )
2022-03-08 15:00:27 +00:00
{
switch ( media_feature_id ) { ) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
TRY ( media_feature_data . try_for_each_member ( [ & ] ( auto & name , auto & ) - > ErrorOr < void > {
2023-06-17 13:12:36 +01:00
auto member_generator = TRY ( generator . fork ( ) ) ;
2022-03-08 15:00:27 +00:00
member_generator . set ( " name " , name ) ;
2023-06-17 12:40:43 +01:00
TRY ( member_generator . set ( " name:titlecase " , TRY ( title_casify ( name ) ) ) ) ;
2022-03-08 15:00:27 +00:00
member_generator . append ( R " ~~~(
case MediaFeatureID : : @ name : titlecase @ :
2023-01-06 19:02:26 +01:00
return " @name@ " sv ; ) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
return { } ;
} ) ) ;
2022-03-08 15:00:27 +00:00
generator . append ( R " ~~~(
}
VERIFY_NOT_REACHED ( ) ;
}
2022-03-08 17:39:08 +00:00
bool media_feature_type_is_range ( MediaFeatureID media_feature_id )
{
switch ( media_feature_id ) { ) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
TRY ( media_feature_data . try_for_each_member ( [ & ] ( auto & name , auto & value ) - > ErrorOr < void > {
2022-03-08 17:39:08 +00:00
VERIFY ( value . is_object ( ) ) ;
auto & feature = value . as_object ( ) ;
2023-06-17 13:12:36 +01:00
auto member_generator = TRY ( generator . fork ( ) ) ;
2023-06-17 12:40:43 +01:00
TRY ( member_generator . set ( " name:titlecase " , TRY ( title_casify ( name ) ) ) ) ;
2022-07-11 17:32:29 +00:00
VERIFY ( feature . has ( " type " sv ) ) ;
2022-12-21 14:37:27 +00:00
auto feature_type = feature . get_deprecated_string ( " type " sv ) ;
VERIFY ( feature_type . has_value ( ) ) ;
member_generator . set ( " is_range " , feature_type . value ( ) = = " range " ? " true " : " false " ) ;
2022-03-08 17:39:08 +00:00
member_generator . append ( R " ~~~(
case MediaFeatureID : : @ name : titlecase @ :
return @ is_range @ ; ) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
return { } ;
} ) ) ;
2022-03-08 17:39:08 +00:00
generator . append ( R " ~~~(
}
VERIFY_NOT_REACHED ( ) ;
}
2022-03-08 20:42:57 +00:00
bool media_feature_accepts_type ( MediaFeatureID media_feature_id , MediaFeatureValueType value_type )
{
switch ( media_feature_id ) { ) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
TRY ( media_feature_data . try_for_each_member ( [ & ] ( auto & name , auto & member ) - > ErrorOr < void > {
2022-03-08 20:42:57 +00:00
VERIFY ( member . is_object ( ) ) ;
auto & feature = member . as_object ( ) ;
2023-06-17 13:12:36 +01:00
auto member_generator = TRY ( generator . fork ( ) ) ;
2023-06-17 12:40:43 +01:00
TRY ( member_generator . set ( " name:titlecase " , TRY ( title_casify ( name ) ) ) ) ;
2022-03-08 20:42:57 +00:00
member_generator . append ( R " ~~~(
case MediaFeatureID : : @ name : titlecase @ : ) ~ ~ ~ " );
bool have_output_value_type_switch = false ;
2022-07-11 17:32:29 +00:00
if ( feature . has ( " values " sv ) ) {
2022-03-08 20:42:57 +00:00
auto append_value_type_switch_if_needed = [ & ] ( ) {
if ( ! have_output_value_type_switch ) {
member_generator . append ( R " ~~~(
switch ( value_type ) { ) ~ ~ ~ " );
}
have_output_value_type_switch = true ;
} ;
2022-12-21 14:37:27 +00:00
auto values = feature . get_array ( " values " sv ) ;
VERIFY ( values . has_value ( ) ) ;
auto & values_array = values . value ( ) ;
2022-03-08 20:42:57 +00:00
for ( auto & type : values_array . values ( ) ) {
VERIFY ( type . is_string ( ) ) ;
auto type_name = type . as_string ( ) ;
// Skip identifiers.
if ( type_name [ 0 ] ! = ' < ' )
continue ;
if ( type_name = = " <mq-boolean> " ) {
append_value_type_switch_if_needed ( ) ;
member_generator . append ( R " ~~~(
case MediaFeatureValueType : : Boolean :
return true ; ) ~ ~ ~ " );
} else if ( type_name = = " <integer> " ) {
append_value_type_switch_if_needed ( ) ;
member_generator . append ( R " ~~~(
case MediaFeatureValueType : : Integer :
return true ; ) ~ ~ ~ " );
} else if ( type_name = = " <length> " ) {
append_value_type_switch_if_needed ( ) ;
member_generator . append ( R " ~~~(
case MediaFeatureValueType : : Length :
return true ; ) ~ ~ ~ " );
} else if ( type_name = = " <ratio> " ) {
append_value_type_switch_if_needed ( ) ;
member_generator . append ( R " ~~~(
case MediaFeatureValueType : : Ratio :
return true ; ) ~ ~ ~ " );
} else if ( type_name = = " <resolution> " ) {
append_value_type_switch_if_needed ( ) ;
member_generator . append ( R " ~~~(
case MediaFeatureValueType : : Resolution :
return true ; ) ~ ~ ~ " );
} else {
warnln ( " Unrecognized media-feature value type: `{}` " , type_name ) ;
VERIFY_NOT_REACHED ( ) ;
}
}
}
if ( have_output_value_type_switch ) {
member_generator . append ( R " ~~~(
default :
return false ;
} ) ~ ~ ~ " );
} else {
member_generator . append ( R " ~~~(
return false ; ) ~ ~ ~ " );
}
2023-06-17 12:40:43 +01:00
return { } ;
} ) ) ;
2022-03-08 20:42:57 +00:00
generator . append ( R " ~~~(
}
VERIFY_NOT_REACHED ( ) ;
}
bool media_feature_accepts_identifier ( MediaFeatureID media_feature_id , ValueID identifier )
{
switch ( media_feature_id ) { ) ~ ~ ~ " );
2023-06-17 12:40:43 +01:00
TRY ( media_feature_data . try_for_each_member ( [ & ] ( auto & name , auto & member ) - > ErrorOr < void > {
2022-03-08 20:42:57 +00:00
VERIFY ( member . is_object ( ) ) ;
auto & feature = member . as_object ( ) ;
2023-06-17 13:12:36 +01:00
auto member_generator = TRY ( generator . fork ( ) ) ;
2023-06-17 12:40:43 +01:00
TRY ( member_generator . set ( " name:titlecase " , TRY ( title_casify ( name ) ) ) ) ;
2022-03-08 20:42:57 +00:00
member_generator . append ( R " ~~~(
case MediaFeatureID : : @ name : titlecase @ : ) ~ ~ ~ " );
bool have_output_identifier_switch = false ;
2022-07-11 17:32:29 +00:00
if ( feature . has ( " values " sv ) ) {
2022-03-08 20:42:57 +00:00
auto append_identifier_switch_if_needed = [ & ] ( ) {
if ( ! have_output_identifier_switch ) {
member_generator . append ( R " ~~~(
switch ( identifier ) { ) ~ ~ ~ " );
}
have_output_identifier_switch = true ;
} ;
2022-12-21 14:37:27 +00:00
auto values = feature . get_array ( " values " sv ) ;
VERIFY ( values . has_value ( ) ) ;
auto & values_array = values . value ( ) ;
2022-03-08 20:42:57 +00:00
for ( auto & identifier : values_array . values ( ) ) {
VERIFY ( identifier . is_string ( ) ) ;
auto identifier_name = identifier . as_string ( ) ;
// Skip types.
if ( identifier_name [ 0 ] = = ' < ' )
continue ;
append_identifier_switch_if_needed ( ) ;
2023-06-17 13:12:36 +01:00
auto ident_generator = TRY ( member_generator . fork ( ) ) ;
2023-06-17 12:40:43 +01:00
TRY ( ident_generator . set ( " identifier:titlecase " , TRY ( title_casify ( identifier_name ) ) ) ) ;
2022-03-08 20:42:57 +00:00
ident_generator . append ( R " ~~~(
case ValueID : : @ identifier : titlecase @ :
return true ; ) ~ ~ ~ " );
}
}
if ( have_output_identifier_switch ) {
member_generator . append ( R " ~~~(
default :
return false ;
} ) ~ ~ ~ " );
} else {
member_generator . append ( R " ~~~(
return false ; ) ~ ~ ~ " );
}
2023-06-17 12:40:43 +01:00
return { } ;
} ) ) ;
2022-03-08 20:42:57 +00:00
generator . append ( R " ~~~(
}
VERIFY_NOT_REACHED ( ) ;
}
2022-03-08 15:00:27 +00:00
}
) ~ ~ ~ " );
2023-03-01 16:28:32 +01:00
TRY ( file . write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
2022-04-01 17:19:09 +01:00
return { } ;
2022-03-08 15:00:27 +00:00
}