diff --git a/angel_serialize_generator/lib/angel_serialize_generator.dart b/angel_serialize_generator/lib/angel_serialize_generator.dart deleted file mode 100644 index 1ed44f92..00000000 --- a/angel_serialize_generator/lib/angel_serialize_generator.dart +++ /dev/null @@ -1,282 +0,0 @@ -import 'dart:async'; -import 'package:analyzer/dart/element/element.dart'; -import 'package:analyzer/dart/element/type.dart'; -import 'package:angel_serialize/angel_serialize.dart'; -import 'package:build/build.dart'; -import 'package:code_builder/code_builder.dart'; -import 'package:code_builder/dart/core.dart'; -import 'package:source_gen/source_gen.dart' hide LibraryBuilder; -import 'build_context.dart'; -import 'context.dart'; - -class JsonModelGenerator extends GeneratorForAnnotation { - final bool autoSnakeCaseNames; - final bool autoIdAndDateFields; - - const JsonModelGenerator( - {this.autoSnakeCaseNames: true, this.autoIdAndDateFields: true}); - - @override - Future generateForAnnotatedElement( - Element element, ConstantReader reader, BuildStep buildStep) async { - if (element.kind != ElementKind.CLASS) - throw 'Only classes can be annotated with a @Serializable() annotation.'; - var ctx = await buildContext( - element, - serializable, - buildStep, - await buildStep.resolver, - autoSnakeCaseNames != false, - autoIdAndDateFields != false); - var lib = generateSerializerLibrary(ctx); - return prettyToSource(lib.buildAst()); - } - - LibraryBuilder generateSerializerLibrary(BuildContext ctx) { - var lib = new LibraryBuilder(); - lib.addMember(generateBaseModelClass(ctx)); - return lib; - } - - ClassBuilder generateBaseModelClass(BuildContext ctx) { - if (!ctx.originalClassName.startsWith('_')) - throw 'Classes annotated with @Serializable() must have names starting with a leading underscore.'; - - var genClassName = ctx.modelClassName; - var genClass = new ClassBuilder(genClassName, - asExtends: new TypeBuilder(ctx.originalClassName)); - var modelType = new TypeBuilder(genClassName); - - // Now, add all fields to the base class - ctx.fields.forEach((field) { - genClass.addField( - varField(field.name, type: new TypeBuilder(field.type.displayName)) - ..addAnnotation(reference('override'))); - }); - - // Create convenience constructor - var convenienceConstructor = constructor(ctx.fields.map((field) { - return thisField(named(parameter(field.name))); - })); - genClass.addConstructor(convenienceConstructor); - - // Create toJson - Map toJsonFields = {}; - - ctx.fields.forEach((field) { - var resolvedName = ctx.resolveFieldName(field.name); - ExpressionBuilder value; - - // DateTime - if (field.type.name == 'DateTime') { - value = reference(field.name).equals(literal(null)).ternary( - literal(null), reference(field.name).invoke('toIso8601String', [])); - } - - // Serialize models - else if (serializableTypeChecker.firstAnnotationOf(field.type.element) != - null) { - value = reference(field.name).invoke('toJson', []); - } - - // Anything else - else { - value = reference(field.name); - } - - toJsonFields[resolvedName] = value; - }); - - var toJson = new MethodBuilder('toJson', - returnType: new TypeBuilder('Map', genericTypes: [ - new TypeBuilder('String'), - new TypeBuilder('dynamic') - ]), - returns: map(toJsonFields)); - genClass.addMethod(toJson); - - // Create factory [name].fromJson - var fromJson = new ConstructorBuilder(name: 'fromJson', asFactory: true); - fromJson.addPositional(parameter('data', [new TypeBuilder('Map')])); - var namedParams = - ctx.fields.fold>({}, (out, field) { - var resolvedName = ctx.resolveFieldName(field.name); - var mapKey = reference('data')[literal(resolvedName)]; - ExpressionBuilder value = mapKey; - var type = field.type; - - // DateTime - if (type.name == 'DateTime') { - // map['foo'] is DateTime ? map['foo'] : (map['foo'] is String ? DateTime.parse(map['foo']) : null) - var dt = new TypeBuilder('DateTime'); - value = mapKey.isInstanceOf(dt).ternary( - mapKey, - (mapKey.isInstanceOf(new TypeBuilder('String')).ternary( - new TypeBuilder('DateTime').invoke('parse', [mapKey]), - literal(null))) - .parentheses()); - } - - bool done = false; - - // Handle List - if (type.toString().contains('List') && type is ParameterizedType) { - var listType = type.typeArguments.first; - if (listType.element is ClassElement) { - var genericClass = listType.element as ClassElement; - String fromJsonClassName; - bool hasFromJson = - genericClass.constructors.any((c) => c.name == 'fromJson'); - - if (hasFromJson) { - fromJsonClassName = genericClass.displayName; - } else { - // If it has a serializable annotation, act accordingly. - if (serializableTypeChecker.firstAnnotationOf(genericClass) != null) { - fromJsonClassName = genericClass.displayName.substring(1); - hasFromJson = true; - } - } - - // Check for fromJson - if (hasFromJson) { - var outputType = new TypeBuilder(fromJsonClassName); - var x = reference('x'); - value = mapKey.isInstanceOf(lib$core.List).ternary( - mapKey.invoke('map', [ - new MethodBuilder.closure( - returns: x.equals(literal(null)).ternary( - literal(null), - (x.isInstanceOf(outputType).ternary( - x, - outputType.newInstance([reference('x')], - constructor: 'fromJson'))) - .parentheses())) - ..addPositional(parameter('x')) - ]).invoke('toList', []), - literal(null)); - done = true; - } - } - } - - // Check for fromJson - if (!done && type.element is ClassElement) { - String fromJsonClassName; - var genericClass = type.element as ClassElement; - bool hasFromJson = - genericClass.constructors.any((c) => c.name == 'fromJson'); - - if (hasFromJson) { - fromJsonClassName = type.displayName; - } else { - // If it has a serializable annotation, act accordingly. - if (serializableTypeChecker.firstAnnotationOf(genericClass) != null) { - fromJsonClassName = type.displayName.substring(1); - hasFromJson = true; - } - } - - // Check for fromJson - if (hasFromJson) { - var outputType = new TypeBuilder(fromJsonClassName); - value = mapKey.equals(literal(null)).ternary( - literal(null), - (mapKey.isInstanceOf(outputType).ternary( - mapKey, - outputType - .newInstance([mapKey], constructor: 'fromJson'))) - .parentheses()); - } - } - - // Handle Map... - if (!done && - type.toString().contains('Map') && - type is ParameterizedType && - type.typeArguments.length >= 2) { - var targetType = type.typeArguments[1]; - if (targetType.element is ClassElement) { - String fromJsonClassName; - var genericClass = targetType.element as ClassElement; - bool hasFromJson = - genericClass.constructors.any((c) => c.name == 'fromJson'); - - if (hasFromJson) { - fromJsonClassName = targetType.displayName; - } else { - // If it has a serializable annotation, act accordingly. - if (serializableTypeChecker.firstAnnotationOf(genericClass) != null) { - fromJsonClassName = targetType.displayName.substring(1); - hasFromJson = true; - } - } - - // Check for fromJson - if (hasFromJson) { - var outputType = new TypeBuilder(fromJsonClassName); - var v = mapKey[reference('k')]; - value = mapKey.isInstanceOf(lib$core.Map).ternary( - mapKey.property('keys').invoke('fold', [ - map({}), - new MethodBuilder.closure() - ..addStatements([ - v - .equals(literal(null)) - .ternary( - literal(null), - (v.isInstanceOf(outputType).ternary( - v, - outputType.newInstance([v], - constructor: 'fromJson'))) - .parentheses()) - .asAssign(reference('out')[reference('k')]), - reference('out').asReturn() - ]) - ..addPositional(parameter('out')) - ..addPositional(parameter('k')) - ]), - literal(null)); - } else { - value = mapKey - .isInstanceOf(lib$core.Map) - .ternary(mapKey, literal(null)); - } - } - } - - // Deerialize models - if (!done && - serializableTypeChecker.firstAnnotationOf(field.type.element) != - null) { - var typeName = field.type.name; - typeName.startsWith('_') ? typeName = typeName.substring(1) : null; - var typeBuilder = new TypeBuilder(typeName); - value = mapKey.isInstanceOf(typeBuilder).ternary( - mapKey, typeBuilder.newInstance([mapKey], constructor: 'fromJson')); - } - - return out..[field.name] = value; - }); - fromJson - .addStatement(modelType.newInstance([], named: namedParams).asReturn()); - genClass.addConstructor(fromJson); - - // Create `parse` to just forward - var parseMethod = new MethodBuilder('parse', - returnType: modelType, - returns: - modelType.newInstance([reference('map')], constructor: 'fromJson')); - parseMethod.addPositional(parameter('map', [new TypeBuilder('Map')])); - genClass.addMethod(parseMethod, asStatic: true); - - // Create clone() method... - var cloneMethod = new MethodBuilder('clone', returnType: modelType); - cloneMethod.addStatement(modelType.newInstance( - [reference('toJson').call([])], - constructor: 'fromJson').asReturn()); - genClass.addMethod(cloneMethod); - - return genClass; - } -} diff --git a/angel_serialize_generator/pubspec.yaml b/angel_serialize_generator/pubspec.yaml index 844df7ba..a98b9d50 100644 --- a/angel_serialize_generator/pubspec.yaml +++ b/angel_serialize_generator/pubspec.yaml @@ -1,17 +1,18 @@ name: angel_serialize_generator -version: 1.0.0 +version: 2.0.0-alpha description: Model serialization generators, designed for use with Angel. author: Tobe O homepage: https://github.com/angel-dart/serialize environment: sdk: ">=1.19.0" dependencies: - angel_serialize: ^1.0.0-alpha - code_builder: ^1.0.0 + angel_serialize: + path: ../angel_serialize + code_builder: ^2.0.0 id: ^1.0.0 recase: ^1.0.0 source_gen: ^0.7.0 dev_dependencies: angel_framework: ^1.0.0 - build_runner: ^0.5.0 + build_runner: ^0.6.0 test: ">= 0.12.13 < 0.13.0" \ No newline at end of file