228 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
			
		
		
	
	
			228 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Dart
		
	
	
	
	
	
| import "dart:io";
 | |
| 
 | |
| import "package:build/build.dart";
 | |
| import "package:yaml/yaml.dart";
 | |
| 
 | |
| String joinString(List<String> strings, String sep) {
 | |
|   String joined = "";
 | |
| 
 | |
|   for (final str in strings) {
 | |
|     joined += "$str$sep";
 | |
|   }
 | |
| 
 | |
|   return joined;
 | |
| }
 | |
| 
 | |
| String _getAttributeType(dynamic data) {
 | |
|   if (data is String) {
 | |
|     return data;
 | |
|   }
 | |
| 
 | |
|   return (data as YamlMap)["type"]!;
 | |
| }
 | |
| 
 | |
| bool _getAttributeDeserialise(dynamic data) {
 | |
|   if (data is YamlMap) {
 | |
|     return data["deserialise"] ?? false;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| String _generateAttributes(YamlMap data) {
 | |
|   if (data["attributes"] == null) {
 | |
|     return "";
 | |
|   }
 | |
| 
 | |
|   List<String> attrs = [];
 | |
|   data["attributes"].forEach((key, value) {
 | |
|       final type_ = _getAttributeType(value);
 | |
|       assert(!type_.endsWith(" "));
 | |
|       attrs.add("\tfinal $type_ $key;");
 | |
|   });
 | |
| 
 | |
|   final joined = joinString(attrs, "\n");
 | |
|   return joined.substring(0, joined.length - 1);
 | |
| }
 | |
| 
 | |
| String _generateInitializers(YamlMap data) {
 | |
|   if (data["attributes"] == null) {
 | |
|     return "";
 | |
|   }
 | |
| 
 | |
|   List<String> attrs = [];
 | |
|   data["attributes"].forEach((key, value) {
 | |
|       final type_ = _getAttributeType(value);
 | |
|       final defaultValue = value is YamlMap
 | |
|         ? value["default"]
 | |
|         : null;
 | |
|       final defaultString = defaultValue != null
 | |
|         ? " = const $defaultValue"
 | |
|         : "";
 | |
|       final isRequired = !type_.endsWith("?") && defaultValue == null;
 | |
|       final requiredString = isRequired ? "required " : "";
 | |
| 
 | |
|       attrs.add("\t\t${requiredString}this.$key$defaultString,");
 | |
|   });
 | |
| 
 | |
|   final joined = joinString(attrs, "\n");
 | |
|   return joined.substring(0, joined.length - 1);
 | |
| }
 | |
| 
 | |
| String _generateToJson(YamlMap data) {
 | |
|   String json = '''
 | |
| \t@override
 | |
| \tMap<String, dynamic> toJson() => {\n
 | |
| ''';
 | |
| 
 | |
|   if (data["attributes"] != null) {
 | |
|     data["attributes"].forEach((key, _) {
 | |
|         json += "\t\t\"$key\": $key,\n";
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   json += '''
 | |
| \t\t"type": "${data["name"]!}"
 | |
| \t};\n
 | |
| ''';
 | |
|   return json;
 | |
| }
 | |
| 
 | |
| String _generateFromJson(YamlMap data) {
 | |
|   final name = data["name"]!;
 | |
|   String json = "\tstatic $name fromJson(Map<String, dynamic> json) => $name(";
 | |
| 
 | |
|   if (data["attributes"] != null) {
 | |
|     json += "\n";
 | |
|     data["attributes"].forEach((key, value) {
 | |
|         final type_ = _getAttributeType(value);
 | |
|         if (type_.startsWith("List<")) {
 | |
|           final exp = RegExp(r"List\<(.*?)\>");
 | |
|           final listType = exp.firstMatch(type_)!.group(1)!;
 | |
| 
 | |
|           final suffix = type_.endsWith("?") ? " ?? []" : "!";
 | |
|           String attr = '(json["$key"]$suffix)';
 | |
|           if (_getAttributeDeserialise(value)) {
 | |
|             attr += ".map((item) => $listType.fromJson(item))";
 | |
|           }
 | |
| 
 | |
|           json += "\t\t$key: List<$listType>.from($attr),\n";
 | |
|         } else {
 | |
|           if (_getAttributeDeserialise(value)) {
 | |
|             if (type_.endsWith("?")) {
 | |
|               final attrTypeNoNull = type_.substring(0, type_.length - 1);
 | |
|               json += '\t\t$key: json["$key"] != null ? $attrTypeNoNull.fromJson(json["$key"]) : null,\n';
 | |
|             } else {
 | |
|               json += '\t\t$key: $type_.fromJson(json["$key"]!),\n';
 | |
|             }
 | |
|           } else {
 | |
|             final nullString = type_.endsWith("?")
 | |
|               ? "" : "!";
 | |
|             json += '\t\t$key: json["$key"]$nullString,\n';
 | |
|           }
 | |
|         }
 | |
|     });
 | |
|     json += "\t);";
 | |
|   } else {
 | |
|     json += ");";
 | |
|   }
 | |
| 
 | |
|   return json;
 | |
| }
 | |
| 
 | |
| String generateClassFromData(YamlMap data) {
 | |
|   String impls = "";
 | |
|   for (final impl in data["implements"]) {
 | |
|     impls += "$impl, ";
 | |
|   }
 | |
|   final constructor = data["attributes"] != null
 | |
|     ? '''
 | |
| \t${data["name"]!}({
 | |
| ${_generateInitializers(data)}
 | |
| \t});
 | |
|     '''
 | |
|     : '\t${data["name"]!}();';
 | |
|   return '''
 | |
| class ${data["name"]!} extends ${data["extends"]} implements ${impls.substring(0, impls.length - 2)} {
 | |
| ${_generateAttributes(data)}
 | |
| $constructor
 | |
| 
 | |
| \t// JSON Stuff
 | |
| ${_generateToJson(data)}
 | |
| ${_generateFromJson(data)}
 | |
| }
 | |
| 
 | |
| ''';
 | |
| }
 | |
| 
 | |
| String _generateBuilder(String name, String baseClass, YamlList classes) {
 | |
|   String func = '''
 | |
| $baseClass? get${name}FromJson(Map<String, dynamic> json) {
 | |
| \tswitch(json["type"]!) {
 | |
| ''';
 | |
| 
 | |
|   for (final class_ in classes) {
 | |
|     final name = class_["name"]!;
 | |
|     func += '\t\tcase "$name": return $name.fromJson(json);\n';
 | |
|   }
 | |
| 
 | |
|   func += "\t\tdefault: return null;\n";
 | |
|   func += "\t}\n}";
 | |
|   return func;
 | |
| }
 | |
| 
 | |
| String generateFileFromData(YamlMap data) {
 | |
|   String file = '''
 | |
| //// AUTO-GENERATED by build_runner ////
 | |
| /// DO NOT EDIT BY HAND
 | |
| part of "${data["partof"]!}";
 | |
| 
 | |
| ''';
 | |
| 
 | |
|   data["classes"].forEach((attributes) {
 | |
|       file += generateClassFromData(attributes);
 | |
|   });
 | |
|   if (data["generate_builder"]) {
 | |
|     file += "\n\n";
 | |
|     file += _generateBuilder(
 | |
|       data["builder_name"],
 | |
|       data["builder_baseclass"],
 | |
|       data["classes"]
 | |
|     );
 | |
|   }
 | |
|   
 | |
|   return file;
 | |
| }
 | |
| 
 | |
| class DataClassBuilder implements Builder {
 | |
|   @override
 | |
|   Future build(BuildStep step) async {
 | |
|     final file = File(step.inputId.path);
 | |
|     final data = loadYaml(await file.readAsString());
 | |
|     
 | |
|     for (final asset in step.allowedOutputs) {
 | |
|       if (asset.path.endsWith("commands.moxxy.dart")) {
 | |
|         await step.writeAsString(
 | |
|           asset,
 | |
|           generateFileFromData(data["files"]["commands"]!)
 | |
|         );
 | |
|       } else if (asset.path.endsWith("events.moxxy.dart")) {
 | |
|         await step.writeAsString(
 | |
|           asset,
 | |
|           generateFileFromData(data["files"]["events"]!)
 | |
|         );
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   @override
 | |
|   final buildExtensions = const {
 | |
|     "data_classes.yaml": [
 | |
|       "shared/commands.moxxy.dart",
 | |
|       "shared/events.moxxy.dart"
 | |
|     ]
 | |
|   };
 | |
| }
 | |
| 
 | |
| Builder dataClassBuilder(BuilderOptions options) => DataClassBuilder();
 |