A framework for building convergent cross-platform Nextcloud clients using Flutter.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

127 lines
4.0 KiB

import 'dart:convert';
import 'package:build/build.dart';
import 'package:built_collection/built_collection.dart';
import 'package:checked_yaml/checked_yaml.dart';
import 'package:code_builder/code_builder.dart';
import 'package:dart_style/dart_style.dart';
import 'package:dynamite/src/builder/client.dart';
import 'package:dynamite/src/builder/imports.dart';
import 'package:dynamite/src/builder/resolve_type.dart';
import 'package:dynamite/src/builder/serializer.dart';
import 'package:dynamite/src/builder/state.dart';
import 'package:dynamite/src/helpers/dart_helpers.dart';
import 'package:dynamite/src/models/open_api.dart';
import 'package:dynamite/src/models/serializers.dart';
import 'package:dynamite/src/type_result/type_result.dart';
class OpenAPIBuilder implements Builder {
@override
final buildExtensions = const {
'.openapi.json': ['.openapi.dart'],
'.openapi.yaml': ['.openapi.dart'],
};
@override
Future<void> build(final BuildStep buildStep) async {
try {
final inputId = buildStep.inputId;
final outputId = inputId.changeExtension('.dart');
final emitter = DartEmitter(
orderDirectives: true,
useNullSafetySyntax: true,
);
final spec = switch (inputId.extension) {
'json' => serializers.deserializeWith(
OpenAPI.serializer,
json.decode(await buildStep.readAsString(inputId)),
)!,
'yaml' => checkedYamlDecode(
await buildStep.readAsString(inputId),
(final m) => serializers.deserializeWith(OpenAPI.serializer, m)!,
),
_ => throw StateError('Openapi specs can only be yaml or json.'),
};
final supportedVersions = ['3.0.3', '3.1.0'];
if (!supportedVersions.contains(spec.version)) {
throw Exception('Only OpenAPI ${supportedVersions.join(', ')} are supported');
}
final state = State(spec.info.title);
final output = ListBuilder<Spec>()
..addAll(generateImports(outputId))
..addAll(generateClients(spec, state));
if (spec.components?.schemas != null) {
for (final schema in spec.components!.schemas!.entries) {
final identifier = toDartName(schema.key, uppercaseFirstCharacter: true);
if (schema.value.type == null && schema.value.ref == null && schema.value.ofs == null) {
output.add(
TypeDef(
(final b) => b
..name = identifier
..definition = refer('dynamic'),
),
);
} else {
final result = resolveType(
spec,
state,
identifier,
schema.value,
);
if (result is TypeResultBase) {
output.add(
TypeDef(
(final b) => b
..name = identifier
..definition = refer(result.name),
),
);
}
}
}
}
output
..addAll(state.output)
..addAll(buildSerializer(state));
final patterns = [
RegExp(
r'const .*\._\(\);',
),
RegExp(
r'factory .*\.fromJson\(Map<String, dynamic> json\) => _jsonSerializers\.deserializeWith\(serializer, json\)!;',
),
RegExp(
r'Map<String, dynamic> toJson\(\) => _jsonSerializers\.serializeWith\(serializer, this\)! as Map<String, dynamic>;',
),
RegExp(
r'static BuiltSet<.*> get values => _\$.*Values;',
),
];
var outputString = output.build().map((final e) => e.accept(emitter)).join('\n');
for (final pattern in patterns) {
outputString = outputString.replaceAllMapped(
pattern,
(final match) => ' // coverage:ignore-start\n${match.group(0)}\n // coverage:ignore-end',
);
}
final formatter = DartFormatter(pageWidth: 120);
await buildStep.writeAsString(
outputId,
formatter.format(outputString),
);
} catch (e, s) {
print(s);
rethrow;
}
}
}