Skip to content

Commit

Permalink
fix(dynamite): Allow int enums
Browse files Browse the repository at this point in the history
feat(dynamite_end_to_end_test): Add test for int enums

Co-authored-by: Nikolas Rimikis <[email protected]>
Signed-off-by: jld3103 <[email protected]>
Signed-off-by: Nikolas Rimikis <[email protected]>
  • Loading branch information
provokateurin and Leptopoda committed Dec 19, 2023
1 parent 9dcbd4c commit 5ed0eb3
Show file tree
Hide file tree
Showing 31 changed files with 7,619 additions and 5,848 deletions.
302 changes: 218 additions & 84 deletions packages/dynamite/dynamite/lib/src/builder/resolve_enum.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
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/openapi.dart' as openapi;
import 'package:dynamite/src/models/type_result.dart';

Expand All @@ -16,92 +14,228 @@ TypeResult resolveEnum(
final bool nullable = false,
}) {
if (state.resolvedTypes.add(TypeResultEnum(identifier, subResult))) {
state.output.add(
Class(
(final b) => b
..name = 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, uppercaseFirstCharacter: true)}',
schema,
ignoreEnum: true,
);
b
..name = toDartName(value)
..static = true
..modifier = FieldModifier.constant
..type = refer(identifier)
..assignment = Code(
'_\$${toCamelCase(identifier)}${toDartName(value, uppercaseFirstCharacter: true)}',
);
var enumType = subResult.className;
if (enumType == 'JsonObject') {
enumType = 'Object';
}

final values = <String, Object?>{};
for (final enumValue in schema.$enum!) {
final dartName = toDartName(enumValue.toString());
var value = enumValue.value;
if (enumValue.isString) {
value = "r'$value'";
}

if (toDartName(value) != value) {
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 $identifier.',
);
}
b.annotations.add(
refer('BuiltValueEnumConst').call([], {
'wireName': refer(valueToEscapedValue(result, value)),
}),
);
}
},
values[dartName] = value;
}

final $class = Class(
(final b) => b
..name = identifier
..extend = refer('DynamiteEnum<$enumType>')
..constructors.add(
Constructor(
(final b) => b
..name = '_'
..constant = true
..requiredParameters.add(
Parameter(
(final b) => b
..name = 'name'
..toSuper = true,
),
),
),
)
..fields.addAll(
values.entries.map(
(final enumValue) => Field(
(final b) {
b
..name = enumValue.key
..static = true
..modifier = FieldModifier.constant
..type = refer(identifier)
..assignment = Code('$identifier._(${enumValue.value})');
},
),
)
..methods.addAll([
Method(
(final b) => b
..name = 'values'
..returns = refer('BuiltSet<$identifier>')
..lambda = true
..static = true
..body = Code('_\$${toCamelCase(identifier)}Values')
..type = MethodType.getter,
),
Method(
(final b) => b
..name = 'valueOf'
..returns = refer(identifier)
..lambda = true
..static = true
..requiredParameters.add(
Parameter(
(final b) => b
..name = 'name'
..type = refer(subResult.name),
),
)
..body = Code('_\$valueOf$identifier(name)'),
),
buildSerializer(identifier),
]),
),
),
)
..methods.addAll([
Method((final b) {
b
..name = 'values'
..returns = refer('BuiltSet<$identifier>')
..lambda = true
..static = true
..type = MethodType.getter;

final buffer = StringBuffer()
..writeln('BuiltSet<$identifier>(const <$identifier>[')
..writeAll(values.entries.map((final enumValue) => '$identifier.${enumValue.key}'), ',\n')
..writeln(',')
..write('])');

b.body = Code(buffer.toString());
}),
Method((final b) {
b
..name = 'valueOf'
..returns = refer(identifier)
..static = true
..requiredParameters.add(
Parameter(
(final b) => b
..name = 'name'
..type = refer(enumType),
),
);
final buffer = StringBuffer()..writeln('switch (name) {');

for (final enumValue in values.entries) {
buffer
..writeln('case ${enumValue.value}:')
..writeln('return $identifier.${enumValue.key};');
}

buffer.writeln('default: throw ArgumentError(name); }');

b.body = Code(buffer.toString());
}),
buildSerializer(identifier, isCustom: true),
]),
);

