Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support property names such as Type, String #20

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

*.morphy.dart
*.morphy2.dart
*.g.dart

# Flutter/Dart/Pub related
**/doc/api/
Expand Down
56 changes: 56 additions & 0 deletions example/test/ex63_property_shadow_global_type.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import 'package:morphy_annotation/morphy_annotation.dart';
import 'package:test/test.dart';

part 'ex63_property_shadow_global_type.morphy.dart';
part 'ex63_property_shadow_global_type.g.dart';

//nullable private getters and private morphy class

main() {
test("1", () {
final test = Test(
String: 1,
Object: 2,
List: 3,
Map: 4,
Never: (5, 6),
Type: (7, named: 8),
bool: [2, 'abc'],
hashObjects: [4, 5],
identical: [6, 7],
);
expect(test.String, 1);
expect(test.bool, [2, 'abc']);
expect(Test.fromJson(test.toJson()).toJson(), test.toJson());

final test2 = Test2(
PositionalFunction: (a, [b = 3, c = 5]) => a + b + c
);
final f = test2.PositionalFunction;
expect(f(5, 5), 15);
});
}

typedef Int = int;
typedef _List<E> = List<E>;
typedef IntList = List<int>;

@Morphy(generateJson: true)
abstract class $Test {
Int get String;
Int get Object;
Int get List;
Int get Map;
(Int, Int)? get Never;
(Int, {Int named}) get Type;
_List<(Int, Int)>? get int;
_List get bool;
IntList get hashObjects;
_List<Int> get identical;
}

@Morphy(generateJson: false)
abstract class $Test2 {
Int Function(Int p1, {Int n1, required Int n2})? get Function;
Int Function(Int p1, [Int p2, Int p3]) get PositionalFunction;
}
1 change: 1 addition & 0 deletions morphy/lib/morphy2Builder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Builder morphy2Builder(BuilderOptions options) => //
header: '''
// ignore_for_file: UNNECESSARY_CAST
// ignore_for_file: unused_element
// ignore_for_file: library_private_types_in_public_api
''');
1 change: 1 addition & 0 deletions morphy/lib/morphyBuilder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ Builder morphyBuilder(BuilderOptions options) => //
header: '''
// ignore_for_file: UNNECESSARY_CAST
// ignore_for_file: unused_element
// ignore_for_file: library_private_types_in_public_api
''');
21 changes: 14 additions & 7 deletions morphy/lib/src/MorphyGenerator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ class MorphyGenerator<TValueT extends MorphyX> extends GeneratorForAnnotationX<T
.map((e) => //
InterfaceWithComment(
e.element.name,
e.typeArguments.map((e) => e.toString()).toList(),
e.typeArguments.map(typeToString).toList(),
e.element.typeParameters.map((x) => x.name).toList(),
e.element.fields.map((e) => NameType(e.name, e.type.toString())).toList(),
e.element.fields.map((e) => NameType(e.name, typeToString(e.type))).toList(),
comment: e.element.documentationComment,
)) //
.toList();
Expand All @@ -72,7 +72,10 @@ class MorphyGenerator<TValueT extends MorphyX> extends GeneratorForAnnotationX<T
// });

var classGenerics = ce.typeParameters
.map((e) => NameTypeClassComment(e.name, e.bound == null ? null : e.bound.toString(), null)) //
.map((e) {
final bound = e.bound;
return NameTypeClassComment(e.name, bound == null ? null : typeToString(bound), null);
}) //
.toList();

