| // Copyright (c) 2021, 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 'package:collection/collection.dart'; |
| |
| import 'json_schema.dart'; |
| |
| String _toDartType(String type) { |
| if (type.startsWith('#/definitions/')) { |
| return type.replaceAll('#/definitions/', ''); |
| } |
| switch (type) { |
| case 'object': |
| return 'Map<String, Object?>'; |
| case 'integer': |
| return 'int'; |
| case 'number': |
| return 'num'; |
| case 'string': |
| return 'String'; |
| case 'boolean': |
| return 'bool'; |
| case 'null': |
| return 'Null'; |
| default: |
| return type; |
| } |
| } |
| |
| String _toDartUnionType(List<String> types) { |
| const allLiteralTypes = { |
| 'array', |
| 'boolean', |
| 'integer', |
| 'null', |
| 'number', |
| 'object', |
| 'string' |
| }; |
| if (types.length == 7 && allLiteralTypes.containsAll(types)) { |
| return 'Object'; |
| } |
| return 'Either${types.length}<${types.map(_toDartType).join(', ')}>'; |
| } |
| |
| extension JsonSchemaExtensions on JsonSchema { |
| JsonType typeFor(JsonType type) => type.dollarRef != null |
| // TODO(dantup): Do we need to support more than just refs to definitions? |
| ? definitions[type.refName]! |
| : type; |
| |
| Map<String, JsonType> propertiesFor(JsonType type, |
| {bool includeBase = true}) { |
| // Merge this types direct properties with anything from the included |
| // (allOf) types, but excluding those that come from the base class. |
| final baseType = type.baseType; |
| final includedBaseTypes = |
| (type.allOf ?? []).where((t) => includeBase || t != baseType); |
| final properties = { |
| for (final other in includedBaseTypes) ...propertiesFor(typeFor(other)), |
| ...?type.properties, |
| }; |
| |
| return properties; |
| } |
| } |
| |
| extension JsonTypeExtensions on JsonType { |
| String asDartType({bool isOptional = false}) { |
| final dartType = dollarRef != null |
| ? _toDartType(dollarRef!) |
| : oneOf != null |
| ? _toDartUnionType(oneOf!.map((item) => item.asDartType()).toList()) |
| : type!.valueEquals('array') |
| ? 'List<${items!.asDartType()}>' |
| : type!.map(_toDartType, _toDartUnionType); |
| |
| return isOptional ? '$dartType?' : dartType; |
| } |
| |
| /// Whether this type can have any type of value (Object/dynamic/any). |
| bool get isAny => asDartType() == 'Object'; |
| |
| /// Whether this type represents a List. |
| bool get isList => type?.valueEquals('array') ?? false; |
| |
| /// Whether this type is a simple value that does not need any special handling. |
| bool get isSimple { |
| const _dartSimpleTypes = { |
| 'bool', |
| 'int', |
| 'num', |
| 'String', |
| 'Map<String, Object?>', |
| 'Null', |
| }; |
| return _dartSimpleTypes.contains(asDartType()); |
| } |
| |
| /// Whether this type is a Union type using JSON schema's "oneOf" of where its |
| /// [type] is a list of types. |
| bool get isUnion => |
| oneOf != null || type != null && type!.map((_) => false, (_) => true); |
| |
| /// Whether this type is a reference to another spec type (using `dollarRef`). |
| bool get isSpecType => dollarRef != null; |
| |
| /// Whether [propertyName] is a required for this type or its base types. |
| bool requiresField(String propertyName) { |
| if (required?.contains(propertyName) ?? false) { |
| return true; |
| } |
| if (allOf?.any((type) => root.typeFor(type).requiresField(propertyName)) ?? |
| false) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// The name of the type that this one references. |
| String get refName => dollarRef!.replaceAll('#/definitions/', ''); |
| |
| /// The literal value of this type, if it can have only one. |
| /// |
| /// These are represented in the spec using an enum with only a single value. |
| String? get literalValue => enumValues?.singleOrNull; |
| |
| /// The base type for this type. Base types are inferred by a type using |
| /// allOf and the first listed type being a reference (dollarRef) to another |
| /// spec type. |
| JsonType? get baseType { |
| final all = allOf; |
| if (all != null && all.length > 1 && all.first.dollarRef != null) { |
| return all.first; |
| } |
| return null; |
| } |
| |
| /// The list of possible types allowed by this union. |
| /// |
| /// May be represented using `oneOf` or a list of types in `type`. |
| List<JsonType> get unionTypes { |
| final types = oneOf ?? |
| // Fabricate a union for types where "type" is an array of literal types: |
| // ['a', 'b'] |
| type!.map( |
| (_) => throw 'unexpected non-union in isUnion condition', |
| (types) => |
| types.map((t) => JsonType.fromJson(root, {'type': t})).toList(), |
| )!; |
| return types; |
| } |
| } |