Slightly redesigned types

This commit is contained in:
Brandon Dyck 2019-03-21 12:40:54 -06:00
parent b402a3b581
commit 8e2910490f

View File

@ -1,4 +1,4 @@
package main package hatmill
import "fmt" import "fmt"
import "io" import "io"
@ -7,177 +7,178 @@ import "strings"
// Html is either Element or Text // Html is either Element or Text
type Html interface { type Html interface {
io.WriterTo io.WriterTo
// isHtml is a non-functional marker to differentiate Html from interface{}
isHtml() // isHtml is a no-op method to prevent external Html implementations, because
// Go doesn't have sum types.
isHtml()
} }
func Reader() io.Reader { // writeStringsTo attempts to write the strings in ss to w, incrementing n by the
// TODO in-order traversal of HTML tree to spit out bytes // number of bytes written.
return nil func writeStringsTo(w io.Writer, n *int64, ss ...string) error {
} for _, s := range ss {
nCurr, err := io.WriteString(w, s)
func String() string { *n += int64(nCurr)
panic("this could be trivially implemented with a Reader and a bytes.Buffer") if err != nil {
return err
}
}
return nil
} }
type Attrib struct { type Attrib struct {
Key string Key string
Value string Value string
}
type element struct {
tagName string
attribs []Attrib
empty bool
children []Html
}
// writeTo attempts to write the strings in ss to w, incrementing n by the
// number of bytes written.
func writeTo(w io.Writer, n *int64, ss ...string) error {
for _, s := range ss {
nCurr, err := io.WriteString(w, s)
*n += int64(nCurr)
if err != nil {
return err
}
}
return nil
} }
func (a Attrib) WriteTo(w io.Writer) (n int64, err error) { func (a Attrib) WriteTo(w io.Writer) (n int64, err error) {
err = writeTo(w, &n, a.Key) err = writeStringsTo(w, &n, a.Key)
if err != nil { if err != nil {
return return
} }
if a.Value != "" { if a.Value != "" {
err = writeTo(w, &n, "='", a.Value, "'") err = writeStringsTo(w, &n, "='", a.Value, "'")
} }
return return
} }
func (e *element) WriteTo(w io.Writer) (n int64, err error) { type EmptyElement struct {
err = writeTo(w, &n, "<", e.tagName) TagName string
if err != nil { Attribs []Attrib
return
}
for _, attrib := range e.attribs {
err = writeTo(w, &n, " ")
if err != nil {
return
}
var nCurr int64
nCurr, err = attrib.WriteTo(w)
n += int64(nCurr)
if err != nil {
return
}
}
err = writeTo(w, &n, ">")
if err != nil {
return
}
if !e.empty {
for _, child := range e.children {
var nCurr int64
nCurr, err = child.WriteTo(w)
n += int64(nCurr)
if err != nil {
return
}
}
err = writeTo(w, &n, "</", e.tagName, ">")
}
return
} }
func (*element) isHtml() {} func (EmptyElement) isHtml() {}
func emptyElement(tagName string, attribs []Attrib) Html { func (e EmptyElement) WriteTo(w io.Writer) (n int64, err error) {
return &element{ err = writeStringsTo(w, &n, "<", e.TagName)
tagName: tagName, if err != nil {
attribs: attribs, return
empty: true, }
}
for _, attrib := range e.Attribs {
err = writeStringsTo(w, &n, " ")
if err != nil {
return
}
var nCurr int64
nCurr, err = attrib.WriteTo(w)
n += int64(nCurr)
if err != nil {
return
}
}
err = writeStringsTo(w, &n, ">")
return
} }
func elementFunc(tagName string, attribs []Attrib) func(...Html) Html { type ParentElement struct {
return func(children ...Html) Html { EmptyElement
return &element{ Children []Html
tagName: tagName,
attribs: attribs,
children: children,
empty: false,
}
}
} }
type text string func (e *ParentElement) isHtml() {}
func Text(s string) Html { func (e *ParentElement) WriteTo(w io.Writer) (n int64, err error) {
return text(s) n, err = e.EmptyElement.WriteTo(w)
if err != nil {
return
}
for _, child := range e.Children {
var nCurr int64
nCurr, err = child.WriteTo(w)
n += int64(nCurr)
if err != nil {
return
}
}
err = writeStringsTo(w, &n, "</", e.TagName, ">")
return
} }
func (text) isHtml() {} type Text string
func (t text) WriteTo(w io.Writer) (n int64, err error) { func (Text) isHtml() {}
err = writeTo(w, &n, string(t))
return func (t Text) WriteTo(w io.Writer) (n int64, err error) {
err = writeStringsTo(w, &n, string(t))
return
} }
type rText struct { type RText struct {
io.Reader io.Reader
} }
func RText(r io.Reader) Html { func (RText) isHtml() {}
return rText{Reader: r}
}
func (rText) isHtml() {} func (rt RText) WriteTo(w io.Writer) (n int64, err error) {
return io.Copy(w, rt.Reader)
func (rt rText) WriteTo(w io.Writer) (n int64, err error) {
return io.Copy(w, rt.Reader)
} }
// Html5 functions // Html5 functions
func Div(attribs ...Attrib) func(children ...Html) Html { func Div(attribs ...Attrib) func(children ...Html) *ParentElement {
return elementFunc("div", attribs) return func(children ...Html) *ParentElement {
return &ParentElement{
EmptyElement: EmptyElement{
TagName: "div",
Attribs: attribs,
},
Children: children,
}
}
} }
func Img(attribs ...Attrib) Html { func Img(attribs ...Attrib) EmptyElement {
return emptyElement("img", attribs) return EmptyElement{
TagName: "img",
Attribs: attribs,
}
} }
func Id(id string) Attrib { func Id(id string) Attrib {
return Attrib{ return Attrib{
Key: "id", Key: "id",
Value: id, Value: id,
} }
} }
func Disabled() Attrib { func Disabled() Attrib {
return Attrib { return Attrib{
Key: "disabled", Key: "disabled",
} }
} }
func main() { // WriteDocument writes an HTML5 doctype declaration, followed by root.
var page = Div(Disabled(), Id("container"))( // root should probably be an <html> element.
Img(Id("profile-photo")), func WriteDocument(w io.Writer, root *ParentElement) (n int64, err error) {
Text("hello"), err = writeStringsTo(w, &n, "<!DOCTYPE html>")
Img(Disabled()), if err != nil {
RText(strings.NewReader("goodbye!")), return
) }
n, err := page.WriteTo(os.Stdout) var nroot int64
fmt.Println() nroot, err = root.WriteTo(w)
fmt.Println(n) n += nroot
fmt.Println(err) return
}
func Main() {
var page = Div(Disabled(), Id("container"))(
Img(Id("profile-photo")),
Text("hello"),
Img(Disabled()),
RText{strings.NewReader("goodbye!")},
)
n, err := WriteDocument(os.Stdout, page)
fmt.Println()
fmt.Println(n)
if err != nil {
fmt.Println(err)
}
} }