Skip to content

Commit

Permalink
API simplificated
Browse files Browse the repository at this point in the history
  • Loading branch information
juanvillacortac committed Mar 14, 2022
1 parent d762282 commit 689c17d
Show file tree
Hide file tree
Showing 8 changed files with 122 additions and 100 deletions.
4 changes: 3 additions & 1 deletion cmd/rosetta.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"flag"
"fmt"
"os"
"path"
"path/filepath"

"github.com/juanvillacortac/rosetta/pkg/program"
Expand Down Expand Up @@ -66,7 +67,8 @@ func run() int {
return 1
}

files, err := p.Generate(*verbose)
basePath, _ := filepath.Abs(path.Dir(*config))
files, err := p.Generate(basePath, *verbose)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
return 1
Expand Down
2 changes: 1 addition & 1 deletion examples/angular/generate.sh
Original file line number Diff line number Diff line change
@@ -1 +1 @@
go run ../..
go run ../.. -c config.yml
6 changes: 3 additions & 3 deletions examples/angular/templates/model.ts.go.tpl
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{{- range $key, $dep := .Deps -}}
import { {{ $dep }} } from './{{ $dep | ToKebabCase }}'
{{- range $key, $dep := ModelDeps .Model -}}
import { {{ $dep.Name }} } from './{{ $dep.Name | KebabCase }}'
{{ end }}
export class {{ .Model.Name }} {
{{- range .Model.Props}}
{{ .Name }}: {{ .Type }}{{ if .IsArray }}[]{{ end }}{{ if HaveDefaultValue . }} = {{ PropDefaultValue . }}{{ end }}
{{ .Name }}: {{ .Type }}{{ if .IsArray }}[]{{ end }}{{ if .DefaultValue }} = {{ .DefaultValue }}{{ end }}
{{- end }}
}
24 changes: 12 additions & 12 deletions examples/angular/templates/service.ts.go.tpl
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { {{ .Model.ModelName }} } from "../models/{{ .Model.ModelName | ToKebabCase }}";
import { {{ .Model.ModelName }}ListFilter } from "../models/{{ .Model.ModelName | ToKebabCase }}-list-filter";
import { {{ .Model.ModelName }}Filter } from "../models/{{ .Model.ModelName | ToKebabCase }}-filter";
import { {{ .Model.Name }} } from "../models/{{ .Model.Name | KebabCase }}";
import { {{ .Model.Name }}ListFilter } from "../models/{{ .Model.Name | KebabCase }}-list-filter";
import { {{ .Model.Name }}Filter } from "../models/{{ .Model.Name | KebabCase }}-filter";