var allFieldsDistinct = getDistinctFields(allFields, interfaces);
Expand All @@ -93,8 +96,10 @@ class MorphyGenerator<TValueT extends MorphyX> extends GeneratorForAnnotationX<T
return Interface.fromGenerics(
el.name,
el.typeParameters //
.map((TypeParameterElement x) => //
NameType(x.name, x.bound == null ? null : x.bound.toString()))
.map((TypeParameterElement x) {
final bound = x.bound;
return NameType(x.name, bound == null ? null : typeToString(bound));
})
.toList(),
getAllFields(el.allSupertypes, el).where((x) => x.name != "hashCode").toList(),
true,
Expand All @@ -110,8 +115,10 @@ class MorphyGenerator<TValueT extends MorphyX> extends GeneratorForAnnotationX<T
return Interface.fromGenerics(
e.element.name,
e.element.typeParameters //
.map((TypeParameterElement x) => //
NameType(x.name, x.bound == null ? null : x.bound.toString()))
.map((TypeParameterElement x) {
final bound = x.bound;
return NameType(x.name, bound == null ? null : typeToString(bound));
})
.toList(),
getAllFields(e.element.allSupertypes, e.element as ClassElement).where((x) => x.name != "hashCode").toList(),
);
Expand Down
22 changes: 21 additions & 1 deletion morphy/lib/src/common/GeneratorForAnnotationX.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import 'dart:async';
import 'package:analyzer/dart/element/element.dart';
// ignore: import_of_legacy_library_into_null_safe
import 'package:build/build.dart';
import 'package:morphy/src/MorphyGenerator.dart';
import 'package:morphy_annotation/morphy_annotation.dart';
// ignore: import_of_legacy_library_into_null_safe
import 'package:source_gen/source_gen.dart';
// ignore: import_of_legacy_library_into_null_safe
Expand Down Expand Up @@ -40,6 +42,21 @@ abstract class GeneratorForAnnotationX<T> extends Generator {

@override
FutureOr<String> generate(LibraryReader library, BuildStep buildStep) async {
const typedefs =
"""
typedef __String = String;
typedef __Object = Object;
// ignore: unused_element
typedef __List<E> = List<E>;
typedef __Map<K, V> = Map<K, V>;
typedef __Never = Never;
typedef __Type = Type;
typedef __int = int;
typedef __bool = bool;
const __hashObjects = hashObjects;
const __identical = identical;
"""
;
final values = Set<String>();

var classElements = library.allElements //
Expand All @@ -59,7 +76,10 @@ abstract class GeneratorForAnnotationX<T> extends Generator {
}
}

return values.join('\n\n');
return [
if (this is MorphyGenerator<Morphy> && values.isNotEmpty) typedefs,
...values
].join('\n\n');
}

/// Implement to return source code to generate for [element].
Expand Down
69 changes: 62 additions & 7 deletions morphy/lib/src/common/helpers.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// ignore: import_of_legacy_library_into_null_safe
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/nullability_suffix.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:dartx/dartx.dart';

Expand Down Expand Up @@ -28,15 +28,15 @@ MethodDetails<TMeta1> getMethodDetailsForFunctionType<TMeta1>(
FunctionTypedElement fn,
TMeta1 GetMetaData(ParameterElement parameterElement),
) {
var returnType = fn.returnType.toString();
var returnType = typeToString(fn.returnType);

var paramsPositional2 = fn.parameters.where((x) => x.isPositional);
var paramsNamed2 = fn.parameters.where((x) => x.isNamed);

var paramsPositional = paramsPositional2
.map((x) => NameTypeClassCommentData<TMeta1>(
x.name.toString(),
x.type.toString(),
typeToString(x.type),
null,
comment: x.documentationComment,
meta1: GetMetaData(x),
Expand All @@ -45,15 +45,18 @@ MethodDetails<TMeta1> getMethodDetailsForFunctionType<TMeta1>(
var paramsNamed = paramsNamed2
.map((x) => NameTypeClassCommentData<TMeta1>(
x.name.toString(),
x.type.toString(),
typeToString(x.type),
null,
comment: x.documentationComment,
meta1: GetMetaData(x),
))
.toList();

var typeParameters2 = fn.typeParameters //
.map((e) => GenericsNameType(e.name, e.bound == null ? null : e.bound.toString()))
.map((e) {
final bound = e.bound;
return GenericsNameType(e.name, bound == null ? null : typeToString(bound));
})
.toList();

return MethodDetails<TMeta1>(fn.documentationComment, fn.name ?? "", paramsPositional, paramsNamed, typeParameters2, returnType);
Expand All @@ -63,7 +66,7 @@ List<NameTypeClassComment> getAllFields(List<InterfaceType> interfaceTypes, Clas
var superTypeFields = interfaceTypes //
.where((x) => x.element.name != "Object")
.flatMap((st) => st.element.fields.map((f) => //
NameTypeClassComment(f.name, f.type.toString(), st.element.name, comment: f.getter?.documentationComment)))
NameTypeClassComment(f.name, typeToString(f.type), st.element.name, comment: f.getter?.documentationComment)))
.toList();

// if(element is ClassElement){
Expand All @@ -75,8 +78,60 @@ List<NameTypeClassComment> getAllFields(List<InterfaceType> interfaceTypes, Clas
// }

var classFields = element.fields.map((f) => //
NameTypeClassComment(f.name, f.type.toString(), element.name, comment: f.getter?.documentationComment)).toList();
NameTypeClassComment(f.name, typeToString(f.type), element.name, comment: f.getter?.documentationComment)).toList();

//distinct, will keep classFields over superTypeFields
return (classFields + superTypeFields).distinctBy((x) => x.name).toList();
}

String typeToString(DartType type) {
final alias = type.alias;
final manual = alias != null
? aliasToString(alias)
: type is FunctionType
? functionToString(type)
: type is RecordType
? recordToString(type)
: type is ParameterizedType
? genericToString(type)
: null;
final nullMarker = type.nullabilitySuffix == NullabilitySuffix.question ? '?'
: type.nullabilitySuffix == NullabilitySuffix.star ? '*'
: '';
return manual != null ? "$manual$nullMarker" : type.toString();
}

String aliasToString(InstantiatedTypeAliasElement alias) => "${alias.element.name}${alias.typeArguments.isEmpty ? '' : "<${alias.typeArguments.map(typeToString).join(', ')}>"}";

String functionToString(FunctionType type) {
final generics = type.typeFormals.isNotEmpty ? "<${
type.typeFormals
.map((param) {
final bound = param.bound;
return "${param.name}${bound == null ? "" : " = ${typeToString(bound)}"}";
})
.join(', ')
}>" : '';
final normal = type.normalParameterNames.mapIndexed(
(index, name) => "${typeToString(type.normalParameterTypes[index])} $name"
).join(', ');
final named = type.namedParameterTypes.mapEntries((entry) => "${entry.value.element!.hasRequired ? 'required ' : ''}${typeToString(entry.value)} ${entry.key}").join(', ');
final optional = type.optionalParameterNames.mapIndexed(
(index, name) => "${typeToString(type.optionalParameterTypes[index])} $name"
).join(', ');
return "${typeToString(type.returnType)} Function$generics(${
[if (normal.isNotEmpty) normal, if (named.isNotEmpty) "{$named}", if (optional.isNotEmpty) "[$optional]"].join(', ')
})";
}

