import { TypeDefinition, ServiceDefinition, EnumDefinition, Step } from "../ir"; import { CompileTarget } from "../compile"; import { LineAppender, lineAppender } from "../utils"; import chalk from "chalk"; const conversion = { boolean: "bool", int: "int", float: "double", string: "String", void: "void", // bytes: "Uint8List", //TODO: Check this }; function toDartType(type: string): string { return (conversion as any)[type] || type; } export class DartTarget extends CompileTarget<{ dart_library_name: string }> { name: string = "dart"; start(): void { if (this.options.allow_bytes == true) { throw new Error("Dart has no support for 'bytes' yet!"); } if (!this.options.dart_library_name) { throw new Error( "Setting dart_library_name is required for DART target!" ); } } getImport(name: string) { return `import "./${name}.dart";`; } generateImports(a: lineAppender, def: TypeDefinition | ServiceDefinition) { a(0, `import "./base.dart";`); def.depends.forEach((dep) => { a(0, this.getImport(dep)); }); } getTypeParse(type: string, value: string) { if (conversion[type]) { return `${toDartType(type)}_fromJson(${value})`; } else { return `${toDartType(type)}.fromJson(${value})`; } } generateType(definition: TypeDefinition): void { const { a, getResult } = LineAppender(2); this.generateImports(a, definition); a(0, ``); a(0, `class ${definition.name} {`); for (const field of definition.fields) { if (field.array) { a(1, `List<${toDartType(field.type)}>? ${field.name};`); } else if (field.map) { a( 1, `Map<${toDartType(field.map)},${toDartType(field.type)}>? ${ field.name };` ); } else { a(1, `${toDartType(field.type)}? ${field.name};`); } } a(0, ``); a( 1, `${definition.name}({${definition.fields .map((e) => `this.${e.name}`) .join(", ")}});` ); a(0, ``); a(1, `${definition.name}.fromJson(Map json) {`); for (const field of definition.fields) { a(2, `if(json.containsKey("${field.name}")) {`); if (field.array) { a(3, `this.${field.name} = [];`); a(3, `(json["${field.name}"] as List).forEach((e) => {`); a( 4, `this.${field.name}!.add(${this.getTypeParse(field.type, "e")})` ); a(3, `});`); } else if (field.map) { a(3, `this.${field.name} = {};`); a( 3, `(json["${field.name}"] as Map<${toDartType( field.map )},dynamic>).forEach((key, value) => {` ); a( 4, `this.${field.name}![key] = ${this.getTypeParse( field.type, "value" )}` ); a(3, `});`); } else { a( 3, `this.${field.name} = ${this.getTypeParse( field.type, `json["${field.name}"]` )};` ); } a(2, `} else {`); a(3, `this.${field.name} = null;`); a(2, `}`); a(0, ``); } a(1, `}`); a(1, `Map toJson() {`); a(2, `Map res = {};`); for (const field of definition.fields) { if (conversion[field.type]) { a(2, `res["${field.name}"] = this.${field.name};`); } else { if (field.array) { a( 2, `res["${field.name}"] = this.${field.name}?.map((entry) => entry.toJson()).toList();` ); } else if (field.map) { // dict.map((key, value) => MapEntry(key, value.toString())); a( 2, `res["${field.name}"] = this.${field.name}?.map((key, value) => MapEntry(key, value.toJson()));` ); } else { a(2, `res["${field.name}"] = this.${field.name};`); } } } a(2, `return res;`); a(1, `}`); a(0, `}`); this.writeFile(`lib/src/${definition.name}.dart`, getResult()); } generateEnum(definition: EnumDefinition): void { const { a, getResult } = LineAppender(2); a(0, `enum ${definition.name} {`); for (const entry of definition.values) { const isLast = definition.values[definition.values.length - 1] == entry; a(1, `${entry.name}(${entry.value})${isLast ? ";" : ","}`); } a(0, ``); a(1, `final int val;`); a(1, `const ${definition.name}(int valT) : val= valT;`); a(1, `static ${definition.name}? fromJson(int val) {`); a(2, `switch(val){`); for (const entry of definition.values) { a(3, `case ${entry.value}:`); a(4, `return ${definition.name}.${entry.name};`); } a(3, `default:`); a(4, `return null;`); a(2, `}`); a(1, `}`); a(0, ``); a(1, `int toJson() {`); a(2, `return this.val;`); a(1, `}`); a(0, ``); a(0, `}`); a(0, ``); this.writeFile(`lib/src/${definition.name}.dart`, getResult()); } generateServiceClient(definition: ServiceDefinition): void { const { a, getResult } = LineAppender(2); this.generateImports(a, definition); a(0, `import "./service_client.dart";`); a(0, ``); a(0, `class ${definition.name}Client extends Service {`); a(0, ``); a( 1, `${definition.name}Client(ServiceProvider provider):super(provider, "${definition.name}");` ); a(0, ``); for (const func of definition.functions) { const args = func.inputs .map( (inp) => (inp.array ? `List<${toDartType(inp.type)}>` : toDartType(inp.type)) + " " + inp.name ) .join(", "); const asParams = func.inputs.map((e) => e.name).join(", "); if (!func.return) { a(1, `void ${func.name}(${args}) {`); a( 2, `provider.sendNotification("${definition.name}.${func.name}", [${asParams}]);` ); a(1, `}`); } else { const baseReturnType = func.return.type != "void" ? toDartType(func.return.type) + "?" : toDartType(func.return.type); const returnType = func.return.array ? `List<${baseReturnType}>` : baseReturnType; a(1, `Future<${returnType}> ${func.name}(${args}) async {`); a( 2, `var res = await this.provider.sendRequest("${definition.name}.${func.name}", [${asParams}]);` ); if (func.return.type !== "void") { if (func.return.array) { a( 2, `return res.map((entry) =>${this.getTypeParse( func.return.type, "entry" )}).toList();` ); } else { a(2, `return ${this.getTypeParse(func.return.type, "res")};`); } } a(1, `}`); } a(0, ``); } a(0, `}`); a(0, ``); this.writeFile(`lib/src/${definition.name}Client.dart`, getResult()); } generateServiceServer(definition: ServiceDefinition): void { console.log( chalk.yellow("[DART] WARNING:"), "DART support for services is not yet there. Service generation is currently limited to clients!" ); } generateService(definition: ServiceDefinition): void { this.generateServiceClient(definition); this.writeFile( "lib/src/service_client.dart", this.getTemplate("Dart/service_client.dart") ); } finalize(steps: Step[]): void { const { a, getResult } = LineAppender(2); a(0, `library ${this.options.dart_library_name};`); a(0, ``); let hasService = false; steps.forEach(([type, def]) => { switch (type) { case "type": a(0, `export 'src/${def.name}.dart';`); break; case "enum": a(0, `export 'src/${def.name}.dart';`); break; case "service": a(0, `export 'src/${def.name}Client.dart';`); hasService = true; break; default: console.warn( chalk.yellow("[DART] WARNING:"), "unimplemented step found:", type ); } }); if (hasService) { a(0, `export 'src/service_client.dart';`); } this.writeFile(`lib/${this.options.dart_library_name}.dart`, getResult()); this.writeFile( `pubspec.yaml`, this.getTemplate("Dart/pubspec.yaml").replace( "__NAME__", this.options.dart_library_name ) ); this.writeFile(`lib/src/base.dart`, this.getTemplate("Dart/base.dart")); } }