toFreezedClass function

Spec toFreezedClass(
  1. String className,
  2. ObjectType<RuleContext> obj,
  3. GenOption option
)

Implementation

Spec toFreezedClass(String className, ts.ObjectType obj, GenOption option) {
  final isVariant = obj.isVariant;
  final optionalParameters = <Parameter>[];
  final fromJson = StringBuffer();
  final toJson = StringBuffer();
  final toJsonFields = StringBuffer();
  for (final e in obj.children) {
    final child = e.child;
    final idlName = child.id!;
    var fieldName = idlName.camelCase;
    if (kDartKeywordsAndInternalTypes.contains(fieldName)) {
      fieldName += '_';
    }
    final isNumberKey = RegExp(r'^\d+$').hasMatch(fieldName);
    if (isNumberKey) {
      fieldName = '\$$fieldName';
    }
    final isIdType = child is ts.IdType;
    var dartType = child.dartType();
    final useBool = (isIdType && isVariant) || dartType == 'null';
    final isOpt = isIdType ||
        (child as ts.PairType).value.child is ts.OptType ||
        isVariant;
    if (isOpt && !dartType.endsWith('?')) {
      dartType += '?';
    }
    final refType = Reference(useBool ? 'bool' : dartType);
    final parameter = Parameter(
      (b) => b
        ..named = true
        ..type = refType
        ..required = !isOpt
        ..annotations = ListBuilder(
          useBool ? [const CodeExpression(Code('Default(false)'))] : [],
        )
        ..docs = ListBuilder(
          ['/// [$fieldName] defined in Candid: `${child.did}`'],
        )
        ..name = fieldName,
    );
    optionalParameters.add(parameter);
    if (useBool) {
      fromJson.writeln("$fieldName: json.containsKey('$idlName'),");
      toJson.writeln("if ($fieldName) '$idlName': null,");
    } else {
      var deser = child.deserialize(nullable: isOpt);
      if (deser != null) {
        if (isNumberKey) {
          deser = deser.replaceAll(ts.IDLType.ph, 'json[$idlName]');
        } else {
          deser = deser.replaceAll(ts.IDLType.ph, "json['$idlName']");
        }
      } else {
        if (isNumberKey) {
          deser = 'json[$idlName]';
        } else {
          deser = "json['$idlName']";
        }
      }
      fromJson.writeln('$fieldName: $deser,');
      final ser = child.serialize();
      final arg =
          ser == null ? fieldName : ser.replaceAll(ts.IDLType.ph, fieldName);
      var isOptChild = false;
      if (child is ts.PairType) {
        final value = child.value.child;
        isOptChild = value is ts.OptType ||
            (value is ts.IdType &&
                value.child is ts.Id &&
                (value.child as ts.Id).isOpt);
      }
      if ((!isVariant && isOptChild) || !isOpt) {
        if (isNumberKey) {
          toJson.writeln('$idlName: $arg,');
        } else {
          toJson.writeln("'$idlName': $arg,");
        }
      } else {
        if (isNumberKey) {
          toJson.writeln('if ($fieldName != null) $idlName: $arg,');
        } else {
          toJson.writeln("if ($fieldName != null) '$idlName': $arg,");
        }
      }
    }
    toJsonFields.writeln('final $fieldName = this.$fieldName;');
  }
  return Class(
    (b) => b
      ..name = className
      ..mixins = ListBuilder([Reference('_\$$className')])
      ..annotations = ListBuilder([CodeExpression(Code(option.annotation))])
      ..constructors = ListBuilder([
        Constructor(
          (b) => b
            ..name = '_'
            ..constant = true,
        ),
        Constructor(
          (b) => b
            ..optionalParameters = ListBuilder(optionalParameters)
            ..redirect = Reference('_$className')
            ..factory = true
            ..constant = true,
        ),
        Constructor(
          (b) => b
            ..name = 'fromJson'
            ..factory = true
            ..body = Code('return ${obj.ctx.getClassName()}($fromJson);')
            ..requiredParameters = ListBuilder([
              Parameter(
                (b) => b
                  ..type = const Reference('Map')
                  ..name = 'json',
              ),
            ]),
        ),
      ])
      ..methods = ListBuilder([
        Method(
          (b) => b
            ..name = 'toJson'
            ..body = Code('${toJsonFields}return { $toJson };')
            ..returns = const Reference('Map<String, dynamic>'),
        ),
        toStringMethod,
      ])
      ..docs = ListBuilder(['/// [$className] defined in Candid', obj.doc]),
  );
}