Refactored codegen

This commit is contained in:
Brandon Dyck 2019-04-22 20:23:56 -06:00
parent 500e15b5e6
commit 38d31f8a25

View File

@ -11,14 +11,6 @@ import (
"strings" "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 { func identifier(s string) string {
words := strings.Split(s, "-") words := strings.Split(s, "-")
for i, word := range words { for i, word := range words {
@ -27,84 +19,67 @@ func identifier(s string) string {
return strings.Join(words, "") return strings.Join(words, "")
} }
func fileHeader(packageName string) string { var attribTypes = map[string]string {
return fmt.Sprintf(headerFmt, packageName) "string": `// %[1]s creates a "%[2]s" attribute
}
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 { func %[1]s(value string) hatmill.Attrib {
return hatmill.Attrib{ return hatmill.Attrib{
Key: "%[2]s", Key: "%[2]s",
Value: value, Value: value,
} }
} }
` `,
boolTemplate = `// %[1]s creates a "%[2]s" attribute
"bool": `// %[1]s creates a "%[2]s" attribute
func %[1]s() hatmill.Attrib { func %[1]s() hatmill.Attrib {
return hatmill.Attrib{ return hatmill.Attrib{
Key: "%[2]s", Key: "%[2]s",
} }
} }
` `,
) }
var template string type AttribSpec struct {
switch def.Type { Name string `json:"name"`
case Bool: Type string `json:"type"`
template = boolTemplate }
case String:
template = stringTemplate type AttribSpecs []AttribSpec
default:
panic(fmt.Errorf("unknown attribute type: %v", def.Type)) 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) return fmt.Sprintf(template, identifier(def.Name), def.Name)
} }
type ElemDef struct { type ElemSpec struct {
Name string `json:"name"` Name string `json:"name"`
Void bool `json:"void"` Void bool `json:"void"`
} }
func (def ElemDef) Generate() string { func (def ElemSpec) Generate() string {
const ( const (
parentTemplate = `// %[1]s creates a <%[2]s> element. parentTemplate = `// %[1]s creates a <%[2]s> element.
func %[1]s(attribs ...hatmill.Attrib) func(children ...hatmill.Term) hatmill.ParentElement { func %[1]s(attribs ...hatmill.Attrib) func(children ...hatmill.Term) hatmill.ParentElement {
@ -136,20 +111,46 @@ func (def ElemDef) Generate() string {
return fmt.Sprintf(template, identifier(def.Name), def.Name) return fmt.Sprintf(template, identifier(def.Name), def.Name)
} }
type Defs struct { type Specs struct {
Attributes []AttribDef `json:"attributes"` Attributes AttribSpecs `json:"attributes"`
Elements []ElemDef `json:"elements"` Elements ElemSpecs `json:"elements"`
} }
func writeFormatted(fileName, packageName string, writeDefs func(*bytes.Buffer)) error { // Code is a very slight abstraction of a source code file.
src := new(bytes.Buffer) type Code struct {
src.WriteString(fileHeader(packageName)) Package string
writeDefs(src) Imports []string
formatted, err := format.Source(src.Bytes()) 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 { if err != nil {
return err return err
} }
return ioutil.WriteFile(fileName, formatted, 0644) return ioutil.WriteFile(path, b, 0644)
} }
func main() { func main() {
@ -165,26 +166,18 @@ func main() {
log.Fatal(err) log.Fatal(err)
} }
var defs Defs var specs Specs
err = json.Unmarshal(input, &defs) err = json.Unmarshal(input, &specs)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
err = writeFormatted(*attribPath, *attribPkg, func(buf *bytes.Buffer) { err = WriteCodeFile(specs.Attributes, *attribPath, *attribPkg)
for _, attribDef := range defs.Attributes {
buf.WriteString(attribDef.Generate())
}
})
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
err = writeFormatted(*elemPath, *elemPkg, func(buf *bytes.Buffer) { err = WriteCodeFile(specs.Elements, *elemPath, *elemPkg)
for _, elemDef := range defs.Elements {
buf.WriteString(elemDef.Generate())
}
})
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }