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