Skip to content

Commit

Permalink
Initial commit.
Browse files Browse the repository at this point in the history
  • Loading branch information
diegofrata committed Feb 15, 2023
1 parent 85daaf8 commit 62fff6e
Show file tree
Hide file tree
Showing 24 changed files with 1,024 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.idea
dist/
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2023 11Wizards
Copyright (c) 2023 11Wizards (U.K.) Limited.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
87 changes: 87 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# go-to-dart

Go-to-dart helps you convert Go structs to Dart classes that can be used with [json_serializable](https://pub.dev/packages/json_serializable).

## Features

- Supports only structs in the same package (no generics or embedded structs yet)
- Supports primitives, slices, maps, and pointers
- Support some other arbitrary types such as `time.Time` and `mo.Option` (easy to extend!)

Need something more? Please open an issue or even better, a PR!

## Installation

```bash
go install github.com/11wizards/go-to-dart
```

The above command will install go-to-dart in your `$GOPATH/bin` directory. Make sure that directory is in your `$PATH`.

## Usage

```bash
go-to-dart -i ./examples/user -o ./examples/user
```

## Example

Running the command above would take the package `./examples/user` below and generate a file `./examples/user/user.dart`.

```go
package user

import (
"time"
)

type User struct {
ID int
Name string
Email string
Password string
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
Options map[string]string
Tags []string
}
```

Contents of `./examples/user/user.dart`:
```dart
import 'package:json_annotation/json_annotation.dart';
part 'user.g.dart';
@JsonSerializable()
class User {
@JsonKey(name: 'ID') final int id;
@JsonKey(name: 'Name') final String name;
@JsonKey(name: 'Email') final String email;
@JsonKey(name: 'Password') final String password;
@JsonKey(name: 'CreatedAt') final DateTime createdAt;
@JsonKey(name: 'UpdatedAt') final DateTime updatedAt;
@JsonKey(name: 'DeletedAt') final DateTime? deletedAt;
@JsonKey(name: 'Options') final Map<String, String> options;
@JsonKey(name: 'Tags') final List<String> tags;
User({
required this.id,
required this.name,
required this.email,
required this.password,
required this.createdAt,
required this.updatedAt,
this.deletedAt,
required this.options,
required this.tags,
});
Map<String, dynamic> toJson() => _$UserToJson(this);
factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}
```
92 changes: 92 additions & 0 deletions examples/everything/everything.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import 'package:json_annotation/json_annotation.dart';

part 'everything.g.dart';

@JsonSerializable()
class Parent {
final String id;
final int number1;
final List<int> number2;
final int? number3;
final List<int>? number4;
final List<int?> number5;
final int? number6;
final List<int?> number7;
final String text1;
final List<String> text2;
final String? text3;
final List<String>? text4;
final List<String?> text5;
final String? text6;
final List<String?> text7;
final DateTime date1;
final List<DateTime> date2;
final DateTime? date3;
final List<DateTime>? date4;
final List<DateTime?> date5;
final String? date6;
final List<String?> date7;
final Child child1;
final List<Child> child2;
final Child? child3;
final List<Child>? child4;
final List<Child?> child5;
final Child? child6;
final List<Child?> child7;
final Map<String, double> map1;
@JsonKey(name: 'map2_weird_name') final Map<int, Child> map2;

Parent({
required this.id,
required this.number1,
required this.number2,
this.number3,
this.number4,
required this.number5,
this.number6,
required this.number7,
required this.text1,
required this.text2,
this.text3,
this.text4,
required this.text5,
this.text6,
required this.text7,
required this.date1,
required this.date2,
this.date3,
this.date4,
required this.date5,
this.date6,
required this.date7,
required this.child1,
required this.child2,
this.child3,
this.child4,
required this.child5,
this.child6,
required this.child7,
required this.map1,
required this.map2,
});

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

factory Parent.fromJson(Map<String, dynamic> json) => _$ParentFromJson(json);
}

@JsonSerializable()
class Child {
final int id;
final String name;

Child({
required this.id,
required this.name,
});

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

factory Child.fromJson(Map<String, dynamic> json) => _$ChildFromJson(json);
}

54 changes: 54 additions & 0 deletions examples/everything/everything.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package everything

import (
"github.com/samber/mo"
"time"
)

type ParentID string

type Parent struct {
ID ParentID `json:"id"`

Number1 int `json:"number1"`
Number2 []int `json:"number2"`
Number3 *int `json:"number3"`
Number4 *[]int `json:"number4"`
Number5 []*int `json:"number5"`
Number6 mo.Option[int] `json:"number6"`
Number7 []mo.Option[int] `json:"number7"`

Text1 string `json:"text1"`
Text2 []string `json:"text2"`
Text3 *string `json:"text3"`
Text4 *[]string `json:"text4"`
Text5 []*string `json:"text5"`
Text6 mo.Option[string] `json:"text6"`
Text7 []mo.Option[string] `json:"text7"`

Date1 time.Time `json:"date1"`
Date2 []time.Time `json:"date2"`
Date3 *time.Time `json:"date3"`
Date4 *[]time.Time `json:"date4"`
Date5 []*time.Time `json:"date5"`
Date6 mo.Option[string] `json:"date6"`
Date7 []mo.Option[string] `json:"date7"`

Child1 Child `json:"child1"`
Child2 []Child `json:"child2"`
Child3 *Child `json:"child3"`
Child4 *[]Child `json:"child4"`
Child5 []*Child `json:"child5"`
Child6 mo.Option[Child] `json:"child6"`
Child7 []mo.Option[Child] `json:"child7"`

Map1 map[string]float64 `json:"map1"`
Map2 map[ChildID]Child `json:"map2_weird_name"`
}

type ChildID int64

type Child struct {
ID ChildID `json:"id"`
Name string `json:"name"`
}
33 changes: 33 additions & 0 deletions examples/user/user.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import 'package:json_annotation/json_annotation.dart';

part 'user.g.dart';

@JsonSerializable()
class User {
@JsonKey(name: 'ID') final int id;
@JsonKey(name: 'Name') final String name;
@JsonKey(name: 'Email') final String email;
@JsonKey(name: 'Password') final String password;
@JsonKey(name: 'CreatedAt') final DateTime createdAt;
@JsonKey(name: 'UpdatedAt') final DateTime updatedAt;
@JsonKey(name: 'DeletedAt') final DateTime? deletedAt;
@JsonKey(name: 'Options') final Map<String, String> options;
@JsonKey(name: 'Tags') final List<String> tags;

User({
required this.id,
required this.name,
required this.email,
required this.password,
required this.createdAt,
required this.updatedAt,
this.deletedAt,
required this.options,
required this.tags,
});

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

factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json);
}

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

