summaryrefslogtreecommitdiff
path: root/cmd/ponzu/generate.go
diff options
context:
space:
mode:
Diffstat (limited to 'cmd/ponzu/generate.go')
-rw-r--r--cmd/ponzu/generate.go157
1 files changed, 134 insertions, 23 deletions
diff --git a/cmd/ponzu/generate.go b/cmd/ponzu/generate.go
index d28f0a2..5211df8 100644
--- a/cmd/ponzu/generate.go
+++ b/cmd/ponzu/generate.go
@@ -11,9 +11,10 @@ import (
)
type generateType struct {
- Name string
- Initial string
- Fields []generateField
+ Name string
+ Initial string
+ Fields []generateField
+ HasReferences bool
}
type generateField struct {
@@ -22,6 +23,10 @@ type generateField struct {
TypeName string
JSONName string
View string
+
+ IsReference bool
+ ReferenceName string
+ ReferenceJSONTags []string
}
var reservedFieldNames = map[string]string{
@@ -59,7 +64,7 @@ func parseType(args []string) (generateType, error) {
fields := args[1:]
for _, field := range fields {
- f, err := parseField(field, t)
+ f, err := parseField(field, &t)
if err != nil {
return generateType{}, err
}
@@ -88,8 +93,12 @@ func parseType(args []string) (generateType, error) {
return t, nil
}
-func parseField(raw string, gt generateType) (generateField, error) {
- // contents:string or // contents:string:richtext
+func parseField(raw string, gt *generateType) (generateField, error) {
+ // contents:string
+ // contents:string:richtext
+ // author:@author,name,age
+ // authors:[]@author,name,age
+
if !strings.Contains(raw, ":") {
return generateField{}, fmt.Errorf("Invalid generate argument. [%s]", raw)
}
@@ -99,16 +108,17 @@ func parseField(raw string, gt generateType) (generateField, error) {
field := generateField{
Name: fieldName(data[0]),
Initial: gt.Initial,
- TypeName: strings.ToLower(data[1]),
JSONName: fieldJSONName(data[0]),
}
- fieldType := "input"
+ setFieldTypeName(&field, data[1], gt)
+
+ viewType := "input"
if len(data) == 3 {
- fieldType = data[2]
+ viewType = data[2]
}
- err := setFieldView(&field, fieldType)
+ err := setFieldView(&field, viewType)
if err != nil {
return generateField{}, err
}
@@ -116,6 +126,47 @@ func parseField(raw string, gt generateType) (generateField, error) {
return field, nil
}
+// parse the field's type name and check if it is a special reference type, or
+// a slice of reference types, which we'll set their underlying type to string
+// or []string respectively
+func setFieldTypeName(field *generateField, fieldType string, gt *generateType) {
+ if !strings.Contains(fieldType, "@") {
+ // not a reference, set as-is downcased
+ field.TypeName = strings.ToLower(fieldType)
+ field.IsReference = false
+ return
+ }
+
+ // some possibilities are
+ // @author,name,age
+ // []@author,name,age
+ // -------------------
+ // [] = slice of author
+ // @author = reference to Author struct
+ // ,name,age = JSON tag names from Author struct fields to use as select option display
+
+ if strings.Contains(fieldType, ",") {
+ referenceConf := strings.Split(fieldType, ",")
+ fieldType = referenceConf[0]
+ field.ReferenceJSONTags = referenceConf[1:]
+ }
+
+ var referenceType string
+ if strings.HasPrefix(fieldType, "[]") {
+ referenceType = strings.TrimPrefix(fieldType, "[]@")
+ fieldType = "[]string"
+ } else {
+ referenceType = strings.TrimPrefix(fieldType, "@")
+ fieldType = "string"
+ }
+
+ field.TypeName = strings.ToLower(fieldType)
+ field.ReferenceName = fieldName(referenceType)
+ field.IsReference = true
+ gt.HasReferences = true
+ return
+}
+
// get the initial field name passed and check it for all possible cases
// MyTitle:string myTitle:string my_title:string -> MyTitle
// error-message:string -> ErrorMessage
@@ -174,6 +225,41 @@ func fieldJSONName(name string) string {
return name
}
+func optimizeFieldView(field *generateField, viewType string) string {
+ viewType = strings.ToLower(viewType)
+
+ if field.IsReference {
+ viewType = "reference"
+ }
+
+ // if we have a []T field type, automatically make the input view a repeater
+ // as long as a repeater exists for the input type
+ repeaterElements := []string{"input", "select", "file", "reference"}
+ if strings.HasPrefix(field.TypeName, "[]") {
+ for _, el := range repeaterElements {
+ // if the viewType already is declared to be a -repeater
+ // the comparison below will fail but the switch will
+ // still find the right generator template
+ // ex. authors:"[]string":select
+ // ex. authors:string:select-repeater
+ if viewType == el {
+ viewType = viewType + "-repeater"
+ }
+ }
+ } else {
+ // if the viewType is already declared as a -repeater, but
+ // the TypeName is not of []T, add the [] prefix so the user
+ // code is correct
+ // ex. authors:string:select-repeater
+ // ex. authors:@author:select-repeater
+ if strings.HasSuffix(viewType, "-repeater") {
+ field.TypeName = "[]" + field.TypeName
+ }
+ }
+
+ return viewType
+}
+
// set the specified view inside the editor field for a generated field for a type
func setFieldView(field *generateField, viewType string) error {
var err error
@@ -186,34 +272,59 @@ func setFieldView(field *generateField, viewType string) error {
}
tmplDir := filepath.Join(pwd, "cmd", "ponzu", "templates")
- tmplFrom := func(filename string) (*template.Template, error) {
- return template.ParseFiles(filepath.Join(tmplDir, filename))
+ tmplFromWithDelims := func(filename string, delim [2]string) (*template.Template, error) {
+ if delim[0] == "" || delim[1] == "" {
+ delim = [2]string{"{{", "}}"}
+ }
+
+ return template.New(filename).Delims(delim[0], delim[1]).ParseFiles(filepath.Join(tmplDir, filename))
}
- viewType = strings.ToLower(viewType)
+ viewType = optimizeFieldView(field, viewType)
switch viewType {
case "checkbox":
- tmpl, err = tmplFrom("gen-checkbox.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-checkbox.tmpl", [2]string{})
case "custom":
- tmpl, err = tmplFrom("gen-custom.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-custom.tmpl", [2]string{})
case "file":
- tmpl, err = tmplFrom("gen-file.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-file.tmpl", [2]string{})
case "hidden":
- tmpl, err = tmplFrom("gen-hidden.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-hidden.tmpl", [2]string{})
case "input", "text":
- tmpl, err = tmplFrom("gen-input.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-input.tmpl", [2]string{})
case "richtext":
- tmpl, err = tmplFrom("gen-richtext.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-richtext.tmpl", [2]string{})
case "select":
- tmpl, err = tmplFrom("gen-select.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-select.tmpl", [2]string{})
case "textarea":
- tmpl, err = tmplFrom("gen-textarea.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-textarea.tmpl", [2]string{})
case "tags":
- tmpl, err = tmplFrom("gen-tags.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-tags.tmpl", [2]string{})
+
+ case "input-repeater":
+ tmpl, err = tmplFromWithDelims("gen-input-repeater.tmpl", [2]string{})
+ case "select-repeater":
+ tmpl, err = tmplFromWithDelims("gen-select-repeater.tmpl", [2]string{})
+ case "file-repeater":
+ tmpl, err = tmplFromWithDelims("gen-file-repeater.tmpl", [2]string{})
+
+ // use [[ and ]] as delimeters since reference views need to generate
+ // display names containing {{ and }}
+ case "reference":
+ tmpl, err = tmplFromWithDelims("gen-reference.tmpl", [2]string{"[[", "]]"})
+ if err != nil {
+ return err
+ }
+ case "reference-repeater":
+ tmpl, err = tmplFromWithDelims("gen-reference-repeater.tmpl", [2]string{"[[", "]]"})
+ if err != nil {
+ return err
+ }
+
default:
msg := fmt.Sprintf("'%s' is not a recognized view type. Using 'input' instead.", viewType)
fmt.Println(msg)
- tmpl, err = tmplFrom("gen-input.tmpl")
+ tmpl, err = tmplFromWithDelims("gen-input.tmpl", [2]string{})
}
if err != nil {