Use unified JSON file for codegen input
This commit is contained in:
parent
6664713f32
commit
e5b056c1c1
22
attribs.go
22
attribs.go
@ -1,22 +0,0 @@
|
||||
// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/attribgen
|
||||
// DO NOT EDIT!
|
||||
|
||||
package hatmill
|
||||
|
||||
func Id(value string) Attrib {
|
||||
return Attrib{
|
||||
Key: "id",
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
func Disabled() Attrib {
|
||||
return Attrib{
|
||||
Key: "disabled",
|
||||
}
|
||||
}
|
||||
func Src(value string) Attrib {
|
||||
return Attrib{
|
||||
Key: "src",
|
||||
Value: value,
|
||||
}
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
id string
|
||||
disabled bool
|
||||
src string
|
@ -1,9 +0,0 @@
|
||||
html parent
|
||||
head parent
|
||||
body parent
|
||||
div parent
|
||||
img empty
|
||||
span parent
|
||||
ul parent
|
||||
li parent
|
||||
title parent
|
@ -2,7 +2,7 @@ package hatmill
|
||||
|
||||
import "io"
|
||||
|
||||
//go:generate go run ./internal/codegen/codegen.go
|
||||
//go:generate go run ./internal/codegen/codegen.go -input htmldefs.json -output htmldefs.go -package hatmill
|
||||
|
||||
// Term represents a fragment of HTML markup, and is one of EmptyElement, ParentElement, or Text.
|
||||
type Term interface {
|
||||
|
@ -1,34 +1,8 @@
|
||||
// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/elementgen
|
||||
// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/codegen
|
||||
// DO NOT EDIT!
|
||||
|
||||
package hatmill
|
||||
|
||||
// Html creates a <html> element.
|
||||
func Html(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "html",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Head creates a <head> element.
|
||||
func Head(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "head",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Body creates a <body> element.
|
||||
func Body(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
@ -55,6 +29,32 @@ func Div(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
}
|
||||
}
|
||||
|
||||
// Head creates a <head> element.
|
||||
func Head(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "head",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Html creates a <html> element.
|
||||
func Html(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "html",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Img creates a <img> element.
|
||||
func Img(attribs ...Attrib) EmptyElement {
|
||||
return EmptyElement{
|
||||
@ -63,32 +63,6 @@ func Img(attribs ...Attrib) EmptyElement {
|
||||
}
|
||||
}
|
||||
|
||||
// Span creates a <span> element.
|
||||
func Span(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "span",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ul creates a <ul> element.
|
||||
func Ul(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "ul",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Li creates a <li> element.
|
||||
func Li(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
@ -102,6 +76,19 @@ func Li(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
}
|
||||
}
|
||||
|
||||
// Span creates a <span> element.
|
||||
func Span(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "span",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Title creates a <title> element.
|
||||
func Title(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
@ -114,3 +101,33 @@ func Title(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ul creates a <ul> element.
|
||||
func Ul(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "ul",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
func Disabled() Attrib {
|
||||
return Attrib{
|
||||
Key: "disabled",
|
||||
}
|
||||
}
|
||||
func Id(value string) Attrib {
|
||||
return Attrib{
|
||||
Key: "id",
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
func Src(value string) Attrib {
|
||||
return Attrib{
|
||||
Key: "src",
|
||||
Value: value,
|
||||
}
|
||||
}
|
18
htmldefs.json
Normal file
18
htmldefs.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"attributes": [
|
||||
{"name": "disabled", "type": "bool"},
|
||||
{"name": "id", "type": "string"},
|
||||
{"name": "src", "type": "string"}
|
||||
],
|
||||
"elements": [
|
||||
{"name": "body"},
|
||||
{"name": "div"},
|
||||
{"name": "head"},
|
||||
{"name": "html"},
|
||||
{"name": "img", "empty": true},
|
||||
{"name": "li"},
|
||||
{"name": "span"},
|
||||
{"name": "title"},
|
||||
{"name": "ul"}
|
||||
]
|
||||
}
|
@ -1,21 +1,23 @@
|
||||
# codegen
|
||||
|
||||
codegen reads simple descriptions of HTML elements and attributes from
|
||||
`elements.txt` and `attribs.txt`, respectively, and generates Go functions to
|
||||
create them.
|
||||
codegen reads JSON descriptions of HTML elements and attributes, and generates
|
||||
Go functions to create them.
|
||||
|
||||
The input file consists of a JSON object like the following, which includes all
|
||||
options for attribute types and element emptiness:
|
||||
|
||||
The files contains one description per line. Each attribute description is in
|
||||
the form
|
||||
```
|
||||
attribute-name attribute-type
|
||||
{
|
||||
"elements": [
|
||||
{"name": "div"},
|
||||
{"name": "img", "empty": true},
|
||||
],
|
||||
"attributes": [
|
||||
{"name": "src", "type": "string"},
|
||||
{"name": "disabled", "type": "bool"}
|
||||
]
|
||||
}
|
||||
```
|
||||
where `attribute-name` is the lowercase name of the attribute, and
|
||||
`attribute-type` is either `string` for normal attributes (e.g. `id`, 'style')
|
||||
or `bool` for boolean attributes (e.g. `disabled`). Each element description is
|
||||
in the form
|
||||
```
|
||||
tag-name element-type
|
||||
```
|
||||
where `tag-name` is the lowercase name of the element, and `element-type` is
|
||||
either `empty` for empty elements (e.g. `img`, 'br') or `parent` for elements
|
||||
that can have child nodes (e.g. 'div', 'body').
|
||||
|
||||
The `"empty"` key can be omitted from an element definition and defaults to
|
||||
`false`.
|
||||
|
@ -1,104 +1,65 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"go/format"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const packageName = "hatmill"
|
||||
const headerFmt = `// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/codegen
|
||||
// DO NOT EDIT!
|
||||
|
||||
func genElements() {
|
||||
const (
|
||||
elementInputPath = "elements.txt"
|
||||
elementOutputPath = "elements.go"
|
||||
package %s
|
||||
`
|
||||
|
||||
parentType = "parent"
|
||||
emptyType = "empty"
|
||||
|
||||
parentTemplate = `// %[1]s creates a <%[2]s> element.
|
||||
func %[1]s(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "%[2]s",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
emptyTemplate = `// %[1]s creates a <%[2]s> element.
|
||||
func %[1]s(attribs ...Attrib) EmptyElement {
|
||||
return EmptyElement{
|
||||
TagName: "%[2]s",
|
||||
Attribs: attribs,
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
inputFile, err := os.Open(elementInputPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer inputFile.Close()
|
||||
|
||||
var output bytes.Buffer
|
||||
|
||||
fmt.Fprintln(&output, "// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/elementgen")
|
||||
fmt.Fprintln(&output, "// DO NOT EDIT!\n")
|
||||
fmt.Fprintln(&output, "package ", packageName, "\n")
|
||||
|
||||
scanner := bufio.NewScanner(inputFile)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
var tagName, elemType string
|
||||
_, err = fmt.Sscanf(line, "%s %s", &tagName, &elemType)
|
||||
if err != nil {
|
||||
log.Fatalf("error parsing input line: %s", err)
|
||||
}
|
||||
|
||||
var template string
|
||||
switch elemType {
|
||||
case parentType:
|
||||
template = parentTemplate
|
||||
case emptyType:
|
||||
template = emptyTemplate
|
||||
default:
|
||||
log.Fatal("unknown element type: ", elemType)
|
||||
}
|
||||
fmt.Fprintf(&output, template, strings.Title(tagName), tagName)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatalf("error scanning input: %s", err)
|
||||
}
|
||||
|
||||
formatted, err := format.Source(output.Bytes())
|
||||
if err != nil {
|
||||
log.Fatalf("error formatting output: %s", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(elementOutputPath, formatted, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("error writing output: %s", err)
|
||||
}
|
||||
func fileHeader(packageName string, needImport bool) string {
|
||||
header := fmt.Sprintf(headerFmt, packageName)
|
||||
if needImport {
|
||||
header += `import . "gitlab.codemonkeysoftware.net/b/hatmill"` + "\n"
|
||||
}
|
||||
return header
|
||||
}
|
||||
|
||||
func genAttribs() {
|
||||
const (
|
||||
inputPath = "attribs.txt"
|
||||
outputPath = "attribs.go"
|
||||
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) String() string {
|
||||
const (
|
||||
boolType = "bool"
|
||||
stringType = "string"
|
||||
|
||||
@ -117,58 +78,95 @@ func genAttribs() {
|
||||
`
|
||||
)
|
||||
|
||||
inputFile, err := os.Open(inputPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer inputFile.Close()
|
||||
var template string
|
||||
switch def.Type {
|
||||
case Bool:
|
||||
template = boolTemplate
|
||||
case String:
|
||||
template = stringTemplate
|
||||
default:
|
||||
panic(fmt.Errorf("unknown attribute type: %s", def.Type))
|
||||
}
|
||||
|
||||
var output bytes.Buffer
|
||||
return fmt.Sprintf(template, strings.Title(def.Name), def.Name)
|
||||
}
|
||||
|
||||
fmt.Fprintln(&output, "// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/attribgen")
|
||||
fmt.Fprintln(&output, "// DO NOT EDIT!\n")
|
||||
fmt.Fprintln(&output, "package ", packageName, "\n")
|
||||
type ElemDef struct {
|
||||
Name string `json:"name"`
|
||||
Empty bool `json:"empty"`
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(inputFile)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
func (def ElemDef) String() string {
|
||||
const (
|
||||
parentTemplate = `// %[1]s creates a <%[2]s> element.
|
||||
func %[1]s(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||
return func(children ...Term) *ParentElement {
|
||||
return &ParentElement{
|
||||
EmptyElement: EmptyElement{
|
||||
TagName: "%[2]s",
|
||||
Attribs: attribs,
|
||||
},
|
||||
Children: children,
|
||||
}
|
||||
}
|
||||
}
|
||||
`
|
||||
emptyTemplate = `// %[1]s creates a <%[2]s> element.
|
||||
func %[1]s(attribs ...Attrib) EmptyElement {
|
||||
return EmptyElement{
|
||||
TagName: "%[2]s",
|
||||
Attribs: attribs,
|
||||
}
|
||||
}
|
||||
`
|
||||
)
|
||||
|
||||
var attribName, attribType string
|
||||
_, err = fmt.Sscanf(line, "%s %s", &attribName, &attribType)
|
||||
if err != nil {
|
||||
log.Fatalf("error parsing input line: %s", err)
|
||||
}
|
||||
template := parentTemplate
|
||||
if def.Empty {
|
||||
template = emptyTemplate
|
||||
}
|
||||
return fmt.Sprintf(template, strings.Title(def.Name), def.Name)
|
||||
}
|
||||
|
||||
var template string
|
||||
switch attribType {
|
||||
case boolType:
|
||||
template = boolTemplate
|
||||
case stringType:
|
||||
template = stringTemplate
|
||||
default:
|
||||
log.Fatal("unknown attribute type: ", attribType)
|
||||
}
|
||||
fmt.Fprintf(&output, template, strings.Title(attribName), attribName)
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
log.Fatalf("error scanning input: %s", err)
|
||||
}
|
||||
|
||||
formatted, err := format.Source(output.Bytes())
|
||||
if err != nil {
|
||||
log.Fatalf("error formatting output: %s", err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(outputPath, formatted, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("error writing output: %s", err)
|
||||
}
|
||||
type Defs struct {
|
||||
Attributes []AttribDef `json:"attributes"`
|
||||
Elements []ElemDef `json:"elements"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
genElements()
|
||||
genAttribs()
|
||||
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.String())
|
||||
}
|
||||
for _, attribDef := range defs.Attributes {
|
||||
output.WriteString(attribDef.String())
|
||||
}
|
||||
|
||||
formatted, err := format.Source(output.Bytes())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
err = ioutil.WriteFile(*outputPath, formatted, 0644)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user