Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

/// Wraps the [input] in curly braces if it's not empty.
String wrapBracesIfNotEmpty(String input) => input.isEmpty ? input : '{$input}';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha nice! 😄


/// Wraps the [input] in curly braces or adds a semicolon if it's empty.
String wrapInBracesOrSemicolon(String input) =>
input.isEmpty ? ';' : '{ $input }';
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../model/class_info.dart';

class EnumGenerator {
final EnumClassInfo classInfo;

EnumGenerator(this.classInfo);

String generate() {
final buffer = StringBuffer();
final className = classInfo.name;
final enumValues = classInfo.enumValues;

final staticFinals = <String>[];
for (final value in enumValues) {
final valueName = value.name;
final jsonValue = value.jsonValue;
staticFinals.add(
'static const $valueName = $className._(\'$jsonValue\');',
);
}

buffer.writeln('''
class $className {
final String name;

const $className._(this.name);

${staticFinals.join('\n\n')}

static const List<$className> values = [
${enumValues.map((e) => e.name).join(',')}
];

static final Map<String, $className> _byName = {
for (final value in values) value.name: value,
};

$className.unknown(this.name) : assert(!_byName.keys.contains(name));

factory $className.fromJson(String name) {
final knownValue = _byName[name];
if(knownValue != null) {
return knownValue;
}
return $className.unknown(name);
}

bool get isKnown => _byName[name] != null;

@override
String toString() => name;
}
''');
return buffer.toString();
}
}
17 changes: 17 additions & 0 deletions pkgs/json_syntax_generator/lib/src/generator/helper_library.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ extension on Map<String, Object?> {
return list.cast();
}

List<T>? optionalListParsed<T extends Object?>(
String key,
T Function(Object?) elementParser,
) {
final jsonValue = optionalList(key);
if (jsonValue == null) return null;
return [for (final element in jsonValue) elementParser(element)];
}

Map<String, T> map$<T extends Object?>(String key) =>
_castMap<T>(get<Map<String, Object?>>(key), key);

Expand Down Expand Up @@ -92,6 +101,14 @@ extension on Map<String, Object?> {
}
return Uri.file(path);
}

void setOrRemove(String key, Object? value) {
if (value == null) {
remove(key);
} else {
this[key] = value;
}
}
}

extension on List<Uri> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import '../model/class_info.dart';
import 'code_generation_helpers.dart';
import 'property_generator.dart';

class ClassGenerator {
final NormalClassInfo classInfo;

ClassGenerator(this.classInfo);

String generate() {
final buffer = StringBuffer();
final className = classInfo.name;
final superclass = classInfo.superclass;
final superclassName = superclass?.name;
final properties = classInfo.properties;
final identifyingSubtype = classInfo.taggedUnionKey;

final constructorParams = <String>[];
final setupParams = <String>[];
final constructorSetterCalls = <String>[];
final accessors = <String>[];
final superParams = <String>[];

final propertyNames =
{
for (final property in properties) property.name,
if (superclass != null)
for (final property in superclass.properties) property.name,
}.toList()
..sort();
for (final propertyName in propertyNames) {
final superClassProperty = superclass?.getProperty(propertyName);
final thisClassProperty = classInfo.getProperty(propertyName);
final property = superClassProperty ?? thisClassProperty!;
if (superClassProperty != null) {
if (identifyingSubtype == null) {
constructorParams.add(
'${property.isRequired ? 'required ' : ''}super.${property.name}',
);
} else {
superParams.add("type: '$identifyingSubtype'");
}
} else {
final dartType = property.type;
constructorParams.add(
'${property.isRequired ? 'required ' : ''}$dartType ${property.name}',
);
setupParams.add(
'${property.isRequired ? 'required' : ''} $dartType ${property.name}',
);
if (property.setterPrivate) {
constructorSetterCalls.add('_${property.name} = ${property.name};');
} else {
constructorSetterCalls.add(
'this.${property.name} = ${property.name};',
);
}
}
if (thisClassProperty != null) {
accessors.add(PropertyGenerator(thisClassProperty).generate());
}
}

if (constructorSetterCalls.isNotEmpty) {
constructorSetterCalls.add('json.sortOnKey();');
}

if (superclass != null) {
buffer.writeln('''
class $className extends $superclassName {
$className.fromJson(super.json) : super.fromJson();

$className(${wrapBracesIfNotEmpty(constructorParams.join(', '))})
: super(${superParams.join(',')})
${wrapInBracesOrSemicolon(constructorSetterCalls.join('\n '))}
''');
if (setupParams.isNotEmpty) {
buffer.writeln('''
/// Setup all fields for [$className] that are not in
/// [$superclassName].
void setup (
${wrapBracesIfNotEmpty(setupParams.join(','))}
) {
${constructorSetterCalls.join('\n')}
}
''');
}

buffer.writeln('''
${accessors.join('\n')}

@override
String toString() => '$className(\$json)';
}
''');
} else {
buffer.writeln('''
class $className {
final Map<String, Object?> json;

$className.fromJson(this.json);

$className(${wrapBracesIfNotEmpty(constructorParams.join(', '))}) : json = {} {
${constructorSetterCalls.join('\n ')}
}

${accessors.join('\n')}

@override
String toString() => '$className(\$json)';
}
''');
}

if (identifyingSubtype != null) {
buffer.writeln('''
extension ${className}Extension on $superclassName {
bool get is$className => type == '$identifyingSubtype';

$className get as$className => $className.fromJson(json);
}
''');
}

return buffer.toString();
}
}
Loading