GeneratePnpIDs.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. /*
  2. * Copyright (c) 2022, the SerenityOS developers.
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/SourceGenerator.h>
  7. #include <AK/String.h>
  8. #include <LibCore/ArgsParser.h>
  9. #include <LibCore/File.h>
  10. struct ApprovalDate {
  11. unsigned year;
  12. unsigned month;
  13. unsigned day;
  14. };
  15. struct PnpIdData {
  16. String manufacturer_name;
  17. ApprovalDate approval_date;
  18. };
  19. static ErrorOr<ApprovalDate> parse_approval_date(StringView date)
  20. {
  21. auto parts = date.trim_whitespace().split_view('/', SplitBehavior::KeepEmpty);
  22. if (parts.size() != 3)
  23. return Error::from_string_literal("Failed to parse approval date parts (mm/dd/yyyy)");
  24. auto month = parts[0].to_number<unsigned>();
  25. if (!month.has_value())
  26. return Error::from_string_literal("Failed to parse month from approval date");
  27. if (month.value() == 0 || month.value() > 12)
  28. return Error::from_string_literal("Invalid month in approval date");
  29. auto day = parts[1].to_number<unsigned>();
  30. if (!day.has_value())
  31. return Error::from_string_literal("Failed to parse day from approval date");
  32. if (day.value() == 0 || day.value() > 31)
  33. return Error::from_string_literal("Invalid day in approval date");
  34. auto year = parts[2].to_number<unsigned>();
  35. if (!year.has_value())
  36. return Error::from_string_literal("Failed to parse year from approval date");
  37. if (year.value() < 1900 || year.value() > 2999)
  38. return Error::from_string_literal("Invalid year approval date");
  39. return ApprovalDate { .year = year.value(), .month = month.value(), .day = day.value() };
  40. }
  41. static ErrorOr<HashMap<String, PnpIdData>> parse_pnp_ids_database(Core::InputBufferedFile& pnp_ids_file)
  42. {
  43. HashMap<String, PnpIdData> pnp_id_data;
  44. Array<u8, 1024> buffer;
  45. // The first line is just a header.
  46. (void)TRY(pnp_ids_file.read_line(buffer));
  47. while (TRY(pnp_ids_file.can_read_line())) {
  48. auto line = TRY(pnp_ids_file.read_line(buffer));
  49. auto segments = line.split_view(',');
  50. VERIFY(segments.size() >= 3);
  51. auto approval_date = TRY(parse_approval_date(segments.take_last()));
  52. auto manufacturer_id = MUST(String::from_utf8(segments.take_last()));
  53. auto manufacturer_name = MUST(MUST(String::join(',', segments)).trim("\""sv));
  54. pnp_id_data.set(move(manufacturer_id), { .manufacturer_name = move(manufacturer_name), .approval_date = approval_date });
  55. }
  56. if (pnp_id_data.size() <= 1)
  57. return Error::from_string_literal("Expected more than one row");
  58. return pnp_id_data;
  59. }
  60. static ErrorOr<void> generate_header(Core::InputBufferedFile& file)
  61. {
  62. StringBuilder builder;
  63. SourceGenerator generator { builder };
  64. generator.append(R"~~~(
  65. #pragma once
  66. #include <AK/Function.h>
  67. #include <AK/StringView.h>
  68. #include <AK/Types.h>
  69. namespace PnpIDs {
  70. struct PnpIDData {
  71. StringView manufacturer_id;
  72. StringView manufacturer_name;
  73. struct {
  74. u16 year { 0 };
  75. u8 month { 0 };
  76. u8 day { 0 };
  77. } approval_date;
  78. };
  79. Optional<PnpIDData> find_by_manufacturer_id(StringView);
  80. IterationDecision for_each(Function<IterationDecision(PnpIDData const&)>);
  81. }
  82. )~~~");
  83. TRY(file.write_until_depleted(generator.as_string_view().bytes()));
  84. return {};
  85. }
  86. static ErrorOr<void> generate_source(Core::InputBufferedFile& file, HashMap<String, PnpIdData> const& pnp_ids)
  87. {
  88. StringBuilder builder;
  89. SourceGenerator generator { builder };
  90. generator.append(R"~~~(
  91. #include <LibEDID/PnpIDs.h>
  92. namespace PnpIDs {
  93. static constexpr PnpIDData s_pnp_ids[] = {)~~~");
  94. for (auto& pnp_id_data : pnp_ids) {
  95. generator.set("manufacturer_id", pnp_id_data.key);
  96. generator.set("manufacturer_name", pnp_id_data.value.manufacturer_name);
  97. generator.set("approval_year", MUST(String::number(pnp_id_data.value.approval_date.year)));
  98. generator.set("approval_month", MUST(String::number(pnp_id_data.value.approval_date.month)));
  99. generator.set("approval_day", MUST(String::number(pnp_id_data.value.approval_date.day)));
  100. generator.append(R"~~~(
  101. { "@manufacturer_id@"sv, "@manufacturer_name@"sv, { @approval_year@, @approval_month@, @approval_day@ } },)~~~");
  102. }
  103. generator.append(R"~~~(
  104. };
  105. Optional<PnpIDData> find_by_manufacturer_id(StringView manufacturer_id)
  106. {
  107. for (auto& pnp_data : s_pnp_ids) {
  108. if (pnp_data.manufacturer_id == manufacturer_id)
  109. return pnp_data;
  110. }
  111. return {};
  112. }
  113. IterationDecision for_each(Function<IterationDecision(PnpIDData const&)> callback)
  114. {
  115. for (auto& pnp_data : s_pnp_ids) {
  116. auto decision = callback(pnp_data);
  117. if (decision != IterationDecision::Continue)
  118. return decision;
  119. }
  120. return IterationDecision::Continue;
  121. }
  122. }
  123. )~~~");
  124. TRY(file.write_until_depleted(generator.as_string_view().bytes()));
  125. return {};
  126. }
  127. ErrorOr<int> serenity_main(Main::Arguments arguments)
  128. {
  129. StringView generated_header_path;
  130. StringView generated_implementation_path;
  131. StringView pnp_ids_file_path;
  132. Core::ArgsParser args_parser;
  133. args_parser.add_option(generated_header_path, "Path to the header file to generate", "generated-header-path", 'h', "generated-header-path");
  134. args_parser.add_option(generated_implementation_path, "Path to the implementation file to generate", "generated-implementation-path", 'c', "generated-implementation-path");
  135. args_parser.add_option(pnp_ids_file_path, "Path to the input PNP ID database file", "pnp-ids-file", 'p', "pnp-ids-file");
  136. args_parser.parse(arguments);
  137. auto open_file = [&](StringView path, Core::File::OpenMode mode = Core::File::OpenMode::Read) -> ErrorOr<NonnullOwnPtr<Core::InputBufferedFile>> {
  138. if (path.is_empty()) {
  139. args_parser.print_usage(stderr, arguments.strings[0]);
  140. return Error::from_string_literal("Must provide all command line options");
  141. }
  142. auto file = TRY(Core::File::open(path, mode));
  143. return Core::InputBufferedFile::create(move(file));
  144. };
  145. auto generated_header_file = TRY(open_file(generated_header_path, Core::File::OpenMode::ReadWrite));
  146. auto generated_implementation_file = TRY(open_file(generated_implementation_path, Core::File::OpenMode::ReadWrite));
  147. auto pnp_ids_file = TRY(open_file(pnp_ids_file_path));
  148. auto pnp_id_map = TRY(parse_pnp_ids_database(*pnp_ids_file));
  149. TRY(generate_header(*generated_header_file));
  150. TRY(generate_source(*generated_implementation_file, pnp_id_map));
  151. return 0;
  152. }