toClass function
Implementation
Spec toClass(
String className,
ts.ObjectType obj,
GenOption option,
) {
final isVariant = obj.isVariant;
final constructorParameters = <Parameter>[];
final fromJson = StringBuffer();
final fromDeserializable = StringBuffer();
final toJson = StringBuffer();
final toJsonFields = StringBuffer();
final toSerializable = StringBuffer();
final toSerializableFields = StringBuffer();
final hashes = <String>[];
final equals = <String>[];
final fields = <Field>[];
final copyWithParameters = <Parameter>[];
final copyWithAssign = 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;
String 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 isList = dartType.startsWith('List<');
if (isList) {
hashes.add('const DeepCollectionEquality().hash($fieldName)');
equals.add(
'const DeepCollectionEquality().equals(other.$fieldName, $fieldName)',
);
} else {
hashes.add(fieldName);
equals.add(
'(identical(other.$fieldName, $fieldName) || other.$fieldName == $fieldName)',
);
}
final type = Reference(useBool ? 'bool' : dartType);
constructorParameters.add(
Parameter(
(b) => b
..named = true
..required = !isOpt
..defaultTo = useBool ? const Code('false') : null
..name = fieldName
..toThis = true,
),
);
copyWithParameters.add(
Parameter(
(b) => b
..name = fieldName
..named = true
..type = Reference(
useBool
? 'bool?'
: dartType.endsWith('?')
? dartType
: '$dartType?',
),
),
);
copyWithAssign.writeln('$fieldName: $fieldName ?? this.$fieldName,');
fields.add(
Field(
(b) => b
..name = fieldName
..type = type
..modifier = FieldModifier.final$
..docs = ListBuilder(
['/// [$fieldName] defined in Candid: `${child.did}`'],
),
),
);
if (useBool) {
fromDeserializable.writeln("$fieldName: obj.containsKey('$idlName'),");
toSerializable.writeln("if ($fieldName) '$idlName': null,");
if (option.explicitSerializationMethods) {
fromJson.writeln("$fieldName: json['$idlName'],");
toJson.writeln("'$idlName': $fieldName,");
} else {
fromJson.writeln("$fieldName: json.containsKey('$idlName'),");
toJson.writeln("if ($fieldName) '$idlName': null,");
}
} else {
final deserJson = child.deserializeAndReplace(
replace: isNumberKey ? 'json[$idlName]' : "json['$idlName']",
fromIDL: false,
nullable: isOpt,
);
fromJson.writeln('$fieldName: $deserJson,');
final deserIDL = child.deserializeAndReplace(
replace: isNumberKey ? 'obj[$idlName]' : "obj['$idlName']",
fromIDL: true,
nullable: isOpt,
);
fromDeserializable.writeln('$fieldName: $deserIDL,');
final ser = child.serialize(
fromIDL: !option.explicitSerializationMethods,
);
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) {
toSerializable.writeln('$idlName: $arg,');
toJson.writeln('$idlName: $arg,');
} else {
toSerializable.writeln("'$idlName': $arg,");
toJson.writeln("'$idlName': $arg,");
}
} else {
if (isNumberKey) {
toSerializable.writeln('if ($fieldName != null) $idlName: $arg,');
toJson.writeln('if ($fieldName != null) $idlName: $arg,');
} else {
toSerializable.writeln("if ($fieldName != null) '$idlName': $arg,");
toJson.writeln("if ($fieldName != null) '$idlName': $arg,");
}
}
}
toSerializableFields.writeln('final $fieldName = this.$fieldName;');
final toJsonField = _typeToJsonField(option, obj, e, fieldName);
toJsonFields.writeln('final $fieldName = this.$toJsonField;');
}
return Class(
(b) => b
..name = className
..annotations = ListBuilder([const CodeExpression(Code('immutable'))])
..constructors = ListBuilder([
Constructor(
(b) => b
..optionalParameters = ListBuilder(constructorParameters)
..constant = true,
),
if (option.explicitSerializationMethods)
Constructor(
(b) => b
..docs = ListBuilder([
'/// An extra method for the deserialization with `packages:agent_dart`.',
])
..name = 'fromIDLDeserializable'
..factory = true
..body =
Code('return ${obj.ctx.getClassName()}($fromDeserializable);')
..requiredParameters = ListBuilder([
Parameter(
(b) => b
..type = const Reference('Map')
..name = 'obj',
),
]),
),
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',
),
]),
),
])
..fields = ListBuilder(fields)
..methods = ListBuilder([
if (option.explicitSerializationMethods)
Method(
(b) => b
..docs = ListBuilder([
'/// An extra method for the serialization with `packages:agent_dart`.',
])
..name = 'toIDLSerializable'
..body =
Code('${toSerializableFields}return { $toSerializable };')
..returns = const Reference('Map<String, dynamic>'),
),
Method(
(b) => b
..name = 'toJson'
..body = Code('${toJsonFields}return { $toJson };')
..returns = Reference(
'${option.explicitSerializationMethods ? '' : '\n'}'
'Map<String, dynamic>',
),
),
if (option.copyWith)
Method(
(b) => b
..name = 'copyWith'
..optionalParameters = ListBuilder(copyWithParameters)
..body = Code('return $className($copyWithAssign);')
..returns = Reference(className),
),
if (option.equal) ...[
Method(
(b) => b
..name = 'operator =='
..returns = const Reference('bool')
..requiredParameters = ListBuilder([
Parameter(
(b) => b
..name = 'other'
..type = const Reference('Object'),
),
])
..annotations =
ListBuilder([const CodeExpression(Code('override'))])
..body = Code(
'return identical(this, other) || (other.runtimeType == runtimeType && other is $className ${fields.isEmpty ? '' : '&&'} ${equals.join("&&")});',
),
),
Method(
(b) => b
..type = MethodType.getter
..name = 'hashCode'
..returns = const Reference('int')
..lambda = true
..annotations =
ListBuilder([const CodeExpression(Code('override'))])
..body =
Code('Object.hashAll([runtimeType,${hashes.join(",")}])'),
),
],
toStringMethod,
])
..docs = ListBuilder(['/// [$className] defined in Candid', obj.doc]),
);
}