Generate_CSS_MediaFeatureID_cpp.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. * Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include "GeneratorUtil.h"
  7. #include <AK/SourceGenerator.h>
  8. #include <AK/StringBuilder.h>
  9. #include <LibMain/Main.h>
  10. ErrorOr<int> serenity_main(Main::Arguments arguments)
  11. {
  12. if (arguments.argc != 2) {
  13. warnln("usage: {} <path/to/CSS/MediaFeatures.json", arguments.strings[0]);
  14. return 1;
  15. }
  16. auto json = TRY(read_entire_file_as_json(arguments.strings[1]));
  17. VERIFY(json.is_object());
  18. StringBuilder builder;
  19. SourceGenerator generator { builder };
  20. generator.append(R"~~~(#include <LibWeb/CSS/MediaFeatureID.h>
  21. namespace Web::CSS {
  22. Optional<MediaFeatureID> media_feature_id_from_string(StringView string)
  23. {)~~~");
  24. json.as_object().for_each_member([&](auto& name, auto&) {
  25. auto member_generator = generator.fork();
  26. member_generator.set("name", name);
  27. member_generator.set("name:titlecase", title_casify(name));
  28. member_generator.append(R"~~~(
  29. if (string.equals_ignoring_case("@name@"sv))
  30. return MediaFeatureID::@name:titlecase@;
  31. )~~~");
  32. });
  33. generator.append(R"~~~(
  34. return {};
  35. }
  36. char const* string_from_media_feature_id(MediaFeatureID media_feature_id)
  37. {
  38. switch (media_feature_id) {)~~~");
  39. json.as_object().for_each_member([&](auto& name, auto&) {
  40. auto member_generator = generator.fork();
  41. member_generator.set("name", name);
  42. member_generator.set("name:titlecase", title_casify(name));
  43. member_generator.append(R"~~~(
  44. case MediaFeatureID::@name:titlecase@:
  45. return "@name@";)~~~");
  46. });
  47. generator.append(R"~~~(
  48. }
  49. VERIFY_NOT_REACHED();
  50. }
  51. bool media_feature_type_is_range(MediaFeatureID media_feature_id)
  52. {
  53. switch (media_feature_id) {)~~~");
  54. json.as_object().for_each_member([&](auto& name, auto& value) {
  55. VERIFY(value.is_object());
  56. auto& feature = value.as_object();
  57. auto member_generator = generator.fork();
  58. member_generator.set("name:titlecase", title_casify(name));
  59. VERIFY(feature.has("type"));
  60. auto feature_type = feature.get("type");
  61. VERIFY(feature_type.is_string());
  62. member_generator.set("is_range", feature_type.as_string() == "range" ? "true" : "false");
  63. member_generator.append(R"~~~(
  64. case MediaFeatureID::@name:titlecase@:
  65. return @is_range@;)~~~");
  66. });
  67. generator.append(R"~~~(
  68. }
  69. VERIFY_NOT_REACHED();
  70. }
  71. bool media_feature_accepts_type(MediaFeatureID media_feature_id, MediaFeatureValueType value_type)
  72. {
  73. switch (media_feature_id) {)~~~");
  74. json.as_object().for_each_member([&](auto& name, auto& member) {
  75. VERIFY(member.is_object());
  76. auto& feature = member.as_object();
  77. auto member_generator = generator.fork();
  78. member_generator.set("name:titlecase", title_casify(name));
  79. member_generator.append(R"~~~(
  80. case MediaFeatureID::@name:titlecase@:)~~~");
  81. bool have_output_value_type_switch = false;
  82. if (feature.has("values")) {
  83. auto append_value_type_switch_if_needed = [&]() {
  84. if (!have_output_value_type_switch) {
  85. member_generator.append(R"~~~(
  86. switch (value_type) {)~~~");
  87. }
  88. have_output_value_type_switch = true;
  89. };
  90. auto& values = feature.get("values");
  91. VERIFY(values.is_array());
  92. auto& values_array = values.as_array();
  93. for (auto& type : values_array.values()) {
  94. VERIFY(type.is_string());
  95. auto type_name = type.as_string();
  96. // Skip identifiers.
  97. if (type_name[0] != '<')
  98. continue;
  99. if (type_name == "<mq-boolean>") {
  100. append_value_type_switch_if_needed();
  101. member_generator.append(R"~~~(
  102. case MediaFeatureValueType::Boolean:
  103. return true;)~~~");
  104. } else if (type_name == "<integer>") {
  105. append_value_type_switch_if_needed();
  106. member_generator.append(R"~~~(
  107. case MediaFeatureValueType::Integer:
  108. return true;)~~~");
  109. } else if (type_name == "<length>") {
  110. append_value_type_switch_if_needed();
  111. member_generator.append(R"~~~(
  112. case MediaFeatureValueType::Length:
  113. return true;)~~~");
  114. } else if (type_name == "<ratio>") {
  115. append_value_type_switch_if_needed();
  116. member_generator.append(R"~~~(
  117. case MediaFeatureValueType::Ratio:
  118. return true;)~~~");
  119. } else if (type_name == "<resolution>") {
  120. append_value_type_switch_if_needed();
  121. member_generator.append(R"~~~(
  122. case MediaFeatureValueType::Resolution:
  123. return true;)~~~");
  124. } else {
  125. warnln("Unrecognized media-feature value type: `{}`", type_name);
  126. VERIFY_NOT_REACHED();
  127. }
  128. }
  129. }
  130. if (have_output_value_type_switch) {
  131. member_generator.append(R"~~~(
  132. default:
  133. return false;
  134. })~~~");
  135. } else {
  136. member_generator.append(R"~~~(
  137. return false;)~~~");
  138. }
  139. });
  140. generator.append(R"~~~(
  141. }
  142. VERIFY_NOT_REACHED();
  143. }
  144. bool media_feature_accepts_identifier(MediaFeatureID media_feature_id, ValueID identifier)
  145. {
  146. switch (media_feature_id) {)~~~");
  147. json.as_object().for_each_member([&](auto& name, auto& member) {
  148. VERIFY(member.is_object());
  149. auto& feature = member.as_object();
  150. auto member_generator = generator.fork();
  151. member_generator.set("name:titlecase", title_casify(name));
  152. member_generator.append(R"~~~(
  153. case MediaFeatureID::@name:titlecase@:)~~~");
  154. bool have_output_identifier_switch = false;
  155. if (feature.has("values")) {
  156. auto append_identifier_switch_if_needed = [&]() {
  157. if (!have_output_identifier_switch) {
  158. member_generator.append(R"~~~(
  159. switch (identifier) {)~~~");
  160. }
  161. have_output_identifier_switch = true;
  162. };
  163. auto& values = feature.get("values");
  164. VERIFY(values.is_array());
  165. auto& values_array = values.as_array();
  166. for (auto& identifier : values_array.values()) {
  167. VERIFY(identifier.is_string());
  168. auto identifier_name = identifier.as_string();
  169. // Skip types.
  170. if (identifier_name[0] == '<')
  171. continue;
  172. append_identifier_switch_if_needed();
  173. auto ident_generator = member_generator.fork();
  174. ident_generator.set("identifier:titlecase", title_casify(identifier_name));
  175. ident_generator.append(R"~~~(
  176. case ValueID::@identifier:titlecase@:
  177. return true;)~~~");
  178. }
  179. }
  180. if (have_output_identifier_switch) {
  181. member_generator.append(R"~~~(
  182. default:
  183. return false;
  184. })~~~");
  185. } else {
  186. member_generator.append(R"~~~(
  187. return false;)~~~");
  188. }
  189. });
  190. generator.append(R"~~~(
  191. }
  192. VERIFY_NOT_REACHED();
  193. }
  194. }
  195. )~~~");
  196. outln("{}", generator.as_string_view());
  197. return 0;
  198. }