Skip to content

Commit

Permalink
Generic Generics (#21)
Browse files Browse the repository at this point in the history
* ios: Fix bugs

* Improve support for nested generics

Fixes #14
  • Loading branch information
mickeyreiss authored Sep 18, 2018
1 parent 5c1cd24 commit 1b2dc90
Show file tree
Hide file tree
Showing 17 changed files with 370 additions and 473 deletions.
57 changes: 40 additions & 17 deletions config.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package firemodel

import (
"regexp"
"strings"

"github.com/pkg/errors"
"regexp"
)

type Schema struct {
Expand Down Expand Up @@ -64,8 +65,12 @@ func (options SchemaModelOptions) GetFirestorePath() (format string, args []stri
err = errors.Errorf(`firemodel: invalid path option "%s"`, pathTemplate)
return
}

components := strings.Split(pathTemplate, "/")
if len(components)%2 != 0 {
err = errors.Errorf(`firemodel: invalid path option (must be even number of components) "%s"`, pathTemplate)
return
}

for idx, component := range components {
if firestorePathConstantPattern.MatchString(component) {
continue
Expand Down Expand Up @@ -99,31 +104,49 @@ type SchemaField struct {
Name string
Comment string
Type SchemaFieldType
Extras *SchemaFieldExtras
}

type SchemaFieldType string
type SchemaFieldType interface {
isSchemaTypeName()
}

type SchemaEnumValue struct {
Name string
Comment string
}

type SchemaFieldExtras struct {
ReferenceTo string
ArrayOfPrimitive SchemaFieldType
ArrayOfModel string
ArrayOfEnum string
MapToPrimitive SchemaFieldType
MapToModel string
MapToEnum string
EnumType string
URL bool
File bool
}
type Boolean struct{}
type Integer struct{}
type Double struct{}
type GeoPoint struct{}
type Timestamp struct{}
type String struct{}
type Bytes struct{}
type Reference struct{ T *SchemaModel }
type Array struct{ T SchemaFieldType }
type Map struct{ T SchemaFieldType }
type Model struct{ T *SchemaModel }
type Enum struct{ T *SchemaEnum }
type URL struct{}
type File struct{}

func (t *Boolean) isSchemaTypeName() {}
func (t *Integer) isSchemaTypeName() {}
func (t *Double) isSchemaTypeName() {}
func (t *GeoPoint) isSchemaTypeName() {}
func (t *Timestamp) isSchemaTypeName() {}
func (t *String) isSchemaTypeName() {}
func (t *Bytes) isSchemaTypeName() {}
func (t *Reference) isSchemaTypeName() {}
func (t *Array) isSchemaTypeName() {}
func (t *Map) isSchemaTypeName() {}
func (t *Model) isSchemaTypeName() {}
func (t *Enum) isSchemaTypeName() {}
func (t *URL) isSchemaTypeName() {}
func (t *File) isSchemaTypeName() {}

type SchemaNestedCollection struct {
Name string
Comment string
Type string
Type *Model
}
4 changes: 3 additions & 1 deletion firemodel.example.firemodel
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ enum TestDirection {

// A Test is a test model.
model TestModel {
option firestore.path = "users/{user_id}/test_models";
option firestore.path = "users/{user_id}/test_models/{test_model_id}";
option firestore.autotimestamp = true;

// The name.
Expand All @@ -29,6 +29,7 @@ model TestModel {
array<TestDirection> directions;
array<TestModel> models;
array<reference> refs;
array<reference<TestTimestamps>> model_refs;
map meta;
map<string> meta_strs;
TestDirection direction;
Expand All @@ -39,5 +40,6 @@ model TestModel {
}

model TestTimestamps {
option firestore.path = "timestamps/{test_timestamps_id}";
option firestore.autotimestamp = true;
}
62 changes: 0 additions & 62 deletions firemodel.example.yml

This file was deleted.

15 changes: 1 addition & 14 deletions firemodel.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,9 @@
package firemodel

import (
"github.com/mickeyreiss/firemodel/internal/ast"
"io"
"github.com/go-errors/errors"
)

const (
Boolean = SchemaFieldType(ast.Boolean)
Integer = SchemaFieldType(ast.Integer)
Double = SchemaFieldType(ast.Double)
Timestamp = SchemaFieldType(ast.Timestamp)
String = SchemaFieldType(ast.String)
Bytes = SchemaFieldType(ast.Bytes)
Reference = SchemaFieldType(ast.Reference)
GeoPoint = SchemaFieldType(ast.GeoPoint)
Array = SchemaFieldType(ast.Array)
Map = SchemaFieldType(ast.Map)
"github.com/go-errors/errors"
)

type SourceCoder interface {
Expand Down
1 change: 0 additions & 1 deletion firemodel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ type testCtx struct {
}

func runTest(t *testing.T, schema *firemodel.Schema) {
t.Helper()
ctx := testCtx{
prefix: path.Join(fixturesRoot, t.Name()),
files: make(inMemoryFilesByName),
Expand Down
53 changes: 31 additions & 22 deletions internal/ast/parser.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package ast

import (
"fmt"
"io"
"regexp"
"sort"
Expand Down Expand Up @@ -127,8 +128,36 @@ type ASTField struct {
}

type ASTFieldType struct {
Base ASTType `parser:"@Ident"`
Generic ASTType `parser:"[ '<' @Ident '>' ]"`
Base ASTType `parser:"@Ident"`
Generic *ASTFieldType `parser:"[ '<' @@ '>' ]"`
}

func (ft *ASTFieldType) String() string {
if ft.Generic != nil {
return fmt.Sprintf("%s<%s>", ft.Base, ft.Generic)
} else {
return fmt.Sprintf("%s", ft.Base)
}
}

func (ft *ASTFieldType) IsPrimitive() bool {
switch ft.Base {
case String,
Integer,
Bytes,
Double,
Timestamp,
Boolean,
Reference,
GeoPoint,
Array,
Map:
return true
case collection:
panic("firemodel/schema: bug. collection should never be treated as primitive type.")
default:
return false
}
}

type ASTType string
Expand All @@ -154,23 +183,3 @@ const (
func (s ASTType) IsCollection() bool {
return s == collection
}

func (s ASTType) IsPrimitive() bool {
switch s {
case String,
Integer,
Bytes,
Double,
Timestamp,
Boolean,
Reference,
GeoPoint,
Array,
Map:
return true
case collection:
panic("firemodel/schema: bug. collection should never be treated as primitive type.")
default:
return false
}
}
62 changes: 25 additions & 37 deletions langs/go/go.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ func (m *GoModeler) fields(model *firemodel.SchemaModel) func(g *jen.Group) {
}
g.
Id(strcase.ToCamel(field.Name)).
Do(m.goType(field.Type, field.Extras)).
Do(m.goType(field.Type)).
Tag(map[string]string{"firestore": strcase.ToLowerCamel(field.Name)})
}

Expand All @@ -180,53 +180,41 @@ func (m *GoModeler) fields(model *firemodel.SchemaModel) func(g *jen.Group) {
}
}

func (m *GoModeler) goType(firetype firemodel.SchemaFieldType, extras *firemodel.SchemaFieldExtras) func(s *jen.Statement) {
switch firetype {
case firemodel.Boolean:
func (m *GoModeler) goType(firetype firemodel.SchemaFieldType) func(s *jen.Statement) {
switch firetype := firetype.(type) {
case *firemodel.Boolean:
return func(s *jen.Statement) { s.Bool() }
case firemodel.Integer:
case *firemodel.Integer:
return func(s *jen.Statement) { s.Int64() }
case firemodel.Double:
case *firemodel.Double:
return func(s *jen.Statement) { s.Float64() }
case firemodel.Timestamp:
case *firemodel.Timestamp:
return func(s *jen.Statement) { s.Qual("time", "Time") }
case firemodel.String:
if extras != nil && extras.EnumType != "" {
return func(s *jen.Statement) { s.Id(extras.EnumType) }
}
if extras != nil && extras.URL {
return func(s *jen.Statement) { s.Qual("github.com/mickeyreiss/firemodel/runtime", "URL") }
}
case *firemodel.String:
return func(s *jen.Statement) { s.String() }
case firemodel.Bytes:
case *firemodel.URL:
return func(s *jen.Statement) { s.Qual("github.com/mickeyreiss/firemodel/runtime", "URL") }
case *firemodel.Enum:
return func(s *jen.Statement) { s.Id(firetype.T.Name) }
case *firemodel.Bytes:
return func(s *jen.Statement) { s.Index().Byte() }
case firemodel.Reference:
case *firemodel.Reference:
return func(s *jen.Statement) { s.Op("*").Qual("cloud.google.com/go/firestore", "DocumentRef") }
case firemodel.GeoPoint:
case *firemodel.GeoPoint:
return func(s *jen.Statement) { s.Op("*").Qual("google.golang.org/genproto/googleapis/type/latlng", "LatLng") }
case firemodel.Array:
if extras != nil && extras.ArrayOfModel != "" {
return func(s *jen.Statement) { s.Index().Id(extras.ArrayOfModel) }
}
if extras != nil && extras.ArrayOfEnum != "" {
return func(s *jen.Statement) { s.Index().Id(extras.ArrayOfEnum) }
}
if extras != nil && extras.ArrayOfPrimitive != "" {
return func(s *jen.Statement) { s.Index().Do(m.goType(extras.ArrayOfPrimitive, nil)) }
case *firemodel.Model:
return func(s *jen.Statement) { s.Op("*").Id(firetype.T.Name) }
case *firemodel.Array:
if firetype.T != nil {
return func(s *jen.Statement) { s.Index().Do(m.goType(firetype.T)) }
}
return func(s *jen.Statement) { s.Index().Interface() }
case firemodel.Map:
if extras != nil && extras.File {
return func(s *jen.Statement) { s.Op("*").Qual("github.com/mickeyreiss/firemodel/runtime", "File") }
}
if extras != nil && extras.MapToModel != "" {
return func(s *jen.Statement) { s.Op("*").Id(extras.MapToModel) }
} else if extras != nil && extras.MapToEnum != "" {
return func(s *jen.Statement) { s.Op("*").Id(extras.MapToEnum) }
} else if extras != nil && extras.MapToPrimitive != "" {
return func(s *jen.Statement) { s.Map(jen.String()).Do(m.goType(extras.MapToPrimitive, nil)) }
case *firemodel.File:
return func(s *jen.Statement) { s.Op("*").Qual("github.com/mickeyreiss/firemodel/runtime", "File") }
case *firemodel.Map:
if firetype.T != nil {
return func(s *jen.Statement) { s.Map(jen.String()).Do(m.goType(firetype.T)) }
}

return func(s *jen.Statement) { s.Map(jen.String()).Interface() }
default:
err := errors.Errorf("firemodel/go: unknown type %s", firetype)
Expand Down
Loading

0 comments on commit 1b2dc90

Please sign in to comment.