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"
|
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.
|
// Term represents a fragment of HTML markup, and is one of EmptyElement, ParentElement, or Text.
|
||||||
type Term interface {
|
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!
|
// DO NOT EDIT!
|
||||||
|
|
||||||
package hatmill
|
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.
|
// Body creates a <body> element.
|
||||||
func Body(attribs ...Attrib) func(children ...Term) *ParentElement {
|
func Body(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||||
return 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.
|
// Img creates a <img> element.
|
||||||
func Img(attribs ...Attrib) EmptyElement {
|
func Img(attribs ...Attrib) EmptyElement {
|
||||||
return 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.
|
// Li creates a <li> element.
|
||||||
func Li(attribs ...Attrib) func(children ...Term) *ParentElement {
|
func Li(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||||
return 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.
|
// Title creates a <title> element.
|
||||||
func Title(attribs ...Attrib) func(children ...Term) *ParentElement {
|
func Title(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||||
return 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
|
||||||
|
|
||||||
codegen reads simple descriptions of HTML elements and attributes from
|
codegen reads JSON descriptions of HTML elements and attributes, and generates
|
||||||
`elements.txt` and `attribs.txt`, respectively, and generates Go functions to
|
Go functions to create them.
|
||||||
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')
|
The `"empty"` key can be omitted from an element definition and defaults to
|
||||||
or `bool` for boolean attributes (e.g. `disabled`). Each element description is
|
`false`.
|
||||||
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').
|
|
||||||
|
@ -1,104 +1,65 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/format"
|
"go/format"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
const packageName = "hatmill"
|
const headerFmt = `// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/codegen
|
||||||
|
// DO NOT EDIT!
|
||||||
|
|
||||||
func genElements() {
|
package %s
|
||||||
const (
|
`
|
||||||
elementInputPath = "elements.txt"
|
|
||||||
elementOutputPath = "elements.go"
|
|
||||||
|
|
||||||
parentType = "parent"
|
func fileHeader(packageName string, needImport bool) string {
|
||||||
emptyType = "empty"
|
header := fmt.Sprintf(headerFmt, packageName)
|
||||||
|
if needImport {
|
||||||
parentTemplate = `// %[1]s creates a <%[2]s> element.
|
header += `import . "gitlab.codemonkeysoftware.net/b/hatmill"` + "\n"
|
||||||
func %[1]s(attribs ...Attrib) func(children ...Term) *ParentElement {
|
}
|
||||||
return func(children ...Term) *ParentElement {
|
return header
|
||||||
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 genAttribs() {
|
type AttribType int
|
||||||
const (
|
|
||||||
inputPath = "attribs.txt"
|
|
||||||
outputPath = "attribs.go"
|
|
||||||
|
|
||||||
|
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"
|
boolType = "bool"
|
||||||
stringType = "string"
|
stringType = "string"
|
||||||
|
|
||||||
@ -117,58 +78,95 @@ func genAttribs() {
|
|||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
inputFile, err := os.Open(inputPath)
|
var template string
|
||||||
if err != nil {
|
switch def.Type {
|
||||||
log.Fatal(err)
|
case Bool:
|
||||||
}
|
template = boolTemplate
|
||||||
defer inputFile.Close()
|
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")
|
type ElemDef struct {
|
||||||
fmt.Fprintln(&output, "// DO NOT EDIT!\n")
|
Name string `json:"name"`
|
||||||
fmt.Fprintln(&output, "package ", packageName, "\n")
|
Empty bool `json:"empty"`
|
||||||
|
}
|
||||||
|
|
||||||
scanner := bufio.NewScanner(inputFile)
|
func (def ElemDef) String() string {
|
||||||
for scanner.Scan() {
|
const (
|
||||||
line := scanner.Text()
|
parentTemplate = `// %[1]s creates a <%[2]s> element.
|
||||||
if line == "" {
|
func %[1]s(attribs ...Attrib) func(children ...Term) *ParentElement {
|
||||||
continue
|
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
|
template := parentTemplate
|
||||||
_, err = fmt.Sscanf(line, "%s %s", &attribName, &attribType)
|
if def.Empty {
|
||||||
if err != nil {
|
template = emptyTemplate
|
||||||
log.Fatalf("error parsing input line: %s", err)
|
}
|
||||||
}
|
return fmt.Sprintf(template, strings.Title(def.Name), def.Name)
|
||||||
|
}
|
||||||
|
|
||||||
var template string
|
type Defs struct {
|
||||||
switch attribType {
|
Attributes []AttribDef `json:"attributes"`
|
||||||
case boolType:
|
Elements []ElemDef `json:"elements"`
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
genElements()
|
inputPath := flag.String("input", "", "JSON input file")
|
||||||
genAttribs()
|
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