import (
"time"
)

type User struct {
ID int
Name string
Email string
Password string
CreatedAt time.Time
UpdatedAt time.Time
DeletedAt *time.Time
Options map[string]string
Tags []string
}
54 changes: 54 additions & 0 deletions generator/class.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package generator

import (
"fmt"
"github.com/openconfig/goyang/pkg/indent"
"go/ast"
"io"
)

func generateFields(wr io.Writer, st *ast.StructType) {
for _, f := range st.Fields.List {
generateFieldDeclaration(wr, f)
fmt.Fprintln(wr, ";")
}
fmt.Fprintln(wr)
}

func generateConstructor(wr io.Writer, ts *ast.TypeSpec, st *ast.StructType) {
fmt.Fprintf(wr, "%s({\n", ts.Name)

for _, f := range st.Fields.List {
generateFieldConstrutor(indent.NewWriter(wr, "\t"), f)
fmt.Fprintln(wr, ",")
}

fmt.Fprintf(wr, "});")
fmt.Fprintln(wr)
fmt.Fprintln(wr)
}

func generateSerialization(wr io.Writer, ts *ast.TypeSpec) {
fmt.Fprintf(wr, "Map<String, dynamic> toJson() => _$%sToJson(this);\n\n", ts.Name)
}

func generateDeserialization(wr io.Writer, ts *ast.TypeSpec) {
fmt.Fprintf(wr, "factory %s.fromJson(Map<String, dynamic> json) => _$%sFromJson(json);\n", ts.Name, ts.Name)
}

func generateDartClass(outputFile io.Writer, ts *ast.TypeSpec, st *ast.StructType) bool {
fmt.Fprintln(outputFile, "@JsonSerializable()")
fmt.Fprintf(outputFile, "class %s {\n", ts.Name)

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

generateFields(wr, st)
generateConstructor(wr, ts, st)
generateSerialization(wr, ts)
generateDeserialization(wr, ts)

fmt.Fprintln(outputFile, "}")
fmt.Fprintln(outputFile, "")

return false
}
27 changes: 27 additions & 0 deletions generator/field.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package generator

import (
"fmt"
"github.com/11wizards/go-to-dart/generator/format"
"go/ast"
"io"
)

func generateFieldDeclaration(writer io.Writer, f *ast.Field) {
formatter := format.GetTypeFormatter(f.Type)
fieldName := format.GetFieldName(f)
jsonFieldName := format.GetJSONFieldName(f)

if jsonFieldName != "" && jsonFieldName != fieldName {
fmt.Fprintf(writer, "@JsonKey(name: '%s') ", jsonFieldName)
} else if jsonFieldName == "" {
fmt.Fprintf(writer, "@JsonKey(name: '%s') ", f.Names[0].Name)
}

fmt.Fprintf(writer, "final %s", formatter.Declaration(format.GetFieldName(f), f.Type))
}

func generateFieldConstrutor(writer io.Writer, f *ast.Field) {
formatter := format.GetTypeFormatter(f.Type)
fmt.Fprint(writer, formatter.Constructor(format.GetFieldName(f), f.Type))
}
Loading

0 comments on commit 62fff6e

Please sign in to comment.