Skip to content

Commit

Permalink
feat: prefix support (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
diegofrata authored Oct 17, 2024
1 parent 121adc7 commit 701a0f9
Show file tree
Hide file tree
Showing 12 changed files with 220 additions and 52 deletions.
22 changes: 22 additions & 0 deletions examples/prefix/prefix.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package prefix

import (
"github.com/11wizards/go-to-dart/examples/prefix/shared"
)

type KeyValuePair[TKey, TValue any] struct {
Key TKey `json:"key"`
Value TValue `json:"value"`
}

type Map[TKey, TValue any] struct {
Items []KeyValuePair[TKey, TValue] `json:"items"`
}

type Instance struct {
M Map[string, int] `json:"m"`
}

type UserRepository struct {
Users []shared.User `json:"users"`
}
86 changes: 86 additions & 0 deletions examples/prefix/prefix.go.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// ignore_for_file: always_use_package_imports
import 'package:copy_with_extension/copy_with_extension.dart';
import 'package:equatable/equatable.dart';
import 'package:json_annotation/json_annotation.dart';

part 'prefix.go.g.dart';

@CopyWith()
@JsonSerializable(explicitToJson: true)
class MyInstance extends Equatable {
final MyMap<String, int> m;

const Instance({
required this.m,
});

Map<String, dynamic> toJson() => _$MyInstanceToJson(this);

factory MyInstance.fromJson(Map<String, dynamic> json) => _$MyInstanceFromJson(json);

@override
List<Object?> get props => [
m,
];
}

@CopyWith()
@JsonSerializable(explicitToJson: true, genericArgumentFactories: true)
class MyKeyValuePair<TKey, TValue> extends Equatable {
final TKey key;
final TValue value;

const KeyValuePair({
required this.key,
required this.value,
});

Map<String, dynamic> toJson(Object Function(TKey) toJsonTKey, Object Function(TValue) toJsonTValue) => _$MyKeyValuePairToJson(this, toJsonTKey, toJsonTValue);

factory MyKeyValuePair.fromJson(Map<String, dynamic> json, TKey Function(Object? json) fromJsonTKey, TValue Function(Object? json) fromJsonTValue) => _$MyKeyValuePairFromJson(json, fromJsonTKey, fromJsonTValue);

@override
List<Object?> get props => [
key,
value,
];
}

@CopyWith()
@JsonSerializable(explicitToJson: true, genericArgumentFactories: true)
class MyMap<TKey, TValue> extends Equatable {
@JsonKey(defaultValue: <List<MyKeyValuePair<TKey, TValue>>>[])final List<MyKeyValuePair<TKey, TValue>> items;

const Map({
required this.items,
});

Map<String, dynamic> toJson(Object Function(TKey) toJsonTKey, Object Function(TValue) toJsonTValue) => _$MyMapToJson(this, toJsonTKey, toJsonTValue);

factory MyMap.fromJson(Map<String, dynamic> json, TKey Function(Object? json) fromJsonTKey, TValue Function(Object? json) fromJsonTValue) => _$MyMapFromJson(json, fromJsonTKey, fromJsonTValue);

@override
List<Object?> get props => [
items,
];
}

@CopyWith()
@JsonSerializable(explicitToJson: true)
class MyUserRepository extends Equatable {
@JsonKey(defaultValue: <List<User>>[])final List<User> users;

const UserRepository({
required this.users,
});

Map<String, dynamic> toJson() => _$MyUserRepositoryToJson(this);

factory MyUserRepository.fromJson(Map<String, dynamic> json) => _$MyUserRepositoryFromJson(json);

@override
List<Object?> get props => [
users,
];
}

6 changes: 6 additions & 0 deletions examples/prefix/shared/user.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package shared

type User struct {
Name string `json:"name"`
Age int `json:"age"`
}
10 changes: 5 additions & 5 deletions generator/class.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ func extractFields(st *types.Struct) []fieldProjection {
return fields
}

func generateFields(wr io.Writer, registry *format.TypeFormatterRegistry, mode options.Mode, fields []fieldProjection) {
func generateFields(wr io.Writer, registry *format.TypeFormatterRegistry, opts options.Options, fields []fieldProjection) {
for _, field := range fields {
generateFieldDeclaration(wr, field.field, field.tag, registry, mode)
generateFieldDeclaration(wr, field.field, field.tag, registry, opts)
fmt.Fprintln(wr, ";")
}
fmt.Fprintln(wr)
Expand Down Expand Up @@ -73,7 +73,7 @@ func generateEquatable(wr io.Writer, fields []fieldProjection) {
fmt.Fprintln(wr, "];")
}

func generateDartClass(outputFile io.Writer, ts *types.TypeName, st *types.Struct, registry *format.TypeFormatterRegistry, mode options.Mode) {
func generateDartClass(outputFile io.Writer, ts *types.TypeName, st *types.Struct, registry *format.TypeFormatterRegistry, opts options.Options) {
formatter, ok := registry.GetTypeFormatter(ts.Type()).(format.StructFormatter)
if !ok {
panic(fmt.Sprintf("expected StructFormatter, got %T", registry.GetTypeFormatter(ts.Type())))
Expand All @@ -85,14 +85,14 @@ func generateDartClass(outputFile io.Writer, ts *types.TypeName, st *types.Struc
fmt.Fprintln(outputFile, "@CopyWith()")
}
fmt.Fprintln(outputFile, formatter.Annotation(ts))
if mode == options.Firestore {
if opts.Mode == options.Firestore {
fmt.Fprintln(outputFile, "@_TimestampConverter()")
}
fmt.Fprintf(outputFile, "class %s extends Equatable {\n", formatter.Name(ts))

wr := indent.NewWriter(outputFile, "\t")

generateFields(wr, registry, mode, fields)
generateFields(wr, registry, opts, fields)
generateConstructor(wr, ts, registry, fields)
fmt.Fprint(wr, formatter.Serialization(ts))
fmt.Fprintln(wr, formatter.Deserialization(ts))
Expand Down
8 changes: 4 additions & 4 deletions generator/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ import (
"golang.org/x/exp/slices"
)

func generateFieldJSONKey(writer io.Writer, f *types.Var, tag string, registry *format.TypeFormatterRegistry, mode options.Mode) format.TypeFormatter {
func generateFieldJSONKey(writer io.Writer, f *types.Var, tag string, registry *format.TypeFormatterRegistry, opts options.Options) format.TypeFormatter {
formatter := registry.GetTypeFormatter(f.Type())
fieldName := format.GetFieldName(f)
jsonFieldName := format.GetJSONFieldName(tag, mode)
jsonFieldName := format.GetJSONFieldName(tag, opts.Mode)

keyProperties := map[string]string{}

Expand Down Expand Up @@ -53,8 +53,8 @@ func generateFieldJSONKey(writer io.Writer, f *types.Var, tag string, registry *
return formatter
}

func generateFieldDeclaration(writer io.Writer, f *types.Var, tag string, registry *format.TypeFormatterRegistry, mode options.Mode) {
formatter := generateFieldJSONKey(writer, f, tag, registry, mode)
func generateFieldDeclaration(writer io.Writer, f *types.Var, tag string, registry *format.TypeFormatterRegistry, opts options.Options) {
formatter := generateFieldJSONKey(writer, f, tag, registry, opts)
fmt.Fprintf(writer, "final %s", formatter.Declaration(format.GetFieldName(f), f.Type()))
}

Expand Down
12 changes: 8 additions & 4 deletions generator/format/concrete_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ func (f *ConcreteStructFormatter) CanFormat(expr types.Type) bool {
}

func (f *ConcreteStructFormatter) Signature(expr types.Type) string {
if f.Options.Prefix != "" && f.Registry.IsKnownNamedType(expr.(*types.Named)) {
return fmt.Sprintf("%s%s", f.Options.Prefix, expr.(*types.Named).Obj().Name())
}

return expr.(*types.Named).Obj().Name()
}

Expand All @@ -34,12 +38,12 @@ func (f *ConcreteStructFormatter) Name(expr *types.TypeName) string {
return f.Signature(expr.Type())
}

func (t *ConcreteStructFormatter) Serialization(expr *types.TypeName) string {
return fmt.Sprintf("Map<String, dynamic> toJson() => _$%sToJson(this);\n\n", expr.Name())
func (f *ConcreteStructFormatter) Serialization(expr *types.TypeName) string {
return fmt.Sprintf("Map<String, dynamic> toJson() => _$%sToJson(this);\n\n", f.Signature(expr.Type()))
}

func (t *ConcreteStructFormatter) Deserialization(expr *types.TypeName) string {
return fmt.Sprintf("factory %s.fromJson(Map<String, dynamic> json) => _$%sFromJson(json);\n", expr.Name(), expr.Name())
func (f *ConcreteStructFormatter) Deserialization(expr *types.TypeName) string {
return fmt.Sprintf("factory %s.fromJson(Map<String, dynamic> json) => _$%sFromJson(json);\n", f.Signature(expr.Type()), f.Signature(expr.Type()))
}

func (t *ConcreteStructFormatter) Annotation(expr *types.TypeName) string {
Expand Down
25 changes: 21 additions & 4 deletions generator/format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ type TypeFormatter interface {

type TypeFormatterBase struct {
Registry *TypeFormatterRegistry
Mode options.Mode
Options options.Options
}

func (t *TypeFormatterBase) SetRegistry(registry *TypeFormatterRegistry) {
Expand All @@ -42,16 +42,33 @@ type StructFormatter interface {
}

type TypeFormatterRegistry struct {
KnownTypes map[types.Type]struct{}
Formatters []TypeFormatter

knownTypes map[types.Type]struct{}
knownNamedTypes map[string]struct{}
}

func (t *TypeFormatterRegistry) AddKnownType(typ types.Type) {
t.knownTypes[typ] = struct{}{}
if namedType, ok := typ.(*types.Named); ok {
t.knownNamedTypes[namedType.Obj().Type().String()] = struct{}{}
}
}

func (t *TypeFormatterRegistry) IsKnownNamedType(namedType *types.Named) bool {
name := namedType.Obj().Type().String()
_, ok := t.knownNamedTypes[name]
return ok
}

func NewTypeFormatterRegistry() *TypeFormatterRegistry {
return &TypeFormatterRegistry{
KnownTypes: make(map[types.Type]struct{}),
Formatters: make([]TypeFormatter, 0),
Formatters: make([]TypeFormatter, 0),
knownTypes: make(map[types.Type]struct{}),
knownNamedTypes: make(map[string]struct{}),
}
}

func (t *TypeFormatterRegistry) RegisterTypeFormatter(formatter TypeFormatter) {
t.Formatters = append(t.Formatters, formatter)
formatter.SetRegistry(t)
Expand Down
24 changes: 17 additions & 7 deletions generator/format/generic_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,23 @@ func (f *GenericStructFormatter) under(expr types.Type) types.Type {
return nil
}

func (f *GenericStructFormatter) nameWithoutTypeParams(expr types.Type) string {
namedType := expr.(*types.Named)

if f.Options.Prefix != "" && f.Registry.IsKnownNamedType(namedType) {
return fmt.Sprintf("%s%s", f.Options.Prefix, namedType.Obj().Name())
}

return namedType.Obj().Name()
}

func (f *GenericStructFormatter) CanFormat(expr types.Type) bool {
return f.under(expr) != nil
}

func (f *GenericStructFormatter) Signature(expr types.Type) string {
namedType := expr.(*types.Named)
return fmt.Sprintf("%s%s", namedType.Obj().Name(), GenerateTypeParams(f.Registry, namedType))
return fmt.Sprintf("%s%s", f.nameWithoutTypeParams(expr), GenerateTypeParams(f.Registry, namedType))
}

func (f *GenericStructFormatter) Declaration(fieldName string, expr types.Type) string {
Expand All @@ -37,7 +47,7 @@ func (f *GenericStructFormatter) Name(expr *types.TypeName) string {
return f.Signature(expr.Type())
}

func (t *GenericStructFormatter) Serialization(expr *types.TypeName) string {
func (f *GenericStructFormatter) Serialization(expr *types.TypeName) string {
typeParams := expr.Type().(*types.Named).TypeParams()
buf := new(bytes.Buffer)
fmt.Fprint(buf, "Map<String, dynamic> toJson(")
Expand All @@ -52,7 +62,7 @@ func (t *GenericStructFormatter) Serialization(expr *types.TypeName) string {
fmt.Fprintf(buf, "Object Function(%s) toJson%s", tp, tp)
}

fmt.Fprintf(buf, ") => _$%sToJson(this", expr.Name())
fmt.Fprintf(buf, ") => _$%sToJson(this", f.nameWithoutTypeParams(expr.Type()))

for i := 0; i < typeParams.Len(); i++ {
tp := typeParams.At(i)
Expand All @@ -65,17 +75,17 @@ func (t *GenericStructFormatter) Serialization(expr *types.TypeName) string {
return buf.String()
}

func (t *GenericStructFormatter) Deserialization(expr *types.TypeName) string {
func (f *GenericStructFormatter) Deserialization(expr *types.TypeName) string {
typeParams := expr.Type().(*types.Named).TypeParams()
buf := new(bytes.Buffer)
fmt.Fprintf(buf, "factory %s.fromJson(Map<String, dynamic> json", expr.Name())
fmt.Fprintf(buf, "factory %s.fromJson(Map<String, dynamic> json", f.nameWithoutTypeParams(expr.Type()))

for i := 0; i < typeParams.Len(); i++ {
tp := typeParams.At(i)
fmt.Fprintf(buf, ", %s Function(Object? json) fromJson%s", tp, tp)
}

fmt.Fprintf(buf, ") => _$%sFromJson(json", expr.Name())
fmt.Fprintf(buf, ") => _$%sFromJson(json", f.nameWithoutTypeParams(expr.Type()))

for i := 0; i < typeParams.Len(); i++ {
tp := typeParams.At(i)
Expand All @@ -88,7 +98,7 @@ func (t *GenericStructFormatter) Deserialization(expr *types.TypeName) string {
return buf.String()
}

func (t *GenericStructFormatter) Annotation(expr *types.TypeName) string {
func (f *GenericStructFormatter) Annotation(expr *types.TypeName) string {
return "@JsonSerializable(explicitToJson: true, genericArgumentFactories: true)"
}

Expand Down
Loading

0 comments on commit 701a0f9

Please sign in to comment.