Nikolas Rimikis
1 year ago
9 changed files with 682 additions and 673 deletions
@ -0,0 +1,136 @@
|
||||
import 'package:code_builder/code_builder.dart'; |
||||
import 'package:dynamite/src/builder/resolve_type.dart'; |
||||
import 'package:dynamite/src/builder/state.dart'; |
||||
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||
import 'package:dynamite/src/helpers/dynamite.dart'; |
||||
import 'package:dynamite/src/models/open_api.dart'; |
||||
import 'package:dynamite/src/models/schema.dart'; |
||||
import 'package:dynamite/src/type_result/type_result.dart'; |
||||
|
||||
Spec buildHeaderSerializer(final State state, final String identifier, final OpenAPI spec, final Schema schema) => |
||||
Class( |
||||
(final b) => b |
||||
..name = '_\$${state.classPrefix}${identifier}Serializer' |
||||
..implements.add(refer('StructuredSerializer<${state.classPrefix}$identifier>')) |
||||
..fields.addAll([ |
||||
Field( |
||||
(final b) => b |
||||
..name = 'types' |
||||
..modifier = FieldModifier.final$ |
||||
..type = refer('Iterable<Type>') |
||||
..annotations.add(refer('override')) |
||||
..assignment = Code('const [${state.classPrefix}$identifier, _\$${state.classPrefix}$identifier]'), |
||||
), |
||||
Field( |
||||
(final b) => b |
||||
..name = 'wireName' |
||||
..modifier = FieldModifier.final$ |
||||
..type = refer('String') |
||||
..annotations.add(refer('override')) |
||||
..assignment = Code("r'${state.classPrefix}$identifier'"), |
||||
), |
||||
]) |
||||
..methods.addAll([ |
||||
Method((final b) { |
||||
b |
||||
..name = 'serialize' |
||||
..returns = refer('Iterable<Object?>') |
||||
..annotations.add(refer('override')) |
||||
..requiredParameters.addAll([ |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'serializers' |
||||
..type = refer('Serializers'), |
||||
), |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'object' |
||||
..type = refer('${state.classPrefix}$identifier'), |
||||
), |
||||
]) |
||||
..optionalParameters.add( |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'specifiedType' |
||||
..type = refer('FullType') |
||||
..named = true |
||||
..defaultTo = const Code('FullType.unspecified'), |
||||
), |
||||
) |
||||
..body = const Code('throw UnimplementedError();'); |
||||
}), |
||||
Method((final b) { |
||||
b |
||||
..name = 'deserialize' |
||||
..returns = refer('${state.classPrefix}$identifier') |
||||
..annotations.add(refer('override')) |
||||
..requiredParameters.addAll([ |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'serializers' |
||||
..type = refer('Serializers'), |
||||
), |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'serialized' |
||||
..type = refer('Iterable<Object?>'), |
||||
), |
||||
]) |
||||
..optionalParameters.add( |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'specifiedType' |
||||
..type = refer('FullType') |
||||
..named = true |
||||
..defaultTo = const Code('FullType.unspecified'), |
||||
), |
||||
); |
||||
List<Code> deserializeProperty(final MapEntry<String, Schema> property) { |
||||
final propertyName = property.key; |
||||
final propertySchema = property.value; |
||||
final result = resolveType( |
||||
spec, |
||||
state, |
||||
'${identifier}_${toDartName(propertyName, uppercaseFirstCharacter: true)}', |
||||
propertySchema, |
||||
nullable: isDartParameterNullable(schema.required?.contains(propertyName), propertySchema), |
||||
); |
||||
|
||||
return [ |
||||
Code("case '$propertyName':"), |
||||
if (result.className != 'String') ...[ |
||||
if (result is TypeResultBase || result is TypeResultEnum) ...[ |
||||
Code( |
||||
'result.${toDartName(propertyName)} = ${result.deserialize(result.decode('value!'))};', |
||||
), |
||||
] else ...[ |
||||
Code( |
||||
'result.${toDartName(propertyName)}.replace(${result.deserialize(result.decode('value!'))});', |
||||
), |
||||
], |
||||
] else ...[ |
||||
Code( |
||||
'result.${toDartName(propertyName)} = value!;', |
||||
), |
||||
], |
||||
]; |
||||
} |
||||
|
||||
b.body = Block.of([ |
||||
Code('final result = new ${state.classPrefix}${identifier}Builder();'), |
||||
const Code(''), |
||||
const Code('final iterator = serialized.iterator;'), |
||||
const Code('while (iterator.moveNext()) {'), |
||||
const Code('final key = iterator.current! as String;'), |
||||
const Code('iterator.moveNext();'), |
||||
const Code('final value = iterator.current! as String;'), |
||||
const Code('switch (key) {'), |
||||
for (final property in schema.properties!.entries) ...deserializeProperty(property), |
||||
const Code('}'), |
||||
const Code('}'), |
||||
const Code(''), |
||||
const Code('return result.build();'), |
||||
]); |
||||
}), |
||||
]), |
||||
); |
@ -0,0 +1,229 @@
|
||||
import 'package:built_collection/built_collection.dart'; |
||||
import 'package:code_builder/code_builder.dart'; |
||||
import 'package:dynamite/src/builder/resolve_type.dart'; |
||||
import 'package:dynamite/src/builder/state.dart'; |
||||
import 'package:dynamite/src/helpers/built_value.dart'; |
||||
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||
import 'package:dynamite/src/models/open_api.dart'; |
||||
import 'package:dynamite/src/models/schema.dart'; |
||||
import 'package:dynamite/src/type_result/type_result.dart'; |
||||
|
||||
TypeResult resolveOfs( |
||||
final OpenAPI spec, |
||||
final State state, |
||||
final String identifier, |
||||
final Schema schema, { |
||||
final bool nullable = false, |
||||
}) { |
||||
final result = TypeResultObject( |
||||
'${state.classPrefix}$identifier', |
||||
nullable: nullable, |
||||
); |
||||
|
||||
if (state.resolvedTypes.add(result)) { |
||||
final results = schema.ofs! |
||||
.map( |
||||
(final s) => resolveType( |
||||
spec, |
||||
state, |
||||
'$identifier${schema.ofs!.indexOf(s)}', |
||||
s, |
||||
nullable: !(schema.allOf?.contains(s) ?? false), |
||||
), |
||||
) |
||||
.toList(); |
||||
|
||||
final fields = <String, String>{}; |
||||
for (final result in results) { |
||||
final dartName = toDartName(result.name.replaceFirst(state.classPrefix, '')); |
||||
fields[result.name] = toFieldName(dartName, result.name.replaceFirst(state.classPrefix, '')); |
||||
} |
||||
|
||||
state.output.addAll([ |
||||
buildBuiltClass( |
||||
'${state.classPrefix}$identifier', |
||||
BuiltList.build((final b) { |
||||
b.add( |
||||
Method( |
||||
(final b) { |
||||
b |
||||
..name = 'data' |
||||
..returns = refer('JsonObject') |
||||
..type = MethodType.getter; |
||||
}, |
||||
), |
||||
); |
||||
|
||||
for (final result in results) { |
||||
b.add( |
||||
Method( |
||||
(final b) { |
||||
final s = schema.ofs![results.indexOf(result)]; |
||||
b |
||||
..name = fields[result.name] |
||||
..returns = refer(result.nullableName) |
||||
..type = MethodType.getter |
||||
..docs.addAll(s.formattedDescription); |
||||
}, |
||||
), |
||||
); |
||||
} |
||||
}), |
||||
customSerializer: true, |
||||
), |
||||
Class( |
||||
(final b) => b |
||||
..name = '_\$${state.classPrefix}${identifier}Serializer' |
||||
..implements.add(refer('PrimitiveSerializer<${state.classPrefix}$identifier>')) |
||||
..fields.addAll([ |
||||
Field( |
||||
(final b) => b |
||||
..name = 'types' |
||||
..modifier = FieldModifier.final$ |
||||
..type = refer('Iterable<Type>') |
||||
..annotations.add(refer('override')) |
||||
..assignment = Code('const [${state.classPrefix}$identifier, _\$${state.classPrefix}$identifier]'), |
||||
), |
||||
Field( |
||||
(final b) => b |
||||
..name = 'wireName' |
||||
..modifier = FieldModifier.final$ |
||||
..type = refer('String') |
||||
..annotations.add(refer('override')) |
||||
..assignment = Code("r'${state.classPrefix}$identifier'"), |
||||
), |
||||
]) |
||||
..methods.addAll([ |
||||
Method((final b) { |
||||
b |
||||
..name = 'serialize' |
||||
..returns = refer('Object') |
||||
..annotations.add(refer('override')) |
||||
..requiredParameters.addAll([ |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'serializers' |
||||
..type = refer('Serializers'), |
||||
), |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'object' |
||||
..type = refer('${state.classPrefix}$identifier'), |
||||
), |
||||
]) |
||||
..optionalParameters.add( |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'specifiedType' |
||||
..type = refer('FullType') |
||||
..named = true |
||||
..defaultTo = const Code('FullType.unspecified'), |
||||
), |
||||
) |
||||
..body = const Code('return object.data.value;'); |
||||
}), |
||||
Method((final b) { |
||||
b |
||||
..name = 'deserialize' |
||||
..returns = refer('${state.classPrefix}$identifier') |
||||
..annotations.add(refer('override')) |
||||
..requiredParameters.addAll([ |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'serializers' |
||||
..type = refer('Serializers'), |
||||
), |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'data' |
||||
..type = refer('Object'), |
||||
), |
||||
]) |
||||
..optionalParameters.add( |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'specifiedType' |
||||
..type = refer('FullType') |
||||
..named = true |
||||
..defaultTo = const Code('FullType.unspecified'), |
||||
), |
||||
) |
||||
..body = Code( |
||||
<String>[ |
||||
'final result = new ${state.classPrefix}${identifier}Builder()', |
||||
'..data = JsonObject(data);', |
||||
if (schema.allOf != null) ...[ |
||||
for (final result in results) ...[ |
||||
if (result is TypeResultBase || result is TypeResultEnum) ...[ |
||||
'result.${fields[result.name]!} = ${result.deserialize('data')};', |
||||
] else ...[ |
||||
'result.${fields[result.name]!}.replace(${result.deserialize('data')});', |
||||
], |
||||
], |
||||
] else ...[ |
||||
if (schema.discriminator != null) ...[ |
||||
'if (data is! Iterable) {', |
||||
r"throw StateError('Expected an Iterable but got ${data.runtimeType}');", |
||||
'}', |
||||
'', |
||||
'String? discriminator;', |
||||
'', |
||||
'final iterator = data.iterator;', |
||||
'while (iterator.moveNext()) {', |
||||
'final key = iterator.current! as String;', |
||||
'iterator.moveNext();', |
||||
'final Object? value = iterator.current;', |
||||
"if (key == '${schema.discriminator!.propertyName}') {", |
||||
'discriminator = value! as String;', |
||||
'break;', |
||||
'}', |
||||
'}', |
||||
], |
||||
for (final result in results) ...[ |
||||
if (schema.discriminator != null) ...[ |
||||
"if (discriminator == '${result.name.replaceFirst(state.classPrefix, '')}'", |
||||
if (schema.discriminator!.mapping != null && schema.discriminator!.mapping!.isNotEmpty) ...[ |
||||
for (final key in schema.discriminator!.mapping!.entries |
||||
.where( |
||||
(final entry) => |
||||
entry.value.endsWith('/${result.name.replaceFirst(state.classPrefix, '')}'), |
||||
) |
||||
.map((final entry) => entry.key)) ...[ |
||||
" || discriminator == '$key'", |
||||
], |
||||
') {', |
||||
], |
||||
], |
||||
'try {', |
||||
if (result is TypeResultBase || result is TypeResultEnum) ...[ |
||||
'result._${fields[result.name]!} = ${result.deserialize('data')};', |
||||
] else ...[ |
||||
'result._${fields[result.name]!} = ${result.deserialize('data')}.toBuilder();', |
||||
], |
||||
'} catch (_) {', |
||||
if (schema.discriminator != null) ...[ |
||||
'rethrow;', |
||||
], |
||||
'}', |
||||
if (schema.discriminator != null) ...[ |
||||
'}', |
||||
], |
||||
], |
||||
if (schema.oneOf != null) ...[ |
||||
"assert([${fields.values.map((final e) => 'result._$e').join(',')}].where((final x) => x != null).length >= 1, 'Need oneOf for \${result._data}');", |
||||
], |
||||
if (schema.anyOf != null) ...[ |
||||
"assert([${fields.values.map((final e) => 'result._$e').join(',')}].where((final x) => x != null).length >= 1, 'Need anyOf for \${result._data}');", |
||||
], |
||||
], |
||||
'return result.build();', |
||||
].join(), |
||||
); |
||||
}), |
||||
]), |
||||
), |
||||
]); |
||||
} |
||||
|
||||
return result; |
||||
} |
@ -0,0 +1,112 @@
|
||||
import 'package:code_builder/code_builder.dart'; |
||||
import 'package:dynamite/src/builder/resolve_type.dart'; |
||||
import 'package:dynamite/src/builder/state.dart'; |
||||
import 'package:dynamite/src/helpers/built_value.dart'; |
||||
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||
import 'package:dynamite/src/helpers/type_result.dart'; |
||||
import 'package:dynamite/src/models/open_api.dart'; |
||||
import 'package:dynamite/src/models/schema.dart'; |
||||
import 'package:dynamite/src/type_result/type_result.dart'; |
||||
|
||||
TypeResult resolveEnum( |
||||
final OpenAPI spec, |
||||
final State state, |
||||
final String identifier, |
||||
final Schema schema, |
||||
final TypeResult subResult, { |
||||
final bool nullable = false, |
||||
}) { |
||||
if (state.resolvedTypes.add(TypeResultEnum('${state.classPrefix}$identifier', subResult))) { |
||||
state.output.add( |
||||
Class( |
||||
(final b) => b |
||||
..name = '${state.classPrefix}$identifier' |
||||
..extend = refer('EnumClass') |
||||
..constructors.add( |
||||
Constructor( |
||||
(final b) => b |
||||
..name = '_' |
||||
..constant = true |
||||
..requiredParameters.add( |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'name' |
||||
..toSuper = true, |
||||
), |
||||
), |
||||
), |
||||
) |
||||
..fields.addAll( |
||||
schema.enum_!.map( |
||||
(final value) => Field( |
||||
(final b) { |
||||
final result = resolveType( |
||||
spec, |
||||
state, |
||||
'$identifier${toDartName(value.toString(), uppercaseFirstCharacter: true)}', |
||||
schema, |
||||
ignoreEnum: true, |
||||
); |
||||
b |
||||
..name = toDartName(value.toString()) |
||||
..static = true |
||||
..modifier = FieldModifier.constant |
||||
..type = refer('${state.classPrefix}$identifier') |
||||
..assignment = Code( |
||||
'_\$${toCamelCase('${state.classPrefix}$identifier')}${toDartName(value.toString(), uppercaseFirstCharacter: true)}', |
||||
); |
||||
|
||||
if (toDartName(value.toString()) != value.toString()) { |
||||
if (result.name != 'String' && result.name != 'int') { |
||||
throw Exception( |
||||
'Sorry enum values are a bit broken. ' |
||||
'See https://github.com/google/json_serializable.dart/issues/616. ' |
||||
'Please remove the enum values on ${state.classPrefix}$identifier.', |
||||
); |
||||
} |
||||
b.annotations.add( |
||||
refer('BuiltValueEnumConst').call([], { |
||||
'wireName': refer(valueToEscapedValue(result, value.toString())), |
||||
}), |
||||
); |
||||
} |
||||
}, |
||||
), |
||||
), |
||||
) |
||||
..methods.addAll([ |
||||
Method( |
||||
(final b) => b |
||||
..name = 'values' |
||||
..returns = refer('BuiltSet<${state.classPrefix}$identifier>') |
||||
..lambda = true |
||||
..static = true |
||||
..body = Code('_\$${toCamelCase('${state.classPrefix}$identifier')}Values') |
||||
..type = MethodType.getter, |
||||
), |
||||
Method( |
||||
(final b) => b |
||||
..name = 'valueOf' |
||||
..returns = refer('${state.classPrefix}$identifier') |
||||
..lambda = true |
||||
..static = true |
||||
..requiredParameters.add( |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'name' |
||||
..type = refer(subResult.name), |
||||
), |
||||
) |
||||
..body = Code('_\$valueOf${state.classPrefix}$identifier(name)'), |
||||
), |
||||
buildSerializer('${state.classPrefix}$identifier'), |
||||
]), |
||||
), |
||||
); |
||||
} |
||||
return TypeResultEnum( |
||||
'${state.classPrefix}$identifier', |
||||
subResult, |
||||
nullable: nullable, |
||||
); |
||||
} |
@ -1,14 +1,15 @@
|
||||
import 'package:code_builder/code_builder.dart'; |
||||
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||
import 'package:dynamite/src/type_result/type_result.dart'; |
||||
|
||||
class State { |
||||
State({ |
||||
required this.classPrefix, |
||||
required this.variablePrefix, |
||||
}); |
||||
State(final String prefix) |
||||
: classPrefix = toDartName(prefix, uppercaseFirstCharacter: true), |
||||
variablePrefix = toDartName(prefix); |
||||
|
||||
final String classPrefix; |
||||
final String variablePrefix; |
||||
|
||||
final output = <Spec>[]; |
||||
final resolvedTypes = <TypeResult>{}; |
||||
} |
||||
|
@ -0,0 +1,120 @@
|
||||
import 'package:built_collection/built_collection.dart'; |
||||
import 'package:code_builder/code_builder.dart'; |
||||
import 'package:dynamite/src/helpers/dart_helpers.dart'; |
||||
|
||||
Spec buildBuiltClass( |
||||
final String className, |
||||
final BuiltList<Method> methods, { |
||||
final Iterable<String>? defaults, |
||||
final bool customSerializer = false, |
||||
}) => |
||||
Class( |
||||
(final b) { |
||||
b |
||||
..name = className |
||||
..abstract = true |
||||
..implements.add( |
||||
refer( |
||||
'Built<$className, ${className}Builder>', |
||||
), |
||||
) |
||||
..constructors.addAll([ |
||||
builtValueConstructor(className), |
||||
hiddenConstructor, |
||||
fromJsonConstructor, |
||||
]) |
||||
..methods.addAll([ |
||||
toJsonMethod, |
||||
...methods, |
||||
buildSerializer(className, isCustom: customSerializer), |
||||
]); |
||||
|
||||
if (defaults != null && defaults.isNotEmpty) { |
||||
b.methods.add( |
||||
Method( |
||||
(final b) => b |
||||
..name = '_defaults' |
||||
..returns = refer('void') |
||||
..static = true |
||||
..lambda = true |
||||
..annotations.add( |
||||
refer('BuiltValueHook').call([], { |
||||
'initializeBuilder': literalTrue, |
||||
}), |
||||
) |
||||
..requiredParameters.add( |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'b' |
||||
..type = refer('${className}Builder'), |
||||
), |
||||
) |
||||
..body = Code( |
||||
<String?>[ |
||||
'b', |
||||
...defaults, |
||||
].join(), |
||||
), |
||||
), |
||||
); |
||||
} |
||||
}, |
||||
); |
||||
|
||||
Method get toJsonMethod => Method( |
||||
(final b) => b |
||||
..name = 'toJson' |
||||
..returns = refer('Map<String, dynamic>') |
||||
..lambda = true |
||||
..body = const Code('_jsonSerializers.serializeWith(serializer, this)! as Map<String, dynamic>'), |
||||
); |
||||
|
||||
Method buildSerializer(final String className, {final bool isCustom = false}) => Method((final b) { |
||||
b |
||||
..name = 'serializer' |
||||
..returns = refer('Serializer<$className>') |
||||
..lambda = true |
||||
..static = true |
||||
..body = Code( |
||||
isCustom ? '_\$${className}Serializer()' : '_\$${toCamelCase(className)}Serializer', |
||||
) |
||||
..type = MethodType.getter; |
||||
if (isCustom) { |
||||
b.annotations.add(refer('BuiltValueSerializer').call([], {'custom': literalTrue})); |
||||
} |
||||
}); |
||||
|
||||
Constructor builtValueConstructor(final String className) => Constructor( |
||||
(final b) => b |
||||
..factory = true |
||||
..lambda = true |
||||
..optionalParameters.add( |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'b' |
||||
..type = refer('void Function(${className}Builder)?'), |
||||
), |
||||
) |
||||
..redirect = refer('_\$$className'), |
||||
); |
||||
|
||||
Constructor get hiddenConstructor => Constructor( |
||||
(final b) => b |
||||
..name = '_' |
||||
..constant = true, |
||||
); |
||||
|
||||
Constructor get fromJsonConstructor => Constructor( |
||||
(final b) => b |
||||
..factory = true |
||||
..name = 'fromJson' |
||||
..lambda = true |
||||
..requiredParameters.add( |
||||
Parameter( |
||||
(final b) => b |
||||
..name = 'json' |
||||
..type = refer('Map<String, dynamic>'), |
||||
), |
||||
) |
||||
..body = const Code('_jsonSerializers.deserializeWith(serializer, json)!'), |
||||
); |
Loading…
Reference in new issue