2022-01-17 20:53:17 -03:00
/*
* Copyright ( c ) 2022 , the SerenityOS developers .
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <AK/SourceGenerator.h>
2023-11-07 21:21:53 -03:00
# include <AK/String.h>
2022-01-17 20:53:17 -03:00
# include <LibCore/ArgsParser.h>
2023-02-08 23:02:46 -03:00
# include <LibCore/File.h>
2022-01-17 20:53:17 -03:00
struct ApprovalDate {
unsigned year ;
unsigned month ;
unsigned day ;
} ;
struct PnpIdData {
2023-11-07 21:23:15 -03:00
String manufacturer_name ;
2022-01-17 20:53:17 -03:00
ApprovalDate approval_date ;
} ;
2023-11-07 21:23:15 -03:00
static ErrorOr < ApprovalDate > parse_approval_date ( StringView date )
2022-01-17 20:53:17 -03:00
{
2023-11-07 21:23:15 -03:00
auto parts = date . trim_whitespace ( ) . split_view ( ' / ' , SplitBehavior : : KeepEmpty ) ;
2022-01-17 20:53:17 -03:00
if ( parts . size ( ) ! = 3 )
2022-07-11 14:57:32 -03:00
return Error : : from_string_literal ( " Failed to parse approval date parts (mm/dd/yyyy) " ) ;
2022-01-17 20:53:17 -03:00
2023-12-22 23:59:14 -03:00
auto month = parts [ 0 ] . to_number < unsigned > ( ) ;
2022-01-17 20:53:17 -03:00
if ( ! month . has_value ( ) )
2022-07-11 14:57:32 -03:00
return Error : : from_string_literal ( " Failed to parse month from approval date " ) ;
2022-01-17 20:53:17 -03:00
if ( month . value ( ) = = 0 | | month . value ( ) > 12 )
2022-07-11 14:57:32 -03:00
return Error : : from_string_literal ( " Invalid month in approval date " ) ;
2022-01-17 20:53:17 -03:00
2023-12-22 23:59:14 -03:00
auto day = parts [ 1 ] . to_number < unsigned > ( ) ;
2022-01-17 20:53:17 -03:00
if ( ! day . has_value ( ) )
2022-07-11 14:57:32 -03:00
return Error : : from_string_literal ( " Failed to parse day from approval date " ) ;
2022-01-17 20:53:17 -03:00
if ( day . value ( ) = = 0 | | day . value ( ) > 31 )
2022-07-11 14:57:32 -03:00
return Error : : from_string_literal ( " Invalid day in approval date " ) ;
2022-01-17 20:53:17 -03:00
2023-12-22 23:59:14 -03:00
auto year = parts [ 2 ] . to_number < unsigned > ( ) ;
2022-01-17 20:53:17 -03:00
if ( ! year . has_value ( ) )
2022-07-11 14:57:32 -03:00
return Error : : from_string_literal ( " Failed to parse year from approval date " ) ;
2022-01-17 20:53:17 -03:00
if ( year . value ( ) < 1900 | | year . value ( ) > 2999 )
2022-07-11 14:57:32 -03:00
return Error : : from_string_literal ( " Invalid year approval date " ) ;
2022-01-17 20:53:17 -03:00
return ApprovalDate { . year = year . value ( ) , . month = month . value ( ) , . day = day . value ( ) } ;
}
2023-11-07 21:23:15 -03:00
static ErrorOr < HashMap < String , PnpIdData > > parse_pnp_ids_database ( Core : : InputBufferedFile & pnp_ids_file )
2022-01-17 20:53:17 -03:00
{
2023-11-07 21:23:15 -03:00
HashMap < String , PnpIdData > pnp_id_data ;
Array < u8 , 1024 > buffer ;
// The first line is just a header.
( void ) TRY ( pnp_ids_file . read_line ( buffer ) ) ;
while ( TRY ( pnp_ids_file . can_read_line ( ) ) ) {
auto line = TRY ( pnp_ids_file . read_line ( buffer ) ) ;
2022-01-17 20:53:17 -03:00
2023-11-07 21:23:15 -03:00
auto segments = line . split_view ( ' , ' ) ;
VERIFY ( segments . size ( ) > = 3 ) ;
2022-01-17 20:53:17 -03:00
2023-11-07 21:23:15 -03:00
auto approval_date = TRY ( parse_approval_date ( segments . take_last ( ) ) ) ;
auto manufacturer_id = MUST ( String : : from_utf8 ( segments . take_last ( ) ) ) ;
auto manufacturer_name = MUST ( MUST ( String : : join ( ' , ' , segments ) ) . trim ( " \" " sv ) ) ;
2022-01-17 20:53:17 -03:00
2023-11-07 21:23:15 -03:00
pnp_id_data . set ( move ( manufacturer_id ) , { . manufacturer_name = move ( manufacturer_name ) , . approval_date = approval_date } ) ;
2022-01-17 20:53:17 -03:00
}
if ( pnp_id_data . size ( ) < = 1 )
2022-07-11 14:57:32 -03:00
return Error : : from_string_literal ( " Expected more than one row " ) ;
2022-01-17 20:53:17 -03:00
return pnp_id_data ;
}
2023-11-07 21:23:15 -03:00
static ErrorOr < void > generate_header ( Core : : InputBufferedFile & file )
2022-01-17 20:53:17 -03:00
{
StringBuilder builder ;
SourceGenerator generator { builder } ;
generator . append ( R " ~~~(
# pragma once
# include <AK/Function.h>
# include <AK/StringView.h>
# include <AK/Types.h>
namespace PnpIDs {
struct PnpIDData {
StringView manufacturer_id ;
StringView manufacturer_name ;
struct {
2023-11-07 21:19:45 -03:00
u16 year { 0 } ;
u8 month { 0 } ;
u8 day { 0 } ;
2022-01-17 20:53:17 -03:00
} approval_date ;
} ;
Optional < PnpIDData > find_by_manufacturer_id ( StringView ) ;
IterationDecision for_each ( Function < IterationDecision ( PnpIDData const & ) > ) ;
}
) ~ ~ ~ " );
2023-03-01 12:28:32 -03:00
TRY ( file . write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
2022-09-12 11:47:02 -03:00
return { } ;
2022-01-17 20:53:17 -03:00
}
2023-11-07 21:23:15 -03:00
static ErrorOr < void > generate_source ( Core : : InputBufferedFile & file , HashMap < String , PnpIdData > const & pnp_ids )
2022-01-17 20:53:17 -03:00
{
StringBuilder builder ;
SourceGenerator generator { builder } ;
generator . append ( R " ~~~(
2023-06-01 11:17:13 -03:00
# include <LibEDID/PnpIDs.h>
2022-01-17 20:53:17 -03:00
namespace PnpIDs {
2023-11-07 21:19:45 -03:00
static constexpr PnpIDData s_pnp_ids [ ] = { ) ~ ~ ~ " );
2022-01-17 20:53:17 -03:00
for ( auto & pnp_id_data : pnp_ids ) {
generator . set ( " manufacturer_id " , pnp_id_data . key ) ;
generator . set ( " manufacturer_name " , pnp_id_data . value . manufacturer_name ) ;
2023-11-07 21:21:53 -03:00
generator . set ( " approval_year " , MUST ( String : : number ( pnp_id_data . value . approval_date . year ) ) ) ;
generator . set ( " approval_month " , MUST ( String : : number ( pnp_id_data . value . approval_date . month ) ) ) ;
generator . set ( " approval_day " , MUST ( String : : number ( pnp_id_data . value . approval_date . day ) ) ) ;
2022-01-17 20:53:17 -03:00
generator . append ( R " ~~~(
2023-11-07 21:19:45 -03:00
{ " @manufacturer_id@ " sv , " @manufacturer_name@ " sv , { @ approval_year @ , @ approval_month @ , @ approval_day @ } } , ) ~ ~ ~ " );
2022-01-17 20:53:17 -03:00
}
generator . append ( R " ~~~(
} ;
Optional < PnpIDData > find_by_manufacturer_id ( StringView manufacturer_id )
{
for ( auto & pnp_data : s_pnp_ids ) {
if ( pnp_data . manufacturer_id = = manufacturer_id )
return pnp_data ;
}
return { } ;
}
IterationDecision for_each ( Function < IterationDecision ( PnpIDData const & ) > callback )
{
for ( auto & pnp_data : s_pnp_ids ) {
auto decision = callback ( pnp_data ) ;
if ( decision ! = IterationDecision : : Continue )
return decision ;
}
return IterationDecision : : Continue ;
}
}
) ~ ~ ~ " );
2023-03-01 12:28:32 -03:00
TRY ( file . write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
2022-09-12 11:47:02 -03:00
return { } ;
2022-01-17 20:53:17 -03:00
}
ErrorOr < int > serenity_main ( Main : : Arguments arguments )
{
StringView generated_header_path ;
StringView generated_implementation_path ;
StringView pnp_ids_file_path ;
Core : : ArgsParser args_parser ;
args_parser . add_option ( generated_header_path , " Path to the header file to generate " , " generated-header-path " , ' h ' , " generated-header-path " ) ;
args_parser . add_option ( generated_implementation_path , " Path to the implementation file to generate " , " generated-implementation-path " , ' c ' , " generated-implementation-path " ) ;
args_parser . add_option ( pnp_ids_file_path , " Path to the input PNP ID database file " , " pnp-ids-file " , ' p ' , " pnp-ids-file " ) ;
args_parser . parse ( arguments ) ;
2023-11-07 21:23:15 -03:00
auto open_file = [ & ] ( StringView path , Core : : File : : OpenMode mode = Core : : File : : OpenMode : : Read ) - > ErrorOr < NonnullOwnPtr < Core : : InputBufferedFile > > {
2022-01-17 20:53:17 -03:00
if ( path . is_empty ( ) ) {
2023-02-21 08:44:41 -03:00
args_parser . print_usage ( stderr , arguments . strings [ 0 ] ) ;
2022-07-11 14:57:32 -03:00
return Error : : from_string_literal ( " Must provide all command line options " ) ;
2022-01-17 20:53:17 -03:00
}
2023-11-07 21:23:15 -03:00
auto file = TRY ( Core : : File : : open ( path , mode ) ) ;
return Core : : InputBufferedFile : : create ( move ( file ) ) ;
2022-01-17 20:53:17 -03:00
} ;
2023-02-08 23:02:46 -03:00
auto generated_header_file = TRY ( open_file ( generated_header_path , Core : : File : : OpenMode : : ReadWrite ) ) ;
auto generated_implementation_file = TRY ( open_file ( generated_implementation_path , Core : : File : : OpenMode : : ReadWrite ) ) ;
2022-01-17 20:53:17 -03:00
auto pnp_ids_file = TRY ( open_file ( pnp_ids_file_path ) ) ;
auto pnp_id_map = TRY ( parse_pnp_ids_database ( * pnp_ids_file ) ) ;
2023-11-07 21:21:53 -03:00
TRY ( generate_header ( * generated_header_file ) ) ;
2022-09-12 11:47:02 -03:00
TRY ( generate_source ( * generated_implementation_file , pnp_id_map ) ) ;
2023-11-07 21:21:53 -03:00
2022-01-17 20:53:17 -03:00
return 0 ;
}