Skip to content

[native_assets_cli] Add paths to syntax views #2107

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 19, 2025
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
63 changes: 43 additions & 20 deletions pkgs/json_syntax_generator/lib/src/generator/helper_library.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,27 @@
///
/// This simplifies the code generator.
const helperLib = r'''
extension on Map<String, Object?> {
class JsonReader {
/// The JSON Object this reader is reading.
final Map<String, Object?> json;

/// The path traversed by readers of the surrounding JSON.
///
/// Contains [String] property keys and [int] indices.
///
/// This is used to give more precise error messages.
final List<Object> path;

JsonReader(this.json, this.path);

T get<T extends Object?>(String key) {
final value = this[key];
final value = json[key];
if (value is T) return value;
final pathString = _jsonPathToString([key]);
if (value == null) {
throw FormatException('No value was provided for required key: $key');
throw FormatException("No value was provided for '$pathString'.");
}
throw FormatException(
'Unexpected value \'$value\' for key \'.$key\'. '
'Expected a $T.',
);
throwFormatException(value, T, [key]);
}

List<T> list<T extends Object?>(String key) =>
Expand All @@ -29,14 +39,13 @@ extension on Map<String, Object?> {
};

/// [List.cast] but with [FormatException]s.
static List<T> _castList<T extends Object?>(List<Object?> list, String key) {
List<T> _castList<T extends Object?>(List<Object?> list, String key) {
var index = 0;
for (final value in list) {
if (value is! T) {
throw FormatException(
'Unexpected value \'$list\' (${list.runtimeType}) for key \'.$key\'. '
'Expected a ${List<T>}.',
);
throwFormatException(value, T, [key, index]);
}
index++;
}
return list.cast();
}
Expand All @@ -60,16 +69,13 @@ extension on Map<String, Object?> {
};

/// [Map.cast] but with [FormatException]s.
static Map<String, T> _castMap<T extends Object?>(
Map<String, T> _castMap<T extends Object?>(
Map<String, Object?> map_,
String key,
String parentKey,
) {
for (final value in map_.values) {
for (final MapEntry(:key, :value) in map_.entries) {
if (value is! T) {
throw FormatException(
'Unexpected value \'$map_\' (${map_.runtimeType}) for key \'.$key\'.'
'Expected a ${Map<String, T>}.',
);
throwFormatException(value, T, [parentKey, key]);
}
}
return map_.cast();
Expand All @@ -79,7 +85,7 @@ extension on Map<String, Object?> {

List<String> stringList(String key) => list<String>(key);

Uri path(String key) => _fileSystemPathToUri(get<String>(key));
Uri path$(String key) => _fileSystemPathToUri(get<String>(key));

Uri? optionalPath(String key) {
final value = get<String?>(key);
Expand All @@ -102,6 +108,23 @@ extension on Map<String, Object?> {
return Uri.file(path);
}

String _jsonPathToString(List<Object> pathEnding) =>
[...path, ...pathEnding].join('.');

Never throwFormatException(
Object? value,
Type expectedType,
List<Object> pathExtension,
) {
final pathString = _jsonPathToString(pathExtension);
throw FormatException(
"Unexpected value '$value' (${value.runtimeType}) for '$pathString'. "
'Expected a $expectedType.',
);
}
}

extension on Map<String, Object?> {
void setOrRemove(String key, Object? value) {
if (value == null) {
remove(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,9 @@ class ClassGenerator {
if (superclass != null) {
buffer.writeln('''
class $className extends $superclassName {
$className.fromJson(super.json) : super.fromJson();
$className.fromJson(super.json, {
super.path,
}) : super.fromJson();

$className(${wrapBracesIfNotEmpty(constructorParams.join(', '))})
: super(${superParams.join(',')})
Expand Down Expand Up @@ -102,9 +104,18 @@ class $className extends $superclassName {
class $className {
final Map<String, Object?> json;

$className.fromJson(this.json);
final List<Object> path;

$className(${wrapBracesIfNotEmpty(constructorParams.join(', '))}) : json = {} {
JsonReader get _reader => JsonReader(json, path);

$className.fromJson(this.json, {
this.path = const [],
});

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

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ class PropertyGenerator {

buffer.writeln('''
$dartType get $fieldName {
final jsonValue = json.get<$dartStringType>('$jsonKey'); $earlyReturn
final jsonValue = _reader.get<$dartStringType>('$jsonKey'); $earlyReturn
return $classType.fromJson(jsonValue);
}
''');
Expand All @@ -115,8 +115,8 @@ set $setterName($dartType value) {
final jsonRead = required ? 'map\$' : 'optionalMap';
buffer.writeln('''
$dartType get $fieldName {
final jsonValue = json.$jsonRead('$jsonKey'); $earlyReturn
return $classType.fromJson(jsonValue);
final jsonValue = _reader.$jsonRead('$jsonKey'); $earlyReturn
return $classType.fromJson(jsonValue, path: [...path, '$jsonKey']);
}
''');
if (!property.isOverride) {
Expand All @@ -142,7 +142,7 @@ set $setterName($dartType value) {
final fieldName = property.name;

buffer.writeln('''
$dartType get $fieldName => json.get<$dartType>('$jsonKey');
$dartType get $fieldName => _reader.get<$dartType>('$jsonKey');

set $setterName($dartType value) {
json.setOrRemove('$jsonKey', value);
Expand All @@ -169,7 +169,7 @@ set $setterName($dartType value) {
case MapDartType():
buffer.writeln('''
$dartType get $fieldName =>
json.optionalMap<${dartType.valueType}>('$jsonKey');
_reader.optionalMap<${dartType.valueType}>('$jsonKey');

set $setterName($dartType value) {
json.setOrRemove('$jsonKey', value);
Expand All @@ -181,17 +181,22 @@ set $setterName($dartType value) {
final typeName = itemType.toString();
buffer.writeln('''
$dartType get $fieldName {
final map_ = json.optionalMap('$jsonKey');
if(map_ == null){
final jsonValue = _reader.optionalMap('$jsonKey');
if (jsonValue == null) {
return null;
}
return {
for (final MapEntry(:key, :value) in map_.entries)
key : [
for (final item in value as List<Object?>)
$typeName.fromJson(item as $jsonObjectDartType)
],
};
final result = <String, List<Asset>>{};
for (final MapEntry(:key, :value) in jsonValue.entries) {
var index = 0;
result[key] = [
for (final item in value as List<Object?>)
$typeName.fromJson(
item as $jsonObjectDartType,
path: [...path, key, index++],
),
];
}
return result;
}

set $setterName($dartType value) {
Expand All @@ -214,7 +219,7 @@ set $setterName($dartType value) {
case 'Object':
if (valueType.isNullable) {
buffer.writeln('''
$dartType get $fieldName => json.optionalMap('$jsonKey');
$dartType get $fieldName => _reader.optionalMap('$jsonKey');

set $setterName($dartType value) {
json.setOrRemove('$jsonKey', value);
Expand Down Expand Up @@ -251,8 +256,16 @@ set $setterName($dartType value) {
throw UnimplementedError('Expected an optional property.');
}
buffer.writeln('''
$dartType get $fieldName =>
json.optionalListParsed('$jsonKey', (e) => $typeName.fromJson(e as Map<String, Object?>));
$dartType get $fieldName {
var index = 0;
return _reader.optionalListParsed(
'$jsonKey',
(e) => $typeName.fromJson(
e as Map<String, Object?>,
path: [...path, '$jsonKey', index++],
),
);
}

set $setterName($dartType value) {
if (value == null) {
Expand All @@ -273,7 +286,7 @@ set $setterName($dartType value) {
final jsonRead = required ? 'stringList' : 'optionalStringList';
final setter = setOrRemove(dartType, jsonKey);
buffer.writeln('''
$dartType get $fieldName => json.$jsonRead('$jsonKey');
$dartType get $fieldName => _reader.$jsonRead('$jsonKey');

set $setterName($dartType value) {
$setter
Expand All @@ -288,7 +301,7 @@ set $setterName($dartType value) {
final jsonRead = required ? 'pathList' : 'optionalPathList';
final setter = setOrRemove(dartType, jsonKey, '.toJson()');
buffer.writeln('''
$dartType get $fieldName => json.$jsonRead('$jsonKey');
$dartType get $fieldName => _reader.$jsonRead('$jsonKey');

set $setterName($dartType value) {
$setter
Expand All @@ -310,10 +323,10 @@ set $setterName($dartType value) {
) {
final fieldName = property.name;
final required = property.isRequired;
final jsonRead = required ? 'path' : 'optionalPath';
final jsonRead = required ? r'path$' : 'optionalPath';
final setter = setOrRemove(dartType, jsonKey, '.toFilePath()');
buffer.writeln('''
$dartType get $fieldName => json.$jsonRead('$jsonKey');
$dartType get $fieldName => _reader.$jsonRead('$jsonKey');

set $setterName($dartType value) {
$setter
Expand Down
13 changes: 0 additions & 13 deletions pkgs/native_assets_cli/lib/src/code_assets/c_compiler_config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,6 @@ final class CCompilerConfig {
WindowsCCompilerConfig? windows,
}) : _windows = windows;

/// Constructs a [CCompilerConfig] from the given [json].
///
/// The json is expected to be valid encoding obtained via
/// [CCompilerConfig.toJson].
factory CCompilerConfig.fromJson(Map<String, Object?> json) =>
CCompilerConfigSyntax.fromSyntax(syntax.CCompilerConfig.fromJson(json));

/// The json representation of this [CCompilerConfig].
///
/// The returned json can be used in [CCompilerConfig.fromJson] to
/// obtain a [CCompilerConfig] again.
Map<String, Object?> toJson() => toSyntax().json;

@override
bool operator ==(Object other) {
if (other is! CCompilerConfig) {
Expand Down
5 changes: 4 additions & 1 deletion pkgs/native_assets_cli/lib/src/code_assets/code_asset.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,10 @@ final class CodeAsset {
factory CodeAsset.fromEncoded(EncodedAsset asset) {
assert(asset.type == CodeAsset.type);
final jsonMap = asset.encoding;
final syntaxNode = syntax.NativeCodeAsset.fromJson(jsonMap);
final syntaxNode = syntax.NativeCodeAsset.fromJson(
jsonMap,
path: asset.jsonPath ?? [],
);
return CodeAsset._(
id: syntaxNode.id,
os: OSSyntax.fromSyntax(syntaxNode.os),
Expand Down
6 changes: 3 additions & 3 deletions pkgs/native_assets_cli/lib/src/code_assets/config.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import 'syntax.g.dart' as syntax;
/// to code assets (only available if code assets are supported).
extension CodeAssetHookConfig on HookConfig {
/// Code asset specific configuration.
CodeConfig get code => CodeConfig._fromJson(json);
CodeConfig get code => CodeConfig._fromJson(json, path);

bool get buildCodeAssets => buildAssetTypes.contains(CodeAsset.type);
}
Expand All @@ -41,8 +41,8 @@ extension CodeAssetLinkInput on LinkInputAssets {
class CodeConfig {
final syntax.CodeConfig _syntax;

CodeConfig._fromJson(Map<String, Object?> json)
: _syntax = syntax.Config.fromJson(json).code!;
CodeConfig._fromJson(Map<String, Object?> json, List<Object> path)
: _syntax = syntax.Config.fromJson(json, path: path).code!;

/// The architecture the code code asset should be built for.
///
Expand Down
Loading