package main import ( "bytes" "encoding/json" "flag" "fmt" "go/format" "io/ioutil" "log" "strings" ) const headerFmt = `// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/codegen // DO NOT EDIT! package %s ` func fileHeader(packageName string, needImport bool) string { header := fmt.Sprintf(headerFmt, packageName) if needImport { header += `import "gitlab.codemonkeysoftware.net/b/hatmill"` + "\n" } return header } type AttribType int const ( String AttribType = iota Bool ) func (t *AttribType) UnmarshalJSON(data []byte) error { if string(data) == "null" { return nil } var typeName string err := json.Unmarshal(data, &typeName) if err != nil { return fmt.Errorf("type property must be a string") } switch typeName { case "bool": *t = Bool case "string": *t = String default: return fmt.Errorf("unrecognized attribute type %s", typeName) } return nil } type AttribDef struct { Name string `json:"name"` Type AttribType `json:"type"` } func (def AttribDef) Generate(qualified bool) string { const ( boolType = "bool" stringType = "string" stringTemplate = `// %[1]s creates a "%[2]s" attribute func %[1]s(value string) %[3]sAttrib { return %[3]sAttrib{ Key: "%[2]s", Value: value, } } ` boolTemplate = `// %[1]s creates a "%[2]s" attribute func %[1]s() %[3]sAttrib { return %[3]sAttrib{ Key: "%[2]s", } } ` ) var pkg string if qualified { pkg = "hatmill." } var template string switch def.Type { case Bool: template = boolTemplate case String: template = stringTemplate default: panic(fmt.Errorf("unknown attribute type: %v", def.Type)) } return fmt.Sprintf(template, strings.Title(def.Name), def.Name, pkg) } type ElemDef struct { Name string `json:"name"` Void bool `json:"void"` } func (def ElemDef) Generate(qualified bool) string { const ( parentTemplate = `// %[1]s creates a <%[2]s> element. func %[1]s(attribs ...%[3]sAttrib) func(children ...%[3]sTerm) %[3]sParentElement { return func(children ...%[3]sTerm) %[3]sParentElement { return %[3]sParentElement{ VoidElement: %[3]sVoidElement{ TagName: "%[2]s", Attribs: attribs, }, Children: children, } } } ` voidTemplate = `// %[1]s creates a <%[2]s> element. func %[1]s(attribs ...%[3]sAttrib) %[3]sVoidElement { return %[3]sVoidElement{ TagName: "%[2]s", Attribs: attribs, } } ` ) var pkg string if qualified { pkg = "hatmill." } template := parentTemplate if def.Void { template = voidTemplate } return fmt.Sprintf(template, strings.Title(def.Name), def.Name, pkg) } type Defs struct { Attributes []AttribDef `json:"attributes"` Elements []ElemDef `json:"elements"` } func main() { inputPath := flag.String("input", "", "JSON input file") outputPath := flag.String("output", "", ".go output file") packageName := flag.String("package", "", "output package name") addImport := flag.Bool("import", false, "import hatmill in output package") flag.Parse() input, err := ioutil.ReadFile(*inputPath) if err != nil { log.Fatal(err) } var defs Defs err = json.Unmarshal(input, &defs) if err != nil { log.Fatal(err) } output := new(bytes.Buffer) output.WriteString(fileHeader(*packageName, *addImport)) for _, elemDef := range defs.Elements { output.WriteString(elemDef.Generate(*addImport)) } for _, attribDef := range defs.Attributes { output.WriteString(attribDef.Generate(*addImport)) } formatted, err := format.Source(output.Bytes()) if err != nil { log.Fatal(err) } err = ioutil.WriteFile(*outputPath, formatted, 0644) if err != nil { log.Fatal(err) } }