generate method

Library generate()

Implementation

Library generate() {
  lb.body.add(Directive.part(partFileName));
  var myRequireFreezed = false;
  void requireFreezed() {
    if (myRequireFreezed) {
      return;
    }
    if (freezedPartFileName.isEmpty) {
      throw StateError(
          'freeze is required, but no freezedPartFileName was given.');
    }
    lb.body.insert(1, Directive.part(freezedPartFileName));
    myRequireFreezed = true;
  }

  // create class for each schema..
  for (final schemaEntry in api.components!.schemas!.entries) {
    _schemaReference(schemaEntry.key, schemaEntry.value!);
  }
  if (api.components!.securitySchemes != null && !ignoreSecuritySchemes) {
    for (final securityScheme in api.components!.securitySchemes!.entries) {
      _securitySchemeReference(securityScheme.key, securityScheme.value!);
    }
  }

  // Create path configs
  final clientInterface = ClassBuilder()
    ..name = '${baseName}Client'
    ..implements.add(_openApiClient)
    ..abstract = true;
  final fields = [
    MapEntry('baseUri', refer('Uri')),
    MapEntry('requestSender', _openApiRequestSender),
  ];
  final providerClosure = Method((mb) => mb
    ..lambda = true
    ..requiredParameters.add(Parameter((pb) => pb..name = 'ref'))
    ..body = refer('StateError')([literalString('must be overwritten')])
        .thrown
        .code).closure;
  final clientProviderName = '${baseName}ClientProvider'.camelCase;
  if (generateProvider) {
    lb.body.add(declareFinal(clientProviderName)
        .assign(_provider.addGenerics(
            refer(clientInterface.name!.pascalCase))([providerClosure]))
        .statement);
  }

  final urlResolveClass = ClassBuilder()
    ..name = '${baseName}UrlResolve'
    ..mixins.add(_openApiUrlEncodeMixin);
  final clientClass = ClassBuilder()
    ..name = '_${baseName}ClientImpl'
    ..extend = _openApiClientBase
    ..implements.add(refer(clientInterface.name!))
    ..constructors.add(Constructor(
      (cb) => cb
        ..name = '_'
        ..requiredParameters.addAll(fields.map((f) => Parameter((pb) => pb
          ..name = f.key
          ..toThis = true))),
    ))
    ..fields.addAll(fields.map((f) => Field((fb) => fb
      ..name = f.key
      ..type = f.value
      ..annotations.add(_override)
      ..modifier = FieldModifier.final$)));
  clientInterface.constructors.add(Constructor((cb) => cb
    ..factory = true
    ..requiredParameters.addAll(fields.map((f) => Parameter((pb) => pb
      ..name = f.key
      ..type = f.value)))
    ..body = refer(clientClass.name!)
        .newInstanceNamed('_', fields.map((f) => refer(f.key)))
        .code));
  final c = Class((cb) {
    cb.name = baseName;
    cb.implements.add(_openApiEndpoint);
//      cb.types.add(TypeReference((b) => b
//        ..symbol = 'T'
//        ..bound = _openApiRequest));
    cb.abstract = true;
    for (final path in api.paths!.entries) {
      for (final operation in path.value!.operations.entries) {
        final pathName = path.key
            .replaceAll(
                // language=RegExp
                RegExp(r'[{}]'),
                '')
            .camelCase;
        final operationName = operation.value!.id == null
            ? '$pathName'
                '${operation.key.pascalCase}'
            : operation.value!.id!.camelCase;

        final responseClass = ClassBuilder();
        responseClass
          ..name = '${operationName.pascalCase}Response'
          ..sealed = true
          ..extend = _openApiResponse
          ..constructors.add(Constructor());
//            ..constructors.add(Constructor((cb) => cb
//              ..name = '_'
//              ..requiredParameters.add(Parameter((pb) => pb
//                ..name = 'status'
//                ..toThis = true))))
        final mapMethod = MethodBuilder()
          ..name = 'map'
          ..types.add(const Reference('R'))
          ..returns = const Reference('R');
        final mapCode = <Code>[];
        Reference? successResponseBodyType;
        Reference? successResponseCodeType;
        MapEntry<String, APIResponse?>? successApiResponse;
        final clientResponseParse = <String, Expression>{};
        for (final response in operation.value!.responses!.entries) {
          final statusAsParameter = response.key == 'default';
          final codeName = response.key.pascalCase;
          final responseCodeClass = ClassBuilder()
            ..extend = refer(responseClass.name!)
            ..name = '${responseClass.name}$codeName'
            ..fields.add(Field((fb) => fb
              ..name = 'status'
              ..annotations.add(_override)
              ..modifier = FieldModifier.final$
              ..type = refer('int')));
          mapMethod.optionalParameters.add(Parameter((pb) => pb
            ..name = 'on$codeName'
            ..asRequired(this, true)
            ..named = true
            ..type = _responseMapType
                .addGenerics(refer(responseCodeClass.name!))
                .addGenerics(const Reference('R'))));
          if (mapCode.isNotEmpty) {
            mapCode.add(const Code(' else '));
          }
          mapCode.add(const Code('if (this is '));
          mapCode.add(refer(responseCodeClass.name!).code);
          mapCode.add(const Code(') {'));
          mapCode.add(refer('on$codeName')(
                  [refer('this').asA(refer(responseCodeClass.name!))])
              .returned
              .statement);
          mapCode.add(const Code('}'));
          final clientResponseParseParams = <Expression>[];
          final constructor = Constructor((cb) {
            cb
              ..name = 'response$codeName'
              ..docs.addDartDoc(response.value!.description);

            refer(cb.name!);
            if (statusAsParameter) {
              cb
                ..requiredParameters.add(
                  Parameter((pb) => pb
                    ..name = 'status'
                    ..type = refer('int')),
                )
                ..initializers
                    .add(refer('status').assign(refer('status')).code);
              clientResponseParseParams
                  .add(refer('response').property('status'));
            } else {
              cb.initializers.add(refer('status')
                  .assign(literalNum(int.parse(response.key)))
                  .code);
            }
            final content =
                (response.value!.content ?? {})[mediaTypeJson.contentType];
//              OpenApiContentType responseContentType;
            Code? responseContentTypeAssignment = _literalNullCode;
            Reference? bodyType;
            if (content != null) {
              const responseContentType = OpenApiContentType.json;
              responseContentTypeAssignment = _openApiContentType
                  .newInstanceNamed('parse',
                      [literalString(responseContentType.toString())]).code;
              final responseSchema = content.schema!;
              if (responseSchema.type == APIType.array) {
                final bodyItemType = _schemaReference(
                    '${responseClass.name}Body$codeName',
                    responseSchema.items!);
                bodyType = _referType('List', generics: [bodyItemType]);
                cb.requiredParameters.add(Parameter((pb) => pb
                  ..name = 'body'
                  ..type = bodyType
                  ..toThis = true));
                // TODO add server side support for arrays.
                // cb.initializers.add(refer('bodyJson')
                //     .assign(refer('body').property('toJson')([]))
                //     .code);
                cb.initializers
                    .add(refer('bodyJson').assign(literalMap({})).code);
                clientResponseParseParams.add(refer('response')
                    .property('responseBodyJsonDynamic')([])
                    .awaited
                    .asA(_referType('List', generics: [refer('dynamic')]))
                    .property('map')([
                      Method((mb) {
                        mb.requiredParameters
                            .add(Parameter((pb) => pb..name = 'item'));
                        mb.lambda = true;
                        mb.body = bodyItemType.newInstanceNamed('fromJson', [
                          refer('item').asA(_referType('Map',
                              generics: [refer('String'), refer('dynamic')]))
                        ]).code;
                      }).closure
                    ])
                    .property('toList')([]));
              } else {
                bodyType = _schemaReference(
                    '${responseClass.name}Body$codeName', content.schema!);
                cb.requiredParameters.add(Parameter((pb) => pb
                  ..name = 'body'
                  ..type = bodyType
                  ..toThis = true));
                cb.initializers.add(refer('bodyJson')
                    .assign(refer('body').property('toJson')([]))
                    .code);

                clientResponseParseParams.add(bodyType.newInstanceNamed(
                    'fromJson', [
                  refer('response').property('responseBodyJson')([]).awaited
                ]));
              }
              responseCodeClass.fields.add(Field((fb) => fb
                ..name = 'body'
                ..type = bodyType
                ..modifier = FieldModifier.final$));
              responseCodeClass.fields.add(Field((fb) => fb
                ..name = 'bodyJson'
                ..annotations.add(_override)
                ..type = _referType('Map',
                    generics: [_typeString, refer('dynamic')])
                ..modifier = FieldModifier.final$));
              responseCodeClass.implements.add(_openApiResponseBodyJson);
            } else {
              if (response.value!.content?.length == 1) {
                final responseContent =
                    response.value!.content!.entries.first;
                final responseContentType =
                    OpenApiContentType.parse(responseContent.key);
                responseContentTypeAssignment = _openApiContentType
                    .newInstanceNamed('parse',
                        [literalString(responseContentType.toString())]).code;
                bodyType = _toDartType('${responseCodeClass}Content',
                    responseContent.value!.schema!);
                checkState(
                    responseContent.value!.schema!.type == APIType.string,
                    message: 'schema type not supported for content type '
                        '${responseContent.key}: '
                        '${responseContent.value!.schema!.type}');
                responseCodeClass.fields.add(Field((fb) => fb
                  ..name = 'body'
                  ..type = bodyType
                  ..annotations.add(_override)
                  ..modifier = FieldModifier.final$));
                if (responseContent.key.contains('*')) {
                  responseContentTypeAssignment = null;
                  cb.requiredParameters.add(Parameter((pb) => pb
                    ..type = _openApiContentType
                    ..name = 'contentType'
                    ..toThis = true));
                  clientResponseParseParams.add(
                      refer('response').property('responseContentType')([]));
                }
                cb.requiredParameters.add(Parameter((pb) => pb
                  ..name = 'body'
                  ..type = bodyType
                  ..toThis = true));
                if (_typeString == bodyType) {
                  responseCodeClass.implements
                      .add(_openApiResponseBodyString);
                  clientResponseParseParams.add(refer('response')
                      .property('responseBodyString')([])
                      .awaited);
                } else if (_uint8List == bodyType) {
                  responseCodeClass.implements
                      .add(_openApiResponseBodyBinary);
                  clientResponseParseParams.add(refer('response')
                      .property('responseBodyBytes')([])
                      .awaited);
                } else {
                  throw StateError(
                      'Unsupported bodyType $bodyType for responses.');
                }
              }
            }
            if (response.key.startsWith('2') ||
                (response.key == 'default' &&
                    successResponseBodyType == null)) {
              successResponseCodeType = refer(responseCodeClass.name!);
              successResponseBodyType = bodyType;
              successApiResponse = response;
            }
            responseCodeClass.fields.add(Field(
              (fb) => fb
                ..name = 'contentType'
                ..type = responseContentTypeAssignment == _literalNullCode
                    ? _openApiContentTypeNullable
                    : _openApiContentType
                ..annotations.add(_override)
                ..modifier = FieldModifier.final$
                ..assignment = responseContentTypeAssignment,
            ));
          });

          responseCodeClass.constructors.add((constructor.toBuilder()
                ..requiredParameters.map((pb) => (pb.toBuilder()
                      ..type = pb.toThis ? null : pb.type)
                    .build()))
              .build());
          responseCodeClass.methods.add(Method((mb) => mb
            ..name = 'propertiesToString'
            ..annotations.add(_override)
            ..returns = _referType('Map',
                generics: [_typeString, refer('Object').asNullable(true)])
            ..lambda = true
            ..body = literalMap(
              Map.fromEntries(responseCodeClass.fields.build().map(
                  (f) => MapEntry(literalString(f.name), refer(f.name)))),
            ).code));
          responseClass.constructors.add((constructor.toBuilder()
                ..factory = true
                ..initializers.clear()
                ..requiredParameters
                    .map((pb) => (pb.toBuilder()..toThis = false).build())
                ..body = refer(responseCodeClass.name!)
                    .newInstanceNamed(
                        constructor.name!,
                        constructor.requiredParameters
                            .map((e) => refer(e.name))
                            .toList())
                    .code)
              .build());
          clientResponseParse[response.key] = Method((mb) => mb
            ..modifier = MethodModifier.async
            ..requiredParameters.add(Parameter((pb) => pb
              ..name = 'response'
              ..type = _openApiClientResponse))
            ..body = refer(responseCodeClass.name!)
                .newInstanceNamed(
                    constructor.name!, clientResponseParseParams)
                .code).closure;
          lb.body.add(responseCodeClass.build());
        }
        if (mapCode.isNotEmpty) {
          mapMethod.optionalParameters.add(Parameter((pb) => pb
            ..name = 'onElse'
            // ..asRequired(this, true)
            ..named = true
            ..type = _responseMapType
                .addGenerics(refer(responseClass.name!))
                .addGenerics(const Reference('R'))
                .asNullable(true)));
          mapCode.add(const Code('else '));
          mapCode.add(ifStatement(
            refer('onElse').notEqualTo(literalNull),
            refer('onElse')([refer('this')]).returned.statement,
            elseCode: refer('StateError')
                .newInstance(
                    [literalString(r'Invalid instance of type $this')])
                .thrown
                .statement,
          ));
          // mapCode.add(const Code(r'''
          //     if (onElse != null) {
          //       return onElse();
          //     } else {
          //       throw StateError('Invalid instance type $this');
          //     }'''));
          // mapCode.add(const Code('}'));
        }
        mapMethod.body = Block.of(mapCode);
        responseClass.methods.add(mapMethod.build());

        if (successApiResponse != null) {
          ArgumentError.checkNotNull(
              successResponseCodeType, 'successResponseCodeType');
          responseClass.implements.add(_hasSuccessResponse
              .addGenerics(successResponseBodyType ?? _void));
          responseClass.methods.add(
            Method((mb) => mb
              ..name = 'requireSuccess'
              ..addDartDoc(successApiResponse!.value!.description,
                  prefix: 'status ${successApiResponse!.key}: ')
              ..annotations.add(_override)
              ..returns = successResponseBodyType ?? _void
              ..body = Block.of([
                const Code('if (this is '),
                successResponseCodeType!.code,
                const Code(') {'),
                successResponseBodyType == null
                    // success, but no body.
                    ? const Code('return;')
                    : refer('this')
                        .asA(successResponseCodeType!)
                        .property('body')
                        .returned
                        .statement,
                const Code('} else {'),
                const Code(
                    r'''throw StateError('Expected success response, but got $this');'''),
                const Code('}'),
              ])),
          );
        }

        lb.body.add(responseClass.build());

        final clientDataClass = ClassBuilder()
          ..name = operationName.pascalCase
          ..mixins.add(refer('_\$${operationName.pascalCase}'))
          ..annotations.add(_freezed);
        final clientDataConstructor = ConstructorBuilder()
          ..factory = true
          ..redirect = refer('_${operationName.pascalCase}');

        final clientMethod = MethodBuilder()
          ..name = operationName
          ..addDartDoc(operation.value!.summary)
          ..addDartDoc(operation.value!.description)
          ..docs.add('/// ${operation.key}: ${path.key}')
          ..docs.add('///')
          ..returns =
              _referType('Future', generics: [refer(responseClass.name!)])
          ..modifier = MethodModifier.async;
        final clientCode = <Code>[
          declareFinal('request')
              .assign(_openApiClientRequest.newInstance([
                literalString(operation.key),
                literalString(path.key),
                _operationSecurityRequirements(
                    operation.value!.security ?? api.security),
              ]))
              .statement,
        ];
        final clientCodeRequest = refer('request');

        cb.methods.add(Method((mb) {
          mb
            ..name = operationName
            ..addDartDoc(operation.value!.summary)
            ..addDartDoc(operation.value!.description)
            ..docs.add('/// ${operation.key}: ${path.key}')
            ..returns =
                _referType('Future', generics: [refer(responseClass.name!)]);

          final routerParams = <Expression>[];
          final routerParamsNamed = <String, Expression>{};

          if (apiMethodsWithRequest) {
            mb.requiredParameters.add(Parameter((pb) => pb
              ..name = 'request'
              ..type = _openApiRequest));
            routerParams.add(refer('request'));
          }

          // ignore: avoid_function_literals_in_foreach_calls
          final allParameters = [
            ...?path.value!.parameters,
            ...?operation.value!.parameters
          ];
          for (final param in allParameters) {
            final paramType = _toDartType(operationName, param!.schema!);
            final paramNameCamelCase = param.name!.camelCase;
            if (param.description != null) {
              clientMethod.docs
                  .add('/// * [$paramNameCamelCase]: ${param.description}');
            }
            final p = Parameter((pb) => pb
              ..name = paramNameCamelCase
              ..type = paramType.asNullable(!param.isRequired)
              ..asRequired(this, param.isRequired)
              ..named = true);
            mb.optionalParameters.add(p);
            clientMethod.optionalParameters.add(p);
            clientDataConstructor.optionalParameters.add(p.rebuild((pb) => pb
                // ..toThis = true
                ));
            // clientDataClass.fields.add(Field((fb) => fb
            //   ..name = paramNameCamelCase
            //   ..modifier = FieldModifier.final$
            //   ..type = paramType.asNullable(!param.isRequired)));
            Expression decodeParameterFrom(
                APIParameter param, Expression expression) {
              final schemaType = ArgumentError.checkNotNull(
                  param.schema?.type, 'param.schema.type');
              switch (schemaType) {
                case APIType.string:
                  final asString = refer('paramToString')([expression]);
                  if (param.schema!.format == 'uuid') {
                    assert(paramType == _apiUuid);
                    return _apiUuid.newInstanceNamed('parse', [asString]);
                  } else if (param.schema?.enumerated?.isNotEmpty == true) {
                    final paramEnumType = paramType;
                    return refer('${paramEnumType.symbol!}Ext')
                        .property('fromName')([asString]);
                  } else if (paramType == _typeDateTime) {
                    return _typeDateTime.property('parse')([asString]);
                  } else if (paramType != _typeString) {
                    throw StateError(
                        'Unsupported paramType for string $paramType');
                  }
                  return asString;
                case APIType.number:
                  return refer('paramToNum')([expression]);
                case APIType.integer:
                  return refer('paramToInt')([expression]);
                case APIType.boolean:
                  return refer('paramToBool')([expression]);
                case APIType.array:
                  checkState(param.schema!.items!.type == APIType.string);
                  if (param.schema!.items!.enumerated != null &&
                      param.schema!.items!.enumerated!.isNotEmpty) {
                    final paramEnumType =
                        (paramType as TypeReference).types.first;
                    return expression
                        .property('map')([
                          Method(
                            (mb) => mb
                              ..lambda = true
                              ..requiredParameters
                                  .add(Parameter((pb) => pb..name = 'e'))
                              ..body = refer('${paramEnumType.symbol!}Ext')
                                  .property('fromName')([refer('e')])
                                  .code,
                          ).closure
                        ])
                        .property('toList')([]);
                  }
                  return expression;
                case APIType.object:
                  return expression;
                default:
                  throw StateError('Invalid schema type $schemaType');
              }
            }

            Expression decodeParameter(Expression? expression) {
              return refer(param.isRequired ? 'paramRequired' : 'paramOpt')(
                  [],
                  {
                    'name': literalString(param.name!),
                    'value': expression!,
                    'decode': Method((mb) => mb
                          ..lambda = true
                          ..requiredParameters
                              .add(Parameter((pb) => pb..name = 'value'))
                          ..body =
                              decodeParameterFrom(param, refer('value')).code)
                        .closure,
                  });
            }

            Expression encodeParameter(Expression expression) {
              final schemaType = ArgumentError.checkNotNull(
                  param.schema?.type, 'param.schema.type');
              switch (schemaType) {
                case APIType.string:
                  if (param.schema!.format == 'uuid') {
                    assert(paramType == _apiUuid);
                    if (param.isRequired) {
                      expression = expression.property('encodeToString')([]);
                    } else {
                      expression =
                          expression.nullSafeProperty('encodeToString')([]);
                    }
                  } else if (param.schema?.enumerated?.isNotEmpty == true) {
                    if (param.isRequired) {
                      expression = expression.property('name');
                    } else {
                      expression = expression.nullSafeProperty('name');
                    }
                  } else if (paramType == _typeDateTime) {
                    if (param.isRequired) {
                      expression = expression.property('toIso8601String')([]);
                    } else {
                      expression =
                          expression.nullSafeProperty('toIso8601String')([]);
                    }
                  } else if (paramType != _typeString) {
                    // TODO not sure if this makes sense, maybe we should just
                    //      use `toString`?
                    throw StateError(
                        'encodeParameter: Unsupported paramType for string $paramType');
                  }
                  return refer('encodeString')([expression]);
                case APIType.number:
                  return refer('encodeNum')([expression]);
                case APIType.integer:
                  return refer('encodeInt')([expression]);
                case APIType.boolean:
                  return refer('encodeBool')([expression]);
                case APIType.array:
                  checkState(param.schema!.items!.type == APIType.string);
                  if (param.schema!.items!.enumerated != null &&
                      param.schema!.items!.enumerated!.isNotEmpty) {
                    return expression.property('map')([
                      Method(
                        (mb) => mb
                          ..lambda = true
                          ..requiredParameters
                              .add(Parameter((pb) => pb..name = 'e'))
                          ..body = refer('e').property('name').code,
                      ).closure
                    ]);
                  }
                  return expression;
                case APIType.object:
                  return expression;
              }
            }

            final paramLocation = ArgumentError.checkNotNull(param.location);
            final paramName = ArgumentError.checkNotNull(param.name);
            routerParamsNamed[paramNameCamelCase] =
                decodeParameter(_readFromRequest(paramLocation, paramName));
            clientCode.add(_writeToRequest(
              clientCodeRequest,
              paramLocation,
              paramName,
              encodeParameter(refer(paramNameCamelCase)),
            ).statement);
          }
          final urlResolverMethod = clientMethod.build().toBuilder()
            ..returns = _openApiClientRequest
            ..modifier = null
            ..body =
                Block.of(clientCode + [clientCodeRequest.returned.statement]);
          urlResolveClass.methods.add(urlResolverMethod.build());

          final body = operation.value!.requestBody;
          if (body != null && body.content!.isNotEmpty) {
            final entry = body.content!.entries.first;

            if (body.content!.length > 1) {
              _logger.warning('Right now we only support one request body, '
                  'but found: ${body.content!.keys}, only using $entry');
            }

            Map.fromEntries([entry]).forEach((key, reqBody) {
              final contentType = OpenApiContentType.parse(key);
//                final ct = OpenApiContentType.allKnown
//                    .firstWhere((element) => element.matches(contentType));
              _createRequestBody(
                contentType,
                reqBody!,
                operationName,
                mb,
                routerParams,
                clientMethod,
                clientCode,
              );
            });
          }

          _routerConfig(
            path.key,
            operation.key,
            refer('impl').property('invoke')([
              refer('request'),
              Method((m) => m
                ..requiredParameters.add(Parameter((pb) => pb
                  ..type = refer(baseName)
                  ..name = 'impl'))
                ..lambda = true
                ..body = refer('impl')
                    .property(operationName)(routerParams, routerParamsNamed)
//                        .returned
                    .code
                ..modifier = MethodModifier.async).closure
            ]),
            operation.value!.security ?? api.security,
          ); //.property(operationName)(parameters));
        }));

        clientCode.add(refer('sendRequest')(
                [refer('request'), literalMap(clientResponseParse)])
            .awaited
            .returned
            .statement);

        clientMethod.body = Block.of(clientCode);
        clientClass.methods
            .add((clientMethod..annotations.add(_override)).build());
        clientInterface.methods.add((clientMethod.build().toBuilder()
              ..annotations.clear()
              ..body = null)
            .build());
        if (generateProvider && operation.key.toLowerCase() == 'get') {
          final params = clientDataConstructor.build().optionalParameters;
          clientDataClass.constructors.add(clientDataConstructor.build());
          // clientDataClass.build().fields.
          if (params.length > 1) {
            lb.body.add(clientDataClass.build());
            requireFreezed();
          }

// final baseBaseIdGet = _i1.StreamProvider.family<BaseBaseIdGetResponse, BaseBaseIdGet>((ref, arg) {
//   final client = ref.watch(mywarmApiClientProvider);
//   return Stream.fromFuture(client.baseBaseIdGet(baseId: arg.baseId));
// });
          final m = Method((mb) {
            mb.requiredParameters.add(Parameter((pb) => pb..name = 'ref'));
            if (params.isNotEmpty) {
              mb.requiredParameters.add(Parameter((pb) => pb..name = 'arg'));
            }
            mb.body = Block.of([
              declareFinal('client')
                  .assign(refer('ref')
                      .property('watch')([refer(clientProviderName)]))
                  .statement,
              refer('Stream')
                  .property('fromFuture')(
                    [
                      refer('client').property(operationName)(
                          [],
                          params.isEmpty
                              ? {}
                              : params.length == 1
                                  ? {
                                      params.first.name: refer('arg'),
                                    }
                                  : Map.fromEntries(params.map((f) =>
                                      MapEntry(f.name,
                                          refer('arg').property(f.name))))),
                    ],
                  )
                  .returned
                  .statement,
            ]);
          });
          final providerName =
              '${providerNamePrefix.isEmpty ? operationName.camelCase : '$providerNamePrefix${operationName.pascalCase}'}Provider';
          final Expression createProvider;
          if (params.isEmpty) {
            createProvider = _streamProvider.property('autoDispose')(
                [m.closure], {}, [refer(responseClass.name!)]);
          } else {
            createProvider = _streamProvider
                .property('autoDispose')
                .property('family')
                .call([
              m.closure
            ], {}, [
              refer(responseClass.name!),
              params.length > 1
                  ? refer(clientDataClass.name!)
                  : params.first.type!
            ]);
          }
          lb.body.add(
              declareFinal(providerName).assign(createProvider).statement);
        }
      }
    }
  });
  lb.body.add(c);
  lb.body.add(clientInterface.build());
  lb.body.add(clientClass.build());
  lb.body.add(urlResolveClass.build());

  lb.body.add(Class((cb) {
    cb.name = '${baseName}Router';
    cb.constructors.add(Constructor((cb) => cb
      ..requiredParameters.add(Parameter((pb) => pb
        ..name = 'impl'
        ..toThis = true))));
    cb.extend = refer(
        'OpenApiServerRouterBase', 'package:openapi_base/openapi_base.dart');
    cb.fields.add(Field((fb) => fb
      ..name = 'impl'
      ..type = _endpointProvider.addGenerics(refer(c.name))
      ..modifier = FieldModifier.final$));
    cb.methods.add(Method((mb) => mb
      ..name = 'configure'
      ..annotations.add(_override)
      ..returns = refer('void')
      ..body = Block.of(routerConfig.map((e) => e!.statement))));
  }));
  lb.body.add(securitySchemesClass.build());

//       api.paths.map((key, value) => MapEntry(key, refer('ApiPathConfig').newInstance([value.])))
  return lb.build();
}