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 import "gitlab.codemonkeysoftware.net/b/hatmill" ` func identifier(s string) string { words := strings.Split(s, "-") for i, word := range words { words[i] = strings.Title(word) } return strings.Join(words, "") } func fileHeader(packageName string) string { return fmt.Sprintf(headerFmt, packageName) } 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() string { const ( boolType = "bool" stringType = "string" stringTemplate = `// %[1]s creates a "%[2]s" attribute func %[1]s(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "%[2]s", Value: value, } } ` boolTemplate = `// %[1]s creates a "%[2]s" attribute func %[1]s() hatmill.Attrib { return hatmill.Attrib{ Key: "%[2]s", } } ` ) 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, identifier(def.Name), def.Name) } type ElemDef struct { Name string `json:"name"` Void bool `json:"void"` } func (def ElemDef) Generate() string { const ( parentTemplate = `// %[1]s creates a <%[2]s> element. func %[1]s(attribs ...hatmill.Attrib) func(children ...hatmill.Term) hatmill.ParentElement { return func(children ...hatmill.Term) hatmill.ParentElement { return hatmill.ParentElement{ VoidElement: hatmill.VoidElement{ TagName: "%[2]s", Attribs: attribs, }, Children: children, } } } ` voidTemplate = `// %[1]s creates a <%[2]s> element. func %[1]s(attribs ...hatmill.Attrib) hatmill.VoidElement { return hatmill.VoidElement{ TagName: "%[2]s", Attribs: attribs, } } ` ) template := parentTemplate if def.Void { template = voidTemplate } return fmt.Sprintf(template, identifier(def.Name), def.Name) } type Defs struct { Attributes []AttribDef `json:"attributes"` Elements []ElemDef `json:"elements"` } func writeFormatted(fileName, packageName string, writeDefs func(*bytes.Buffer)) error { src := new(bytes.Buffer) src.WriteString(fileHeader(packageName)) writeDefs(src) formatted, err := format.Source(src.Bytes()) if err != nil { return err } return ioutil.WriteFile(fileName, formatted, 0644) } func main() { inputPath := flag.String("input", "", "JSON input file") elemPath := flag.String("elemfile", "", "generated element .go file") elemPkg := flag.String("elempkg", "", "generated element package name") attribPath := flag.String("attribfile", "", "generated attribute .go file") attribPkg := flag.String("attribpkg", "", "generated attribute package name") 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) } err = writeFormatted(*attribPath, *attribPkg, func(buf *bytes.Buffer) { for _, attribDef := range defs.Attributes { buf.WriteString(attribDef.Generate()) } }) if err != nil { log.Fatal(err) } err = writeFormatted(*elemPath, *elemPkg, func(buf *bytes.Buffer) { for _, elemDef := range defs.Elements { buf.WriteString(elemDef.Generate()) } }) if err != nil { log.Fatal(err) } }