Skip to content

Commit 9a11537

Browse files
authored
[record_use] Generate syntax (#2868)
1 parent 4341e37 commit 9a11537

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+4098
-350
lines changed

pkgs/hooks/tool/normalize.dart

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ void main(List<String> arguments) {
2929
Directory.fromUri(packageUri.resolve('../code_assets/')),
3030
Directory.fromUri(packageUri.resolve('../data_assets/')),
3131
Directory.fromUri(packageUri.resolve('../pub_formats/')),
32+
Directory.fromUri(packageUri.resolve('../record_use/')),
3233
];
3334
for (final directory in directories) {
3435
final result = processDirectory(directory);
@@ -62,7 +63,12 @@ ProcessDirectoryResult processDirectory(Directory directory) {
6263
for (final entity in entities) {
6364
if (entity is File &&
6465
p.extension(entity.path) == '.json' &&
65-
!entity.path.contains('.dart_tool/')) {
66+
// Don't sort non-source files.
67+
!entity.uri.toFilePath(windows: false).contains('.dart_tool/') &&
68+
// Don't sort recorded uses files, they have ordered arrays.
69+
!entity.uri
70+
.toFilePath(windows: false)
71+
.contains('pkgs/record_use/test_data/json/')) {
6672
processedCount++;
6773
if (processFile(entity)) {
6874
changedCount += 1;
@@ -212,15 +218,8 @@ dynamic sortJson(dynamic data, String filePath) {
212218
return sortedMap;
213219
}
214220
if (data is List) {
215-
return data.map((item) => sortJson(item, filePath)).toList()..sort((a, b) {
216-
if (a is Map && b is Map) {
217-
return compareMaps(a, b);
218-
}
219-
if (a is String && b is String) {
220-
return a.compareTo(b);
221-
}
222-
throw UnimplementedError('Not implemented to compare $a and $b.');
223-
});
221+
return data.map((item) => sortJson(item, filePath)).toList()
222+
..sort(_compareTwoItems);
224223
}
225224
return data;
226225
}
@@ -229,15 +228,27 @@ int _compareTwoItems(dynamic a, dynamic b) {
229228
if (a is Map && b is Map) {
230229
return compareMaps(a, b);
231230
}
232-
if (a is String && b is String) {
233-
return a.compareTo(b);
234-
}
235231
if (a is List && b is List) {
236232
return compareLists(a, b);
237233
}
234+
if (a is String && b is String) {
235+
return a.compareTo(b);
236+
}
238237
if (a is int && b is int) {
239238
return a.compareTo(b);
240239
}
240+
if (a == b) {
241+
return 0;
242+
}
243+
if (a is bool && b is bool) {
244+
return a ? 1 : -1;
245+
}
246+
if (a == null && b != null) {
247+
return -1;
248+
}
249+
if (b == null && a != null) {
250+
return 1;
251+
}
241252
throw UnimplementedError('Not implemented to compare $a and $b.');
242253
}
243254

pkgs/json_syntax_generator/lib/src/generator/property_generator.dart

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,17 @@ List<String> $validateName() => _reader.validateOptionalMap<${dartType.valueType
420420
''');
421421
}
422422
} else {
423-
throw UnimplementedError(valueType.toString());
423+
buffer.writeln('''
424+
$dartType get $fieldName => _reader.optionalMap<${dartType.valueType}>('$jsonKey', $keyPattern);
425+
426+
set $setterName($dartType value) {
427+
_checkArgumentMapKeys(value, $keyPattern);
428+
json.setOrRemove('$jsonKey', value);
429+
$sortOnKey
430+
}
431+
432+
List<String> $validateName() => _reader.validateMap<${dartType.valueType}>('$jsonKey', $keyPattern);
433+
''');
424434
}
425435
default:
426436
throw UnimplementedError(valueType.toString());
@@ -536,6 +546,25 @@ set $setterName($dartType value) {
536546
List<String> $validateName() => _reader.$jsonValidate('$jsonKey');
537547
''');
538548

549+
case 'Object':
550+
case 'int':
551+
final jsonRead = isNullable
552+
? 'optionalList<$itemType>'
553+
: 'list<$itemType>';
554+
final jsonValidate = isNullable
555+
? 'validateOptionalList<$itemType>'
556+
: 'validateList<$itemType>';
557+
final setter = setOrRemove(dartType, jsonKey);
558+
buffer.writeln('''
559+
$dartType get $fieldName => _reader.$jsonRead('$jsonKey');
560+
561+
set $setterName($dartType value) {
562+
$setter
563+
$sortOnKey
564+
}
565+
566+
List<String> $validateName() => _reader.$jsonValidate('$jsonKey');
567+
''');
539568
default:
540569
throw UnimplementedError(itemType.toString());
541570
}

pkgs/json_syntax_generator/lib/src/parser/schema_analyzer.dart

Lines changed: 89 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -300,28 +300,33 @@ class SchemaAnalyzer {
300300
bool required, {
301301
bool allowEnum = true,
302302
}) {
303-
final type = schemas.type;
303+
final (type, typeIsNullable) = schemas.typeAndNullable;
304+
final isNullable = typeIsNullable || !required;
305+
304306
final DartType dartType;
305307
switch (type) {
306308
case null:
307-
dartType = ObjectDartType(isNullable: !required);
309+
dartType = ObjectDartType(isNullable: isNullable);
308310
case SchemaType.boolean:
309-
dartType = BoolDartType(isNullable: !required);
311+
dartType = BoolDartType(isNullable: isNullable);
310312
case SchemaType.integer:
311-
dartType = IntDartType(isNullable: !required);
313+
dartType = IntDartType(isNullable: isNullable);
312314
case SchemaType.string:
313315
if (schemas.generateUri) {
314-
dartType = UriDartType(isNullable: !required);
316+
dartType = UriDartType(isNullable: isNullable);
315317
} else if (schemas.generateEnum && allowEnum) {
316318
_analyzeEnumClass(schemas);
317319
final classInfo = _classes[schemas.className]!;
318-
dartType = ClassDartType(classInfo: classInfo, isNullable: !required);
320+
dartType = ClassDartType(
321+
classInfo: classInfo,
322+
isNullable: isNullable,
323+
);
319324
} else {
320325
if (schemas.patterns.length > 1) {
321326
throw UnsupportedError('Only one pattern is supported.');
322327
}
323328
final pattern = schemas.patterns.firstOrNull;
324-
dartType = StringDartType(isNullable: !required, pattern: pattern);
329+
dartType = StringDartType(isNullable: isNullable, pattern: pattern);
325330
}
326331
case SchemaType.object:
327332
final additionalPropertiesSchema =
@@ -352,7 +357,7 @@ class SchemaAnalyzer {
352357
),
353358
isNullable: false,
354359
),
355-
isNullable: !required,
360+
isNullable: isNullable,
356361
);
357362
default:
358363
throw UnimplementedError(itemType.toString());
@@ -366,7 +371,7 @@ class SchemaAnalyzer {
366371
dartType = MapDartType(
367372
keyType: keyDartType,
368373
valueType: ClassDartType(classInfo: clazz, isNullable: false),
369-
isNullable: !required,
374+
isNullable: isNullable,
370375
);
371376
} else {
372377
dartType = MapDartType(
@@ -375,17 +380,21 @@ class SchemaAnalyzer {
375380
valueType: ObjectDartType(isNullable: true),
376381
isNullable: false,
377382
),
378-
isNullable: !required,
383+
isNullable: isNullable,
379384
);
380385
}
381386
case null:
382387
if (schemas.additionalPropertiesBool == true) {
383388
dartType = ClassDartType(
384389
classInfo: jsonObjectClassInfo,
385-
isNullable: !required,
390+
isNullable: isNullable,
386391
);
387392
} else {
388393
final oneOfs = additionalPropertiesSchema.oneOfs;
394+
if (oneOfs.isEmpty) {
395+
// No type information.
396+
return const ObjectDartType(isNullable: true);
397+
}
389398
if (oneOfs.length != 1) {
390399
throw UnimplementedError();
391400
}
@@ -413,7 +422,7 @@ class SchemaAnalyzer {
413422
isNullable: true,
414423
pattern: stringPattern,
415424
),
416-
isNullable: !required,
425+
isNullable: isNullable,
417426
);
418427
} else {
419428
throw UnimplementedError();
@@ -423,7 +432,13 @@ class SchemaAnalyzer {
423432
dartType = MapDartType(
424433
keyType: keyDartType,
425434
valueType: const StringDartType(isNullable: false),
426-
isNullable: !required,
435+
isNullable: isNullable,
436+
);
437+
case SchemaType.integer:
438+
dartType = MapDartType(
439+
keyType: keyDartType,
440+
valueType: const IntDartType(isNullable: false),
441+
isNullable: isNullable,
427442
);
428443
default:
429444
throw UnimplementedError(additionalPropertiesType.toString());
@@ -433,31 +448,60 @@ class SchemaAnalyzer {
433448
typeName ??= _ucFirst(_snakeToCamelCase(propertyKey));
434449
_analyzeClass(schemas, name: typeName);
435450
final classInfo = _classes[typeName]!;
436-
dartType = ClassDartType(classInfo: classInfo, isNullable: !required);
451+
dartType = ClassDartType(
452+
classInfo: classInfo,
453+
isNullable: isNullable,
454+
);
437455
}
438456
case SchemaType.array:
439457
final items = schemas.items;
440-
final itemType = items.type;
458+
final (itemType, itemNullable) = items.typeAndNullable;
441459
switch (itemType) {
442460
case SchemaType.string:
443461
if (items.generateUri) {
444462
dartType = ListDartType(
445-
itemType: const UriDartType(isNullable: false),
446-
isNullable: !required,
463+
itemType: UriDartType(isNullable: itemNullable),
464+
isNullable: isNullable,
447465
);
448466
} else {
449467
dartType = ListDartType(
450-
itemType: const StringDartType(isNullable: false),
451-
isNullable: !required,
468+
itemType: StringDartType(isNullable: itemNullable),
469+
isNullable: isNullable,
452470
);
453471
}
454-
case SchemaType.object:
455-
final typeName = items.className!;
456-
_analyzeClass(items);
457-
final classInfo = _classes[typeName]!;
472+
case SchemaType.integer:
458473
dartType = ListDartType(
459-
itemType: ClassDartType(classInfo: classInfo, isNullable: false),
460-
isNullable: !required,
474+
itemType: IntDartType(isNullable: itemNullable),
475+
isNullable: isNullable,
476+
);
477+
case SchemaType.object:
478+
final typeName = items.className;
479+
if (typeName != null) {
480+
_analyzeClass(items);
481+
final classInfo = _classes[typeName]!;
482+
dartType = ListDartType(
483+
itemType: ClassDartType(
484+
classInfo: classInfo,
485+
isNullable: itemNullable,
486+
),
487+
isNullable: isNullable,
488+
);
489+
} else if (items.generateMapOf) {
490+
dartType = const ListDartType(
491+
itemType: MapDartType(
492+
valueType: ObjectDartType(isNullable: true),
493+
isNullable: true,
494+
),
495+
isNullable: true,
496+
);
497+
} else {
498+
throw UnimplementedError(itemType.toString());
499+
}
500+
case null:
501+
// No type information.
502+
dartType = const ListDartType(
503+
itemType: ObjectDartType(isNullable: true),
504+
isNullable: true,
461505
);
462506
default:
463507
throw UnimplementedError(itemType.toString());
@@ -550,7 +594,7 @@ extension type JsonSchemas._(List<JsonSchema> _schemas) {
550594
for (final schema in _schemas) ...schema.requiredProperties ?? [],
551595
}.toList()..sort();
552596

553-
SchemaType? get type {
597+
Set<SchemaType> get types {
554598
final types = <SchemaType>{};
555599
for (final schema in _schemas) {
556600
final schemaTypes = schema.typeList;
@@ -560,12 +604,27 @@ extension type JsonSchemas._(List<JsonSchema> _schemas) {
560604
}
561605
}
562606
}
607+
return types;
608+
}
609+
610+
SchemaType? get type {
563611
if (types.length > 1) {
564612
throw StateError('Multiple types found');
565613
}
566614
return types.singleOrNull;
567615
}
568616

617+
(SchemaType?, bool) get typeAndNullable {
618+
if (types.length <= 1) {
619+
return (types.singleOrNull, false);
620+
} else if (types.length == 2 && types.contains(SchemaType.nullValue)) {
621+
final type = types.firstWhere((t) => t != SchemaType.nullValue);
622+
return (type, true);
623+
} else {
624+
throw UnsupportedError('Multiple types: $types.');
625+
}
626+
}
627+
569628
List<RegExp> get patterns {
570629
final patterns = <RegExp>{};
571630
for (final schema in _schemas) {
@@ -745,10 +804,6 @@ extension on JsonSchemas {
745804

746805
String? get generateSubClassesKey {
747806
if (type != SchemaType.object) return null;
748-
// A tagged union either has only a key, or a key and an encoding.
749-
// Classes with more than 2 properties have their a property that has
750-
// predefined values generated as an enum class.
751-
if (propertyKeys.length > 2) return null;
752807
for (final p in propertyKeys) {
753808
final propertySchemas = property(p);
754809
if (propertySchemas.anyOfs.isNotEmpty) {
@@ -785,7 +840,10 @@ extension on JsonSchemas {
785840
if (path.contains('#/definitions/')) {
786841
final splits = path.split('/');
787842
final indexOf = splits.indexOf('definitions');
788-
final nameParts = splits.skip(indexOf + 1).where((e) => e.isNotEmpty);
843+
final nameParts = splits
844+
.skip(indexOf + 1)
845+
.where((e) => e.isNotEmpty)
846+
.toList();
789847
if (nameParts.length == 1 && nameParts.single.startsWithUpperCase()) {
790848
return nameParts.single;
791849
}

pkgs/record_use/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- Made locations optional to accomodate for dart2js compiler not providing
44
source locations for constant instances.
5+
- Introduce a JSON schema for the json encoding.
56

67
## 0.4.2
78

0 commit comments

Comments
 (0)