String recordToString(RecordType type) {
final positional = type.positionalFields.map((e) => typeToString(e.type)).join(', ');
final named = type.namedFields.map((e) => "${typeToString(e.type)} ${e.name}").join(', ');
final trailing = type.positionalFields.length == 1 && type.namedFields.length == 0 ? ',' : '';
return "(${[if (positional.isNotEmpty) positional, if (named.isNotEmpty) "{$named}"].join(', ')}$trailing)";
}

String genericToString(ParameterizedType type) {
final arguments = type.typeArguments.isEmpty ? '' : "<${type.typeArguments.map(typeToString).join(', ')}>";
return "${type.element!.name}$arguments";
}
26 changes: 13 additions & 13 deletions morphy/lib/src/helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -191,11 +191,11 @@ String getInitialiser(List<NameType> fields) {

String getToString(List<NameType> fields, String className) {
if (fields.isEmpty) {
return """String toString() => "($className-)""";
return """__String toString() => "($className-)""";
}

var items = fields.map((e) => "${e.name}:\${${e.name}.toString()}").joinToString(separator: "|");
return """String toString() => "($className-$items)";""";
return """__String toString() => "($className-$items)";""";
}

String getHashCode(List<NameType> fields) {
Expand All @@ -204,13 +204,13 @@ String getHashCode(List<NameType> fields) {
}

var items = fields.map((e) => "${e.name}.hashCode").joinToString(separator: ", ");
return """int get hashCode => hashObjects([$items]);""";
return """__int get hashCode => __hashObjects([$items]);""";
}

String getEquals(List<NameType> fields, String className) {
var sb = StringBuffer();

sb.write("bool operator ==(Object other) => identical(this, other) || other is $className && runtimeType == other.runtimeType");
sb.write("__bool operator ==(__Object other) => __identical(this, other) || other is $className && runtimeType == other.runtimeType");

sb.writeln(fields.isEmpty ? "" : " &&");

Expand Down Expand Up @@ -391,7 +391,7 @@ String getConstructorName(String trimmedClassName, bool hasCustomConstructor) {
String generateFromJsonHeader(String className) {
var _className = "${className.replaceFirst("\$", "")}";

return "factory ${_className.replaceFirst("\$", "")}.fromJson(Map<String, dynamic> json) {";
return "factory ${_className.replaceFirst("\$", "")}.fromJson(__Map<__String, dynamic> json) {";
}

String generateFromJsonBody(String className, List<NameType> generics, List<Interface> interfaces) {
Expand Down Expand Up @@ -435,7 +435,7 @@ String generateFromJsonBody(String className, List<NameType> generics, List<Inte

String generateToJson(String className, List<NameType> generics) {
if (className.startsWith("\$\$")) {
return "Map<String, dynamic> toJson_2([Map<Type, Object? Function(Never)>? fns]);";
return "__Map<__String, dynamic> toJson_2([__Map<__Type, __Object? Function(__Never)>? fns]);";
}

var _className = "${className.replaceFirst("\$", "")}";
Expand All @@ -445,7 +445,7 @@ String generateToJson(String className, List<NameType> generics) {
.join("\n");

var toJsonParams = generics //
.map((e) => " fn_${e.name} as Object? Function(${e.name})")
.map((e) => " fn_${e.name} as __Object? Function(${e.name})")
.join(",\n");

var recordType = generics //
Expand All @@ -454,16 +454,16 @@ String generateToJson(String className, List<NameType> generics) {

var result = """
// ignore: unused_field
Map<Type, Object? Function(Never)> _fns = {};
__Map<__Type, __Object? Function(__Never)> _fns = {};

Map<String, dynamic> toJson_2([Map<Type, Object? Function(Never)>? fns]){
__Map<__String, dynamic> toJson_2([__Map<__Type, __Object? Function(__Never)>? fns]){
this._fns = fns ?? {};
return toJson();
}

Map<String, dynamic> toJson() {
__Map<__String, dynamic> toJson() {
$getGenericFn
final Map<String, dynamic> data = _\$${_className}ToJson(this,
final __Map<__String, dynamic> data = _\$${_className}ToJson(this,
$toJsonParams);
// Adding custom key-value pair
data['_className_'] = '$_className';
Expand All @@ -479,11 +479,11 @@ String createJsonSingleton(String classNameTrim, List<NameType> generics) {
if (generics.length == 0) //
return "";

var objects = generics.map((e) => "Object").join(", ");
var objects = generics.map((e) => "__Object").join(", ");

var result = """
class ${classNameTrim}_Generics_Sing {
Map<List<String>, $classNameTrim<${objects}> Function(Map<String, dynamic>)> fns = {};
__Map<__List<__String>, $classNameTrim<${objects}> Function(__Map<__String, dynamic>)> fns = {};

factory ${classNameTrim}_Generics_Sing() => _singleton;
static final ${classNameTrim}_Generics_Sing _singleton = ${classNameTrim}_Generics_Sing._internal();
Expand Down
Loading