diff --git a/attribs.go b/attribs.go new file mode 100644 index 0000000..98952ac --- /dev/null +++ b/attribs.go @@ -0,0 +1,22 @@ +// 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, + } +} diff --git a/attribs.txt b/attribs.txt new file mode 100644 index 0000000..1185490 --- /dev/null +++ b/attribs.txt @@ -0,0 +1,3 @@ +id string +disabled bool +src string diff --git a/elements.go b/elements.go index deaa78c..84e2c86 100644 --- a/elements.go +++ b/elements.go @@ -3,6 +3,39 @@ package hatmill +func Html(attribs ...Attrib) func(children ...Term) *ParentElement { + return func(children ...Term) *ParentElement { + return &ParentElement{ + EmptyElement: EmptyElement{ + TagName: "html", + Attribs: attribs, + }, + Children: children, + } + } +} +func Head(attribs ...Attrib) func(children ...Term) *ParentElement { + return func(children ...Term) *ParentElement { + return &ParentElement{ + EmptyElement: EmptyElement{ + TagName: "head", + Attribs: attribs, + }, + Children: children, + } + } +} +func Body(attribs ...Attrib) func(children ...Term) *ParentElement { + return func(children ...Term) *ParentElement { + return &ParentElement{ + EmptyElement: EmptyElement{ + TagName: "body", + Attribs: attribs, + }, + Children: children, + } + } +} func Div(attribs ...Attrib) func(children ...Term) *ParentElement { return func(children ...Term) *ParentElement { return &ParentElement{ @@ -31,14 +64,3 @@ func Span(attribs ...Attrib) func(children ...Term) *ParentElement { } } } -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 index d69b3a8..d4fa802 100644 --- a/elements.txt +++ b/elements.txt @@ -1,4 +1,6 @@ +html parent +head parent +body parent div parent img empty span parent -html parent diff --git a/example/example.go b/example/example.go index d6a6a4e..16a3d2c 100644 --- a/example/example.go +++ b/example/example.go @@ -1,17 +1,20 @@ package main import ( - "os" - h "gitlab.codemonkeysoftware.com/b/hatmill" + h "gitlab.codemonkeysoftware.com/b/hatmill" + "os" ) 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) + document := h.Html()( + h.Head()(), + h.Body()( + h.Div()( + h.Img(h.Src("./me.jpg"), 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 0f74dff..346de3c 100644 --- a/hatmill.go +++ b/hatmill.go @@ -3,6 +3,7 @@ package hatmill import "io" //go:generate go run ./internal/elementgen/main.go +//go:generate go run ./internal/attribgen/main.go // Term represents a fragment of HTML markup, and is one of EmptyElement, ParentElement, or Text. type Term interface { @@ -108,40 +109,6 @@ func (t Text) WriteTo(w io.Writer) (n int64, err error) { return } -// Html5 functions - -// 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{ - Key: "id", - Value: id, - } -} - -func Disabled() Attrib { - return Attrib{ - Key: "disabled", - } -} - // WriteDocument writes an HTML5 doctype declaration, followed by root. // root should probably be an element. func WriteDocument(w io.Writer, root *ParentElement) (n int64, err error) { diff --git a/hatmill_test.go b/hatmill_test.go index 72fb3a5..b3c311a 100644 --- a/hatmill_test.go +++ b/hatmill_test.go @@ -4,8 +4,8 @@ import ( "bytes" "fmt" "io" - "reflect" - "strings" + "reflect" + "strings" "testing" "github.com/leanovate/gopter" @@ -53,8 +53,8 @@ var nonEmptyAlphaString = gen.AlphaString().SuchThat(func(v interface{}) bool { }) var attribGen = gen.Struct(reflect.TypeOf(hatmill.Attrib{}), map[string]gopter.Gen{ - "Key": nonEmptyAlphaString, - "Value": gen.AlphaString(), + "Key": nonEmptyAlphaString, + "Value": gen.AlphaString(), }) func TestAttrib(t *testing.T) { @@ -75,9 +75,9 @@ func TestAttrib(t *testing.T) { expected := fmt.Sprintf("%s='%s'", attrib.Key, attrib.Value) return checkWrite(attrib, expected) }, - attribGen.SuchThat(func(attrib interface{}) bool { - return attrib.(hatmill.Attrib).Value != "" - }), + attribGen.SuchThat(func(attrib interface{}) bool { + return attrib.(hatmill.Attrib).Value != "" + }), )) properties.TestingRun(t) @@ -98,78 +98,78 @@ func TestEmptyElement(t *testing.T) { )) properties.Property("WriteTo writes element correctly with attributes", prop.ForAll( - func(tagName string, attribs []hatmill.Attrib) bool { - elem := hatmill.EmptyElement{ - TagName: tagName, - Attribs: attribs, - } + func(tagName string, attribs []hatmill.Attrib) bool { + elem := hatmill.EmptyElement{ + TagName: tagName, + Attribs: attribs, + } - var attribStrings []string - for _, attrib := range attribs { - attribStrings = append(attribStrings, writeToString(attrib)) - } - if len(attribStrings) > 0 { - attribStrings[0] = " " + attribStrings[0] - } + var attribStrings []string + for _, attrib := range attribs { + attribStrings = append(attribStrings, writeToString(attrib)) + } + if len(attribStrings) > 0 { + attribStrings[0] = " " + attribStrings[0] + } - expected := "<" + tagName + strings.Join(attribStrings, " ") + ">" - return checkWrite(elem, expected) - }, - gen.AnyString(), - gen.SliceOf(attribGen), - )) + expected := "<" + tagName + strings.Join(attribStrings, " ") + ">" + return checkWrite(elem, expected) + }, + gen.AnyString(), + gen.SliceOf(attribGen), + )) - properties.TestingRun(t) + properties.TestingRun(t) } var htmlTextGen = gen.AnyString().Map(func(s string) hatmill.Term { - return hatmill.Text(s) + return hatmill.Text(s) }) func TestParentElement(t *testing.T) { - properties := gopter.NewProperties(nil) + properties := gopter.NewProperties(nil) - properties.Property("WriteTo writes element correctly without children", prop.ForAll( - func(tagName string, attribs []hatmill.Attrib) bool { - elem := &hatmill.ParentElement { - EmptyElement: hatmill.EmptyElement{ - TagName: tagName, - Attribs: attribs, - }, - } + properties.Property("WriteTo writes element correctly without children", prop.ForAll( + func(tagName string, attribs []hatmill.Attrib) bool { + elem := &hatmill.ParentElement{ + EmptyElement: hatmill.EmptyElement{ + TagName: tagName, + Attribs: attribs, + }, + } - openTag := writeToString(elem.EmptyElement) - expected := openTag + "" - return checkWrite(elem, expected) - }, - gen.AnyString(), - gen.SliceOf(attribGen), - )) + openTag := writeToString(elem.EmptyElement) + expected := openTag + "" + return checkWrite(elem, expected) + }, + gen.AnyString(), + gen.SliceOf(attribGen), + )) - properties.Property("WriteTo writes element correctly with children", prop.ForAll( - func(tagName string, attribs []hatmill.Attrib, children []hatmill.Term) bool { - elem := &hatmill.ParentElement { - EmptyElement: hatmill.EmptyElement{ - TagName: tagName, - Attribs: attribs, - }, - Children: children, - } + properties.Property("WriteTo writes element correctly with children", prop.ForAll( + func(tagName string, attribs []hatmill.Attrib, children []hatmill.Term) bool { + elem := &hatmill.ParentElement{ + EmptyElement: hatmill.EmptyElement{ + TagName: tagName, + Attribs: attribs, + }, + Children: children, + } - openTag := writeToString(elem.EmptyElement) + openTag := writeToString(elem.EmptyElement) - var childStrings []string - for _, child := range children { - childStrings = append(childStrings, writeToString(child)) - } + var childStrings []string + for _, child := range children { + childStrings = append(childStrings, writeToString(child)) + } - expected := openTag + strings.Join(childStrings, "") + "" - return checkWrite(elem, expected) - }, - gen.AnyString(), - gen.SliceOf(attribGen), - gen.SliceOf(htmlTextGen), - )) + expected := openTag + strings.Join(childStrings, "") + "" + return checkWrite(elem, expected) + }, + gen.AnyString(), + gen.SliceOf(attribGen), + gen.SliceOf(htmlTextGen), + )) - properties.TestingRun(t) + properties.TestingRun(t) } diff --git a/internal/attribgen/main.go b/internal/attribgen/main.go new file mode 100644 index 0000000..df8ca98 --- /dev/null +++ b/internal/attribgen/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "bufio" + "bytes" + "fmt" + "go/format" + "io/ioutil" + "log" + "os" + "strings" +) + +const ( + inputPath = "attribs.txt" + outputPath = "attribs.go" + packageName = "hatmill" + + boolType = "bool" + stringType = "string" + + stringTemplate = `func %s(value string) Attrib { + return Attrib{ + Key: "%s", + Value: value, + } + } + ` + boolTemplate = `func %s() Attrib { + return Attrib{ + Key: "%s", + } + } + ` +) + +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/attribgen") + 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 attribName, attribType string + _, err = fmt.Sscanf(line, "%s %s", &attribName, &attribType) + if err != nil { + log.Fatalf("error parsing input line: %s", err) + } + + 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) + } +} diff --git a/internal/elementgen/main.go b/internal/elementgen/main.go index 0b18569..a9b10dd 100644 --- a/internal/elementgen/main.go +++ b/internal/elementgen/main.go @@ -1,25 +1,25 @@ package main import ( - "bufio" - "bytes" - "fmt" - "go/format" - "io/ioutil" - "log" - "os" - "strings" + "bufio" + "bytes" + "fmt" + "go/format" + "io/ioutil" + "log" + "os" + "strings" ) const ( - inputPath = "elements.txt" - outputPath = "elements.go" - packageName = "hatmill" + inputPath = "elements.txt" + outputPath = "elements.go" + packageName = "hatmill" - parentType = "parent" - emptyType = "empty" + parentType = "parent" + emptyType = "empty" - parentTemplate = `func %s(attribs ...Attrib) func(children ...Term) *ParentElement { + parentTemplate = `func %s(attribs ...Attrib) func(children ...Term) *ParentElement { return func(children ...Term) *ParentElement { return &ParentElement{ EmptyElement: EmptyElement{ @@ -31,7 +31,7 @@ const ( } } ` - emptyTemplate = `func %s(attribs ...Attrib) EmptyElement { + emptyTemplate = `func %s(attribs ...Attrib) EmptyElement { return EmptyElement{ TagName: "%s", Attribs: attribs, @@ -41,53 +41,53 @@ const ( ) func main() { - inputFile, err := os.Open(inputPath) - if err != nil { - log.Fatal(err) - } - defer inputFile.Close() + inputFile, err := os.Open(inputPath) + if err != nil { + log.Fatal(err) + } + defer inputFile.Close() - var output bytes.Buffer + 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") + 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 - } + 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 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) - } + 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) - } + 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) - } + err = ioutil.WriteFile(outputPath, formatted, 0644) + if err != nil { + log.Fatalf("error writing output: %s", err) + } }