final serializer = Class(
(final b) => b
..name = '_\$${identifier}Serializer'
..implements.add(refer('PrimitiveSerializer<$identifier>'))
..constructors.add(
Constructor(
(final b) => b..constant = true,
),
)
..fields.addAll([
Field((final b) {
b
..static = true
..modifier = FieldModifier.constant
..type = refer('Map<$identifier, $enumType>')
..name = '_toWire';
final buffer = StringBuffer()
..writeln('<$identifier, $enumType>{')
..writeAll(
values.entries.map((final enumValue) => '$identifier.${enumValue.key}:${enumValue.value}'),
',\n',
)
..writeln(',')
..write('}');

b.assignment = Code(buffer.toString());
}),
Field((final b) {
b
..static = true
..modifier = FieldModifier.constant
..type = refer('Map<$enumType, $identifier>')
..name = '_fromWire';
final buffer = StringBuffer()
..writeln('<$enumType, $identifier>{')
..writeAll(
values.entries.map((final enumValue) => '${enumValue.value}:$identifier.${enumValue.key}'),
',\n',
)
..writeln(',')
..write('}');

b.assignment = Code(buffer.toString());
}),
])
..methods.addAll([
Method(
(final b) => b
..name = 'types'
..lambda = true
..type = MethodType.getter
..returns = refer('Iterable<Type>')
..annotations.add(refer('override'))
..body = Code('const [$identifier]'),
),
Method(
(final b) => b
..name = 'wireName'
..lambda = true
..type = MethodType.getter
..returns = refer('String')
..annotations.add(refer('override'))
..body = Code("r'$identifier'"),
),
Method((final b) {
b
..name = 'serialize'
..returns = refer('Object')
..annotations.add(refer('override'))
..lambda = true
..requiredParameters.addAll([
Parameter(
(final b) => b
..name = 'serializers'
..type = refer('Serializers'),
),
Parameter(
(final b) => b
..name = 'object'
..type = refer(identifier),
),
])
..optionalParameters.add(
Parameter(
(final b) => b
..name = 'specifiedType'
..type = refer('FullType')
..named = true
..defaultTo = const Code('FullType.unspecified'),
),
)
..body = const Code('_toWire[object]!');
}),
Method((final b) {
b
..name = 'deserialize'
..returns = refer(identifier)
..annotations.add(refer('override'))
..lambda = true
..requiredParameters.addAll([
Parameter(
(final b) => b
..name = 'serializers'
..type = refer('Serializers'),
),
Parameter(
(final b) => b
..name = 'serialized'
..type = refer('Object'),
),
])
..optionalParameters.add(
Parameter(
(final b) => b
..name = 'specifiedType'
..type = refer('FullType')
..named = true
..defaultTo = const Code('FullType.unspecified'),
),
)
..body = const Code('_fromWire[serialized]!');
}),
]),
);

state.output.addAll([
$class,
serializer,
]);
}
return TypeResultEnum(
identifier,
Expand Down
3 changes: 1 addition & 2 deletions packages/dynamite/dynamite/lib/src/builder/resolve_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ TypeResult resolveType(
final State state,
final String identifier,
final openapi.Schema schema, {
final bool ignoreEnum = false,
final bool nullable = false,
}) {
TypeResult? result;
Expand Down Expand Up @@ -178,7 +177,7 @@ TypeResult resolveType(
}

if (result != null) {
if (!ignoreEnum && schema.$enum != null) {
if (schema.$enum != null) {
result = resolveEnum(
spec,
state,
Expand Down
2 changes: 1 addition & 1 deletion packages/dynamite/dynamite/lib/src/builder/state.dart
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class State {
/// Wether the state contains resolved types that need the built_value generator.
bool get hasResolvedBuiltTypes => resolvedTypes
.where(
(final type) => type is TypeResultEnum || (type is TypeResultObject && type.className != 'ContentString'),
(final type) => type is TypeResultObject && type.className != 'ContentString',
)
.isNotEmpty;
}
7 changes: 5 additions & 2 deletions packages/dynamite/dynamite/lib/src/helpers/type_result.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import 'package:dynamite/src/helpers/dart_helpers.dart';
import 'package:dynamite/src/models/type_result.dart';

String valueToEscapedValue(final TypeResult result, final String value) {
if (result is TypeResultBase && result.name == 'String') {
/// Escapes a [value] using the type specific syntax from [result].
///
/// Use [forceString] to ensure the returned value is a `String`.
String valueToEscapedValue(final TypeResult result, final String value, {final bool forceString = false}) {
if ((result is TypeResultBase && result.name == 'String') || forceString) {
return "'$value'";
}
if (result is TypeResultList) {
Expand Down
1 change: 1 addition & 0 deletions packages/dynamite/dynamite/lib/src/models/openapi.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:built_collection/built_collection.dart';
import 'package:built_value/json_object.dart';
import 'package:built_value/serializer.dart';
import 'package:built_value/standard_json_plugin.dart';
import 'package:dynamite/src/models/openapi/components.dart';
Expand Down
2 changes: 1 addition & 1 deletion packages/dynamite/dynamite/lib/src/models/openapi.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ abstract class Schema implements Built<Schema, SchemaBuilder> {
JsonObject? get $default;

@BuiltValueField(wireName: 'enum')
BuiltList<String>? get $enum;
BuiltList<JsonObject>? get $enum;

BuiltMap<String, Schema>? get properties;

Expand Down
Loading

0 comments on commit 5ed0eb3

Please sign in to comment.