Added attribute function generator and more element defs

This commit is contained in:
Brandon Dyck 2019-03-21 21:49:26 -06:00
parent 8517cab0f5
commit 328e865231
9 changed files with 284 additions and 178 deletions

22
attribs.go Normal file
View File

@ -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,
}
}

3
attribs.txt Normal file
View File

@ -0,0 +1,3 @@
id string
disabled bool
src string

View File

@ -3,6 +3,39 @@
package hatmill 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 { func Div(attribs ...Attrib) func(children ...Term) *ParentElement {
return func(children ...Term) *ParentElement { return func(children ...Term) *ParentElement {
return &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,
}
}
}

View File

@ -1,4 +1,6 @@
html parent
head parent
body parent
div parent div parent
img empty img empty
span parent span parent
html parent

View File

@ -1,17 +1,20 @@
package main package main
import ( import (
"os" h "gitlab.codemonkeysoftware.com/b/hatmill"
h "gitlab.codemonkeysoftware.com/b/hatmill" "os"
) )
func main() { func main() {
document := h.Html()( document := h.Html()(
h.Div()( h.Head()(),
h.Img(h.Id("profile-photo")), h.Body()(
h.Text("hello hatmill!"), h.Div()(
h.Div(h.Disabled())(), h.Img(h.Src("./me.jpg"), h.Id("profile-photo")),
), h.Text("hello hatmill!"),
) h.Div(h.Disabled())(),
h.WriteDocument(os.Stdout, document) ),
),
)
h.WriteDocument(os.Stdout, document)
} }

View File

@ -3,6 +3,7 @@ package hatmill
import "io" import "io"
//go:generate go run ./internal/elementgen/main.go //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. // Term represents a fragment of HTML markup, and is one of EmptyElement, ParentElement, or Text.
type Term interface { type Term interface {
@ -108,40 +109,6 @@ func (t Text) WriteTo(w io.Writer) (n int64, err error) {
return 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. // WriteDocument writes an HTML5 doctype declaration, followed by root.
// root should probably be an <html> element. // root should probably be an <html> element.
func WriteDocument(w io.Writer, root *ParentElement) (n int64, err error) { func WriteDocument(w io.Writer, root *ParentElement) (n int64, err error) {

View File

@ -4,8 +4,8 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
"github.com/leanovate/gopter" "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{ var attribGen = gen.Struct(reflect.TypeOf(hatmill.Attrib{}), map[string]gopter.Gen{
"Key": nonEmptyAlphaString, "Key": nonEmptyAlphaString,
"Value": gen.AlphaString(), "Value": gen.AlphaString(),
}) })
func TestAttrib(t *testing.T) { func TestAttrib(t *testing.T) {
@ -75,9 +75,9 @@ func TestAttrib(t *testing.T) {
expected := fmt.Sprintf("%s='%s'", attrib.Key, attrib.Value) expected := fmt.Sprintf("%s='%s'", attrib.Key, attrib.Value)
return checkWrite(attrib, expected) return checkWrite(attrib, expected)
}, },
attribGen.SuchThat(func(attrib interface{}) bool { attribGen.SuchThat(func(attrib interface{}) bool {
return attrib.(hatmill.Attrib).Value != "" return attrib.(hatmill.Attrib).Value != ""
}), }),
)) ))
properties.TestingRun(t) properties.TestingRun(t)
@ -98,78 +98,78 @@ func TestEmptyElement(t *testing.T) {
)) ))
properties.Property("WriteTo writes element correctly with attributes", prop.ForAll( properties.Property("WriteTo writes element correctly with attributes", prop.ForAll(
func(tagName string, attribs []hatmill.Attrib) bool { func(tagName string, attribs []hatmill.Attrib) bool {
elem := hatmill.EmptyElement{ elem := hatmill.EmptyElement{
TagName: tagName, TagName: tagName,
Attribs: attribs, Attribs: attribs,
} }
var attribStrings []string var attribStrings []string
for _, attrib := range attribs { for _, attrib := range attribs {
attribStrings = append(attribStrings, writeToString(attrib)) attribStrings = append(attribStrings, writeToString(attrib))
} }
if len(attribStrings) > 0 { if len(attribStrings) > 0 {
attribStrings[0] = " " + attribStrings[0] attribStrings[0] = " " + attribStrings[0]
} }
expected := "<" + tagName + strings.Join(attribStrings, " ") + ">" expected := "<" + tagName + strings.Join(attribStrings, " ") + ">"
return checkWrite(elem, expected) return checkWrite(elem, expected)
}, },
gen.AnyString(), gen.AnyString(),
gen.SliceOf(attribGen), gen.SliceOf(attribGen),
)) ))
properties.TestingRun(t) properties.TestingRun(t)
} }
var htmlTextGen = gen.AnyString().Map(func(s string) hatmill.Term { var htmlTextGen = gen.AnyString().Map(func(s string) hatmill.Term {
return hatmill.Text(s) return hatmill.Text(s)
}) })
func TestParentElement(t *testing.T) { func TestParentElement(t *testing.T) {
properties := gopter.NewProperties(nil) properties := gopter.NewProperties(nil)
properties.Property("WriteTo writes element correctly without children", prop.ForAll( properties.Property("WriteTo writes element correctly without children", prop.ForAll(
func(tagName string, attribs []hatmill.Attrib) bool { func(tagName string, attribs []hatmill.Attrib) bool {
elem := &hatmill.ParentElement { elem := &hatmill.ParentElement{
EmptyElement: hatmill.EmptyElement{ EmptyElement: hatmill.EmptyElement{
TagName: tagName, TagName: tagName,
Attribs: attribs, Attribs: attribs,
}, },
} }
openTag := writeToString(elem.EmptyElement) openTag := writeToString(elem.EmptyElement)
expected := openTag + "</" + tagName + ">" expected := openTag + "</" + tagName + ">"
return checkWrite(elem, expected) return checkWrite(elem, expected)
}, },
gen.AnyString(), gen.AnyString(),
gen.SliceOf(attribGen), gen.SliceOf(attribGen),
)) ))
properties.Property("WriteTo writes element correctly with children", prop.ForAll( properties.Property("WriteTo writes element correctly with children", prop.ForAll(
func(tagName string, attribs []hatmill.Attrib, children []hatmill.Term) bool { func(tagName string, attribs []hatmill.Attrib, children []hatmill.Term) bool {
elem := &hatmill.ParentElement { elem := &hatmill.ParentElement{
EmptyElement: hatmill.EmptyElement{ EmptyElement: hatmill.EmptyElement{
TagName: tagName, TagName: tagName,
Attribs: attribs, Attribs: attribs,
}, },
Children: children, Children: children,
} }
openTag := writeToString(elem.EmptyElement) openTag := writeToString(elem.EmptyElement)
var childStrings []string var childStrings []string
for _, child := range children { for _, child := range children {
childStrings = append(childStrings, writeToString(child)) childStrings = append(childStrings, writeToString(child))
} }
expected := openTag + strings.Join(childStrings, "") + "</" + tagName + ">" expected := openTag + strings.Join(childStrings, "") + "</" + tagName + ">"
return checkWrite(elem, expected) return checkWrite(elem, expected)
}, },
gen.AnyString(), gen.AnyString(),
gen.SliceOf(attribGen), gen.SliceOf(attribGen),
gen.SliceOf(htmlTextGen), gen.SliceOf(htmlTextGen),
)) ))
properties.TestingRun(t) properties.TestingRun(t)
} }

