diff --git a/elements.go b/elements.go new file mode 100644 index 0000000..deaa78c --- /dev/null +++ b/elements.go @@ -0,0 +1,44 @@ +// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/elementgen +// DO NOT EDIT! + +package hatmill + +func Div(attribs ...Attrib) func(children ...Term) *ParentElement { + return func(children ...Term) *ParentElement { + return &ParentElement{ + EmptyElement: EmptyElement{ + TagName: "div", + Attribs: attribs, + }, + Children: children, + } + } +} +func Img(attribs ...Attrib) EmptyElement { + return EmptyElement{ + TagName: "img", + Attribs: attribs, + } +} +func Span(attribs ...Attrib) func(children ...Term) *ParentElement { + return func(children ...Term) *ParentElement { + return &ParentElement{ + EmptyElement: EmptyElement{ + TagName: "span", + Attribs: attribs, + }, + Children: children, + } + } +} +func Html(attribs ...Attrib) func(children ...Term) *ParentElement { + return func(children ...Term) *ParentElement { + return &ParentElement{ + EmptyElement: EmptyElement{ + TagName: "html", + Attribs: attribs, + }, + Children: children, + } + } +} diff --git a/elements.txt b/elements.txt new file mode 100644 index 0000000..d69b3a8 --- /dev/null +++ b/elements.txt @@ -0,0 +1,4 @@ +div parent +img empty +span parent +html parent diff --git a/example/example.go b/example/example.go new file mode 100644 index 0000000..d6a6a4e --- /dev/null +++ b/example/example.go @@ -0,0 +1,17 @@ +package main + +import ( + "os" + h "gitlab.codemonkeysoftware.com/b/hatmill" +) + +func main() { + document := h.Html()( + h.Div()( + h.Img(h.Id("profile-photo")), + h.Text("hello hatmill!"), + h.Div(h.Disabled())(), + ), + ) + h.WriteDocument(os.Stdout, document) +} diff --git a/hatmill.go b/hatmill.go index 33b48ac..0f74dff 100644 --- a/hatmill.go +++ b/hatmill.go @@ -2,11 +2,13 @@ package hatmill import "io" -// Html is one of EmptyElement, ParentElement, or Text. -type Html interface { +//go:generate go run ./internal/elementgen/main.go + +// Term represents a fragment of HTML markup, and is one of EmptyElement, ParentElement, or Text. +type Term interface { io.WriterTo - // isHtml is a no-op method to prevent external Html implementations, because + // isHtml is a no-op method to prevent external Term implementations, because // Go doesn't have sum types. isHtml() } @@ -73,7 +75,7 @@ func (e EmptyElement) WriteTo(w io.Writer) (n int64, err error) { type ParentElement struct { EmptyElement - Children []Html + Children []Term } func (e *ParentElement) isHtml() {} @@ -108,24 +110,24 @@ func (t Text) WriteTo(w io.Writer) (n int64, err error) { // Html5 functions -func Div(attribs ...Attrib) func(children ...Html) *ParentElement { - return func(children ...Html) *ParentElement { - return &ParentElement{ - EmptyElement: EmptyElement{ - TagName: "div", - Attribs: attribs, - }, - Children: children, - } - } -} - -func Img(attribs ...Attrib) EmptyElement { - return EmptyElement{ - TagName: "img", - Attribs: attribs, - } -} +// func Div(attribs ...Attrib) func(children ...Term) *ParentElement { +// return func(children ...Term) *ParentElement { +// return &ParentElement{ +// EmptyElement: EmptyElement{ +// TagName: "div", +// Attribs: attribs, +// }, +// Children: children, +// } +// } +// } +// +// func Img(attribs ...Attrib) EmptyElement { +// return EmptyElement{ +// TagName: "img", +// Attribs: attribs, +// } +// } func Id(id string) Attrib { return Attrib{ diff --git a/hatmill_test.go b/hatmill_test.go index be5098f..72fb3a5 100644 --- a/hatmill_test.go +++ b/hatmill_test.go @@ -122,7 +122,7 @@ func TestEmptyElement(t *testing.T) { properties.TestingRun(t) } -var htmlTextGen = gen.AnyString().Map(func(s string) hatmill.Html { +var htmlTextGen = gen.AnyString().Map(func(s string) hatmill.Term { return hatmill.Text(s) }) @@ -147,7 +147,7 @@ func TestParentElement(t *testing.T) { )) properties.Property("WriteTo writes element correctly with children", prop.ForAll( - func(tagName string, attribs []hatmill.Attrib, children []hatmill.Html) bool { + func(tagName string, attribs []hatmill.Attrib, children []hatmill.Term) bool { elem := &hatmill.ParentElement { EmptyElement: hatmill.EmptyElement{ TagName: tagName, diff --git a/internal/elementgen/main.go b/internal/elementgen/main.go new file mode 100644 index 0000000..0b18569 --- /dev/null +++ b/internal/elementgen/main.go @@ -0,0 +1,93 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "go/format" + "io/ioutil" + "log" + "os" + "strings" +) + +const ( + inputPath = "elements.txt" + outputPath = "elements.go" + packageName = "hatmill" + + parentType = "parent" + emptyType = "empty" + + parentTemplate = `func %s(attribs ...Attrib) func(children ...Term) *ParentElement { + return func(children ...Term) *ParentElement { + return &ParentElement{ + EmptyElement: EmptyElement{ + TagName: "%s", + Attribs: attribs, + }, + Children: children, + } + } + } + ` + emptyTemplate = `func %s(attribs ...Attrib) EmptyElement { + return EmptyElement{ + TagName: "%s", + Attribs: attribs, + } + } + ` +) + +func main() { + inputFile, err := os.Open(inputPath) + 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(outputPath, formatted, 0644) + if err != nil { + log.Fatalf("error writing output: %s", err) + } +}