package main import ( "bytes" "encoding/json" "flag" "fmt" "go/format" "io/ioutil" "log" "strings" ) func identifier(s string) string { words := strings.Split(s, "-") for i, word := range words { words[i] = strings.Title(word) } return strings.Join(words, "") } var attribTypes = map[string]string { "string": `// %[1]s creates a "%[2]s" attribute func %[1]s(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "%[2]s", Value: value, } } `, "bool": `// %[1]s creates a "%[2]s" attribute func %[1]s() hatmill.Attrib { return hatmill.Attrib{ Key: "%[2]s", } } `, } type AttribSpec struct { Name string `json:"name"` Type string `json:"type"` } type AttribSpecs []AttribSpec func (a AttribSpecs) Code() Code { var c Code const hatmillImport = "gitlab.codemonkeysoftware.net/b/hatmill" c.Imports = append(c.Imports, hatmillImport) for _, spec := range a { c.Defs = append(c.Defs, spec.Generate()) } return c } type ElemSpecs []ElemSpec func (e ElemSpecs) Code() Code { var c Code const hatmillImport = "gitlab.codemonkeysoftware.net/b/hatmill" c.Imports = append(c.Imports, hatmillImport) for _, spec := range e { c.Defs = append(c.Defs, spec.Generate()) } return c } func (def AttribSpec) Generate() string { template := attribTypes[def.Type] return fmt.Sprintf(template, identifier(def.Name), def.Name) } type ElemSpec struct { Name string `json:"name"` Void bool `json:"void"` } func (def ElemSpec) 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 Specs struct { Attributes AttribSpecs `json:"attributes"` Elements ElemSpecs `json:"elements"` } // Code is a very slight abstraction of a source code file. type Code struct { Package string Imports []string Defs []string } func (c Code) Bytes() ([]byte, error) { buf := new(bytes.Buffer) buf.WriteString(`// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/codegen // DO NOT EDIT! `) fmt.Fprintln(buf, "package ", c.Package) for _, imp := range c.Imports { fmt.Fprintf(buf, "import \"%s\"\n", imp) } for _, def := range c.Defs { buf.WriteString(def) } return format.Source(buf.Bytes()) } type Coder interface { Code() Code } func WriteCodeFile(c Coder, path, pkg string) error { code := c.Code() code.Package = pkg b, err := code.Bytes() if err != nil { return err } return ioutil.WriteFile(path, b, 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 specs Specs err = json.Unmarshal(input, &specs) if err != nil { log.Fatal(err) } err = WriteCodeFile(specs.Attributes, *attribPath, *attribPkg) if err != nil { log.Fatal(err) } err = WriteCodeFile(specs.Elements, *elemPath, *elemPkg) if err != nil { log.Fatal(err) } }