@Injectable({
providedIn: "root",
})
export class {{ .Model.ModelName }}Service {
export class {{ .Model.Name }}Service {
constructor(
private _httpClient: HttpClient,
private _httpHelpersService: HttpHelpersService
) {}

get{{ .Model.ModelName }}List(filter: {{ .Model.ModelName }}ListFilter) {
return this._httpClient.get<{{ .Model.ModelName }}[]>(
`/{{ .Model.ModelName }}/list`,
get{{ .Model.Name }}List(filter: {{ .Model.Name }}ListFilter) {
return this._httpClient.get<{{ .Model.Name }}[]>(
`/{{ .Model.Name }}/list`,
{
params: this._httpHelpersService.getHttpParamsFromPlainObject(
filter,
Expand All @@ -25,9 +25,9 @@ export class {{ .Model.ModelName }}Service {
);
}

get{{ .Model.ModelName }}(filter: {{ .Model.ModelName }}Filter) {
return this._httpClient.get<{{ .Model.ModelName }}>(
`/{{ .Model.ModelName }}/`,
get{{ .Model.Name }}(filter: {{ .Model.Name }}Filter) {
return this._httpClient.get<{{ .Model.Name }}>(
`/{{ .Model.Name }}/`,
{
params: this._httpHelpersService.getHttpParamsFromPlainObject(
filter,
Expand All @@ -37,7 +37,7 @@ export class {{ .Model.ModelName }}Service {
);
}

post{{ .Model.ModelName }}(model: {{ .Model.ModelName }}) {
return this._httpClient.post<number>(`/{{ .Model.ModelName }}/`, model);
post{{ .Model.Name }}(model: {{ .Model.Name }}) {
return this._httpClient.post<number>(`/{{ .Model.Name }}/`, model);
}
}
25 changes: 15 additions & 10 deletions pkg/ast/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,33 +51,38 @@ func (m *ModelProp) Options() Options { return m.PropOptions }

type ModelMap map[string]*Model

func (models ModelMap) GetModelDeps(modelName string, deps []string) []string {
if deps == nil {
deps = make([]string, 0)
func (models ModelMap) ModelDependencies(model interface{}) []*Model {
deps := make([]*Model, 0)

var modelName string
if m, ok := model.(*Model); ok {
modelName = m.ModelName
} else if m, ok := model.(string); ok {
modelName = m
} else {
return deps
}
clone := make([]string, len(deps))
copy(clone, deps)

m, ok := models[modelName]
if !ok {
return make([]string, 0)
return make([]*Model, 0)
}

for _, p := range m.Props {
if mm, ok := models[string(p.Type)]; ok {
ok := false
for _, d := range clone {
if d == mm.ModelName {
for _, d := range deps {
if d.ModelName == mm.ModelName {
ok = true
break
}
}
if !ok {
clone = append(clone, mm.ModelName)
deps = append(deps, mm)
}
}
}
return clone
return deps
}

func GetNodeOption(r Node, optionName string) string {
Expand Down
66 changes: 30 additions & 36 deletions pkg/generators/generators.go
Original file line number Diff line number Diff line change
@@ -1,38 +1,41 @@
package generators

import (
"bytes"
"encoding/json"
"fmt"
"os"
"path"
"strings"
"text/template"

"github.com/iancoleman/strcase"
"github.com/juanvillacortac/rosetta/pkg/ast"
)

type Generator func(root *ast.RootNode) ([]OutputFile, error)

type (
TypesMap map[string]string
Definitions map[string]struct {
TypesMap map[string]string

Definitions struct {
Types map[string]string `json:"types"`
Helpers map[string]string `json:"helpers"`
}

DefinitionsMap map[string]struct {
Types map[string]string `json:"types"`
Helpers map[string]string `json:"helpers"`
}
)

type GenerateConfig struct {
Name string `json:"name" yaml:"name"`
Template string `json:"template" yaml:"template"`
Output string `json:"output" yaml:"output"`
From string `json:"from" yaml:"from"`
Types map[string]string `json:"types" yaml:"types"`
Helpers map[string]string `json:"helpers" yaml:"helpers"`
Name string `json:"name" yaml:"name"`
Template string `json:"template,omitempty" yaml:"template,omitempty"`
Output string `json:"output" yaml:"output"`

From string `json:"from" yaml:"from"`
Types map[string]string `json:"types" yaml:"types"`
Helpers map[string]string `json:"helpers" yaml:"helpers"`
}

func (g GenerateConfig) ApplyDefinitions(definitions Definitions) GenerateConfig {
func (g GenerateConfig) ApplyDefinitions(definitions DefinitionsMap) GenerateConfig {
clone := g
if g.From == "" {
return g
Expand All @@ -50,64 +53,55 @@ func (g GenerateConfig) ApplyDefinitions(definitions Definitions) GenerateConfig
return clone
}

func AdaptModel(models ast.ModelMap, typesMap TypesMap) ast.ModelMap {
func AdaptModel(models ast.ModelMap, definitions Definitions) ast.ModelMap {
clone := make(ast.ModelMap)
buff, _ := json.Marshal(models)
if err := json.Unmarshal(buff, &clone); err != nil {
panic(err)
}
for k, m := range clone {
for i, p := range m.Props {
if t, ok := typesMap[p.Type]; ok {
if t, ok := definitions.Types[p.Type]; ok {
clone[k].Props[i].Type = t
}
if p.DefaultValue != nil {
if h, ok := definitions.Helpers[*p.DefaultValue]; ok {
clone[k].Props[i].DefaultValue = &h
}
}
}
}
return clone
}

func Generate(schemaPath string, root *ast.RootNode, options GenerateConfig, verbose bool) ([]OutputFile, error) {
models := AdaptModel(root.Models, options.Types)
reader, err := os.Open(path.Join(schemaPath, options.Template))
func Generate(root *ast.RootNode, config GenerateConfig, verbose bool) ([]OutputFile, error) {
models := AdaptModel(root.Models, Definitions{
Types: config.Types,
Helpers: config.Helpers,
})
t, err := template.New(config.Name).Funcs(templateHelpers(models, config)).Parse(config.Template)
if err != nil {
err = fmt.Errorf("failed to open %s, err %v", options.Template, err)
return nil, err
}
defer reader.Close()

buffer := bytes.Buffer{}
buffer.ReadFrom(reader)
if _, err := buffer.ReadFrom(reader); err != nil {
return nil, err
}
t, err := template.New(options.Name).Funcs(templateHelpers(models, options)).Parse(buffer.String())
if err != nil {
panic(err)
}
files := make([]OutputFile, 0)
cnt := 0
for _, m := range models {
if verbose {
fmt.Fprintf(os.Stdout, "-> [%d/%d] Generating \"%s\"\n", cnt+1, len(models), m.Name())
}
deps := make([]string, 0)

deps = models.GetModelDeps(m.ModelName, deps)

writer := &strings.Builder{}
err = t.Execute(writer, &struct {
Deps []string
Root *ast.RootNode
Model *ast.Model
}{
Deps: deps,
Root: root,
Model: m,
})
if err != nil {
return nil, err
}
filename := strings.ReplaceAll(options.Output, "[model]", m.ModelName)
filename := strings.ReplaceAll(config.Output, "[model]", m.ModelName)
filename = strings.ReplaceAll(filename, "[Model]", strcase.ToCamel(m.ModelName))
filename = strings.ReplaceAll(filename, "[model_]", strcase.ToSnake(m.ModelName))
filename = strings.ReplaceAll(filename, "[model-]", strcase.ToKebab(m.ModelName))
Expand Down
34 changes: 8 additions & 26 deletions pkg/generators/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,31 +9,13 @@ import (

func templateHelpers(models ast.ModelMap, options GenerateConfig) template.FuncMap {
return template.FuncMap{
"HaveDefaultValue": func(p ast.ModelProp) bool { return p.DefaultValue != nil },
"PropDefaultValue": func(p ast.ModelProp) string {
if p.DefaultValue == nil {
return ""
}
if helper, ok := options.Helpers[*p.DefaultValue]; ok {
return helper
}
return *p.DefaultValue
},
"Models": func() ast.ModelMap { return models },
"GetModel": func(modelName string) *ast.Model { return models[modelName] },
"GetModelDeps": func(modelName string) []*ast.Model {
deps := make([]*ast.Model, 0)
depsStr := models.GetModelDeps(modelName, nil)
for _, str := range depsStr {
m := models[str]
deps = append(deps, m)
}
return deps
},
"GetNodeOption": ast.GetNodeOption,
"ToCamelCase": strcase.ToCamel,
"ToLowerCamelCase": strcase.ToLowerCamel,
"ToKebabCase": strcase.ToKebab,
"ToSnakeCase": strcase.ToSnake,
"Models": func() ast.ModelMap { return models },
"Model": func(modelName string) *ast.Model { return models[modelName] },
"ModelDeps": models.ModelDependencies,
"NodeOption": ast.GetNodeOption,
"CamelCase": strcase.ToCamel,
"LowerCamelCase": strcase.ToLowerCamel,
"KebabCase": strcase.ToKebab,
"SnakeCase": strcase.ToSnake,
}
}
61 changes: 50 additions & 11 deletions pkg/program/program.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"fmt"
"os"
"path"
"path/filepath"

"github.com/juanvillacortac/rosetta/pkg/ast"
"github.com/juanvillacortac/rosetta/pkg/generators"
Expand All @@ -17,14 +16,24 @@ import (
)

type ProgramConfig struct {
Definitions generators.Definitions `json:"definitions" yaml:"definitions"`
SchemaFile string `json:"schema" yaml:"schema"`
OutputBasePath string `json:"output" yaml:"output"`
Generators []generators.GenerateConfig `json:"generators"`
SchemaFile string `json:"schema,omitempty" yaml:"schema,omitempty"`
OutputBasePath string `json:"output,omitempty" yaml:"output,omitempty"`
Definitions generators.DefinitionsMap `json:"definitions" yaml:"definitions"`
Generators []GenerateConfig `json:"generators"`

root *ast.RootNode
}

type GenerateConfig struct {
Name string `json:"name" yaml:"name"`
Template string `json:"template" yaml:"template"`
Output string `json:"output" yaml:"output"`

From string `json:"from" yaml:"from"`
Types map[string]string `json:"types" yaml:"types"`
Helpers map[string]string `json:"helpers" yaml:"helpers"`
}

func NewProgramFromConfigFile(reader *os.File) (*ProgramConfig, error) {
buffer := bytes.Buffer{}
buffer.ReadFrom(reader)
Expand Down Expand Up @@ -80,16 +89,46 @@ func (p *ProgramConfig) Parse() error {
return nil
}

func (p *ProgramConfig) Generate(verbose bool) ([]generators.OutputFile, error) {
func (p *ProgramConfig) LoadGenerateConfigsWithTemplates(relativePath string) ([]generators.GenerateConfig, error) {
configs := make([]generators.GenerateConfig, 0)
for i := range p.Generators {
g := p.Generators[i]
reader, err := os.Open(path.Join(relativePath, g.Template))
if err != nil {
err = fmt.Errorf("failed to open %s, err %v", g.Template, err)
return nil, err
}
defer reader.Close()

buffer := bytes.Buffer{}
if _, err := buffer.ReadFrom(reader); err != nil {
return nil, err
}

gg := generators.GenerateConfig{
Name: g.Name,
Template: buffer.String(),
Output: g.Output,
From: g.From,
Types: g.Types,
Helpers: g.Helpers,
}

gApplied := gg.ApplyDefinitions(p.Definitions)
configs = append(configs, gApplied)
}
return configs, nil
}

func (p *ProgramConfig) Generate(relativePath string, verbose bool) ([]generators.OutputFile, error) {
if p.root == nil {
return nil, fmt.Errorf("schema not loaded")
}
schemaPath, _ := filepath.Abs(path.Dir(p.SchemaFile))
files := make([]generators.OutputFile, 0)
for i, g := range p.Generators {
gApplied := g.ApplyDefinitions(p.Definitions)
fmt.Fprintf(os.Stdout, "[%d/%d] %s\n", i+1, len(p.Generators), g.Name)
fs, err := generators.Generate(schemaPath, p.root, gApplied, verbose)
configs, _ := p.LoadGenerateConfigsWithTemplates(relativePath)
for i, g := range configs {
fmt.Fprintf(os.Stdout, "[%d/%d] %s\n", i+1, len(configs), g.Name)
fs, err := generators.Generate(p.root, g, verbose)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 689c17d

Please sign in to comment.