View File

@ -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)
}
}

View File

@ -1,25 +1,25 @@
package main package main
import ( import (
"bufio" "bufio"
"bytes" "bytes"
"fmt" "fmt"
"go/format" "go/format"
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"strings" "strings"
) )
const ( const (
inputPath = "elements.txt" inputPath = "elements.txt"
outputPath = "elements.go" outputPath = "elements.go"
packageName = "hatmill" packageName = "hatmill"
parentType = "parent" parentType = "parent"
emptyType = "empty" 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 func(children ...Term) *ParentElement {
return &ParentElement{ return &ParentElement{
EmptyElement: EmptyElement{ EmptyElement: EmptyElement{
@ -31,7 +31,7 @@ const (
} }
} }
` `
emptyTemplate = `func %s(attribs ...Attrib) EmptyElement { emptyTemplate = `func %s(attribs ...Attrib) EmptyElement {
return EmptyElement{ return EmptyElement{
TagName: "%s", TagName: "%s",
Attribs: attribs, Attribs: attribs,
@ -41,53 +41,53 @@ const (
) )
func main() { func main() {
inputFile, err := os.Open(inputPath) inputFile, err := os.Open(inputPath)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
defer inputFile.Close() 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, "// GENERATED BY gitlab.codemonkeysoftware.net/b/hatmill/internal/elementgen")
fmt.Fprintln(&output, "// DO NOT EDIT!\n") fmt.Fprintln(&output, "// DO NOT EDIT!\n")
fmt.Fprintln(&output, "package ", packageName, "\n") fmt.Fprintln(&output, "package ", packageName, "\n")
scanner := bufio.NewScanner(inputFile) scanner := bufio.NewScanner(inputFile)
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
if line == "" { if line == "" {
continue continue
} }
var tagName, elemType string var tagName, elemType string
_, err = fmt.Sscanf(line, "%s %s", &tagName, &elemType) _, err = fmt.Sscanf(line, "%s %s", &tagName, &elemType)
if err != nil { if err != nil {
log.Fatalf("error parsing input line: %s", err) log.Fatalf("error parsing input line: %s", err)
} }
var template string var template string
switch elemType { switch elemType {
case parentType: case parentType:
template = parentTemplate template = parentTemplate
case emptyType: case emptyType:
template = emptyTemplate template = emptyTemplate
default: default:
log.Fatal("unknown element type: ", elemType) log.Fatal("unknown element type: ", elemType)
} }
fmt.Fprintf(&output, template, strings.Title(tagName), tagName) fmt.Fprintf(&output, template, strings.Title(tagName), tagName)
} }
if err := scanner.Err(); err != nil { if err := scanner.Err(); err != nil {
log.Fatalf("error scanning input: %s", err) log.Fatalf("error scanning input: %s", err)
} }
formatted, err := format.Source(output.Bytes()) formatted, err := format.Source(output.Bytes())
if err != nil { if err != nil {
log.Fatalf("error formatting output: %s", err) log.Fatalf("error formatting output: %s", err)
} }
err = ioutil.WriteFile(outputPath, formatted, 0644) err = ioutil.WriteFile(outputPath, formatted, 0644)
if err != nil { if err != nil {
log.Fatalf("error writing output: %s", err) log.Fatalf("error writing output: %s", err)
} }
} }