diff --git a/attribute/accesskey.go b/attribute/accesskey.go index 6522994..a1bcede 100644 --- a/attribute/accesskey.go +++ b/attribute/accesskey.go @@ -5,6 +5,6 @@ import "gitlab.codemonkeysoftware.net/b/hatmill" func Accesskey(value rune) hatmill.Attrib { return hatmill.Attrib{ Key: "accesskey", - Value: string([]rune{value}), + Value: String([]rune{value}), } } diff --git a/attribute/coords.go b/attribute/coords.go index ddec182..6acfff1 100644 --- a/attribute/coords.go +++ b/attribute/coords.go @@ -14,6 +14,6 @@ func Coords(values ...float32) hatmill.Attrib { } return hatmill.Attrib{ Key: "coords", - Value: strings.Join(s, ","), + Value: String(strings.Join(s, ",")), } } diff --git a/attribute/customdata.go b/attribute/customdata.go index ee7c542..119660b 100644 --- a/attribute/customdata.go +++ b/attribute/customdata.go @@ -6,6 +6,6 @@ import "gitlab.codemonkeysoftware.net/b/hatmill" func CustomData(suffix, value string) hatmill.Attrib { return hatmill.Attrib{ Key: "data-" + suffix, - Value: value, + Value: String(value), } } diff --git a/attribute/generated.go b/attribute/generated.go index 4323078..892284a 100644 --- a/attribute/generated.go +++ b/attribute/generated.go @@ -11,7 +11,7 @@ import "strconv" func Accept(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "accept", - Value: strings.Join(value, ","), + Value: String(strings.Join(value, ",")), } } @@ -19,7 +19,7 @@ func Accept(value ...string) hatmill.Attrib { func AcceptCharset(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "accept-charset", - Value: strings.Join(value, " "), + Value: String(strings.Join(value, " ")), } } @@ -27,7 +27,7 @@ func AcceptCharset(value ...string) hatmill.Attrib { func Action(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "action", - Value: value, + Value: String(value), } } @@ -35,7 +35,7 @@ func Action(value string) hatmill.Attrib { func Alt(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "alt", - Value: value, + Value: String(value), } } @@ -50,7 +50,7 @@ func Async() hatmill.Attrib { func Autocapitalize(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "autocapitalize", - Value: value, + Value: String(value), } } @@ -58,7 +58,7 @@ func Autocapitalize(value string) hatmill.Attrib { func Autocomplete(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "autocomplete", - Value: value, + Value: String(value), } } @@ -80,7 +80,7 @@ func Autoplay() hatmill.Attrib { func Charset(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "charset", - Value: value, + Value: String(value), } } @@ -95,7 +95,7 @@ func Checked() hatmill.Attrib { func Cite(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "cite", - Value: value, + Value: String(value), } } @@ -103,7 +103,7 @@ func Cite(value string) hatmill.Attrib { func Class(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "class", - Value: strings.Join(value, " "), + Value: String(strings.Join(value, " ")), } } @@ -111,7 +111,7 @@ func Class(value ...string) hatmill.Attrib { func Cols(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "cols", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -119,7 +119,7 @@ func Cols(value int) hatmill.Attrib { func Colspan(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "colspan", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -127,7 +127,7 @@ func Colspan(value int) hatmill.Attrib { func Content(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "content", - Value: value, + Value: String(value), } } @@ -135,7 +135,7 @@ func Content(value string) hatmill.Attrib { func Contenteditable(value bool) hatmill.Attrib { return hatmill.Attrib{ Key: "contenteditable", - Value: strconv.FormatBool(value), + Value: String(strconv.FormatBool(value)), } } @@ -143,7 +143,7 @@ func Contenteditable(value bool) hatmill.Attrib { func Contextmenu(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "contextmenu", - Value: value, + Value: String(value), } } @@ -158,7 +158,7 @@ func Controls() hatmill.Attrib { func Crossorigin(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "crossorigin", - Value: value, + Value: String(value), } } @@ -166,7 +166,7 @@ func Crossorigin(value string) hatmill.Attrib { func Datetime(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "datetime", - Value: value, + Value: String(value), } } @@ -174,7 +174,7 @@ func Datetime(value string) hatmill.Attrib { func Decoding(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "decoding", - Value: value, + Value: String(value), } } @@ -196,7 +196,7 @@ func Defer() hatmill.Attrib { func Dir(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "dir", - Value: value, + Value: String(value), } } @@ -204,7 +204,7 @@ func Dir(value string) hatmill.Attrib { func Dirname(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "dirname", - Value: value, + Value: String(value), } } @@ -219,7 +219,7 @@ func Disabled() hatmill.Attrib { func Download(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "download", - Value: value, + Value: String(value), } } @@ -227,7 +227,7 @@ func Download(value string) hatmill.Attrib { func Draggable(value bool) hatmill.Attrib { return hatmill.Attrib{ Key: "draggable", - Value: strconv.FormatBool(value), + Value: String(strconv.FormatBool(value)), } } @@ -235,7 +235,7 @@ func Draggable(value bool) hatmill.Attrib { func Enctype(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "enctype", - Value: value, + Value: String(value), } } @@ -243,7 +243,7 @@ func Enctype(value string) hatmill.Attrib { func For(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "for", - Value: strings.Join(value, " "), + Value: String(strings.Join(value, " ")), } } @@ -251,7 +251,7 @@ func For(value ...string) hatmill.Attrib { func Form(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "form", - Value: value, + Value: String(value), } } @@ -259,7 +259,7 @@ func Form(value string) hatmill.Attrib { func Formaction(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "formaction", - Value: value, + Value: String(value), } } @@ -267,7 +267,7 @@ func Formaction(value string) hatmill.Attrib { func Formmethod(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "formmethod", - Value: value, + Value: String(value), } } @@ -275,7 +275,7 @@ func Formmethod(value string) hatmill.Attrib { func Headers(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "headers", - Value: strings.Join(value, " "), + Value: String(strings.Join(value, " ")), } } @@ -283,7 +283,7 @@ func Headers(value ...string) hatmill.Attrib { func Height(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "height", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -298,7 +298,7 @@ func Hidden() hatmill.Attrib { func High(value float32) hatmill.Attrib { return hatmill.Attrib{ Key: "high", - Value: strconv.FormatFloat(float64(value), 'G', -1, 32), + Value: String(strconv.FormatFloat(float64(value), 'G', -1, 32)), } } @@ -306,7 +306,7 @@ func High(value float32) hatmill.Attrib { func Href(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "href", - Value: value, + Value: String(value), } } @@ -314,7 +314,7 @@ func Href(value string) hatmill.Attrib { func Hreflang(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "hreflang", - Value: value, + Value: String(value), } } @@ -322,7 +322,7 @@ func Hreflang(value string) hatmill.Attrib { func HttpEquiv(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "http-equiv", - Value: value, + Value: String(value), } } @@ -330,7 +330,7 @@ func HttpEquiv(value string) hatmill.Attrib { func Id(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "id", - Value: value, + Value: String(value), } } @@ -345,7 +345,7 @@ func Ismap() hatmill.Attrib { func Itemprop(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "itemprop", - Value: value, + Value: String(value), } } @@ -353,7 +353,7 @@ func Itemprop(value string) hatmill.Attrib { func Kind(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "kind", - Value: value, + Value: String(value), } } @@ -361,7 +361,7 @@ func Kind(value string) hatmill.Attrib { func Label(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "label", - Value: value, + Value: String(value), } } @@ -369,7 +369,7 @@ func Label(value string) hatmill.Attrib { func Lang(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "lang", - Value: value, + Value: String(value), } } @@ -377,7 +377,7 @@ func Lang(value string) hatmill.Attrib { func List(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "list", - Value: value, + Value: String(value), } } @@ -392,7 +392,7 @@ func Loop() hatmill.Attrib { func Low(value float32) hatmill.Attrib { return hatmill.Attrib{ Key: "low", - Value: strconv.FormatFloat(float64(value), 'G', -1, 32), + Value: String(strconv.FormatFloat(float64(value), 'G', -1, 32)), } } @@ -400,7 +400,7 @@ func Low(value float32) hatmill.Attrib { func Max(value float32) hatmill.Attrib { return hatmill.Attrib{ Key: "max", - Value: strconv.FormatFloat(float64(value), 'G', -1, 32), + Value: String(strconv.FormatFloat(float64(value), 'G', -1, 32)), } } @@ -408,7 +408,7 @@ func Max(value float32) hatmill.Attrib { func Maxlength(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "maxlength", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -416,7 +416,7 @@ func Maxlength(value int) hatmill.Attrib { func Media(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "media", - Value: value, + Value: String(value), } } @@ -424,7 +424,7 @@ func Media(value string) hatmill.Attrib { func Method(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "method", - Value: value, + Value: String(value), } } @@ -432,7 +432,7 @@ func Method(value string) hatmill.Attrib { func Min(value float32) hatmill.Attrib { return hatmill.Attrib{ Key: "min", - Value: strconv.FormatFloat(float64(value), 'G', -1, 32), + Value: String(strconv.FormatFloat(float64(value), 'G', -1, 32)), } } @@ -440,7 +440,7 @@ func Min(value float32) hatmill.Attrib { func Minlength(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "minlength", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -462,7 +462,7 @@ func Muted() hatmill.Attrib { func Name(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "name", - Value: value, + Value: String(value), } } @@ -484,7 +484,7 @@ func Open() hatmill.Attrib { func Optimum(value float32) hatmill.Attrib { return hatmill.Attrib{ Key: "optimum", - Value: strconv.FormatFloat(float64(value), 'G', -1, 32), + Value: String(strconv.FormatFloat(float64(value), 'G', -1, 32)), } } @@ -492,7 +492,7 @@ func Optimum(value float32) hatmill.Attrib { func Pattern(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "pattern", - Value: value, + Value: String(value), } } @@ -500,7 +500,7 @@ func Pattern(value string) hatmill.Attrib { func Ping(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "ping", - Value: strings.Join(value, " "), + Value: String(strings.Join(value, " ")), } } @@ -508,7 +508,7 @@ func Ping(value ...string) hatmill.Attrib { func Placeholder(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "placeholder", - Value: value, + Value: String(value), } } @@ -516,7 +516,7 @@ func Placeholder(value string) hatmill.Attrib { func Poster(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "poster", - Value: value, + Value: String(value), } } @@ -524,7 +524,7 @@ func Poster(value string) hatmill.Attrib { func Preload(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "preload", - Value: value, + Value: String(value), } } @@ -539,7 +539,7 @@ func Readonly() hatmill.Attrib { func Referrerpolicy(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "referrerpolicy", - Value: value, + Value: String(value), } } @@ -547,7 +547,7 @@ func Referrerpolicy(value string) hatmill.Attrib { func Rel(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "rel", - Value: strings.Join(value, " "), + Value: String(strings.Join(value, " ")), } } @@ -569,7 +569,7 @@ func Reversed() hatmill.Attrib { func Rows(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "rows", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -577,7 +577,7 @@ func Rows(value int) hatmill.Attrib { func Rowspan(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "rowspan", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -585,7 +585,7 @@ func Rowspan(value int) hatmill.Attrib { func Sandbox(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "sandbox", - Value: strings.Join(value, " "), + Value: String(strings.Join(value, " ")), } } @@ -593,7 +593,7 @@ func Sandbox(value ...string) hatmill.Attrib { func Scope(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "scope", - Value: value, + Value: String(value), } } @@ -608,7 +608,7 @@ func Selected() hatmill.Attrib { func Shape(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "shape", - Value: value, + Value: String(value), } } @@ -616,7 +616,7 @@ func Shape(value string) hatmill.Attrib { func Size(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "size", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -624,7 +624,7 @@ func Size(value int) hatmill.Attrib { func Sizes(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "sizes", - Value: value, + Value: String(value), } } @@ -632,7 +632,7 @@ func Sizes(value string) hatmill.Attrib { func Span(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "span", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -640,7 +640,7 @@ func Span(value int) hatmill.Attrib { func Spellcheck(value bool) hatmill.Attrib { return hatmill.Attrib{ Key: "spellcheck", - Value: strconv.FormatBool(value), + Value: String(strconv.FormatBool(value)), } } @@ -648,7 +648,7 @@ func Spellcheck(value bool) hatmill.Attrib { func Src(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "src", - Value: value, + Value: String(value), } } @@ -656,7 +656,7 @@ func Src(value string) hatmill.Attrib { func Srcdoc(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "srcdoc", - Value: value, + Value: String(value), } } @@ -664,7 +664,7 @@ func Srcdoc(value string) hatmill.Attrib { func Srclang(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "srclang", - Value: value, + Value: String(value), } } @@ -672,7 +672,7 @@ func Srclang(value string) hatmill.Attrib { func Srcset(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "srcset", - Value: strings.Join(value, ","), + Value: String(strings.Join(value, ",")), } } @@ -680,7 +680,7 @@ func Srcset(value ...string) hatmill.Attrib { func Start(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "start", - Value: value, + Value: String(value), } } @@ -688,7 +688,7 @@ func Start(value string) hatmill.Attrib { func Style(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "style", - Value: value, + Value: String(value), } } @@ -696,7 +696,7 @@ func Style(value string) hatmill.Attrib { func Tabindex(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "tabindex", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -704,7 +704,7 @@ func Tabindex(value int) hatmill.Attrib { func Target(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "target", - Value: value, + Value: String(value), } } @@ -712,7 +712,7 @@ func Target(value string) hatmill.Attrib { func Title(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "title", - Value: value, + Value: String(value), } } @@ -720,7 +720,7 @@ func Title(value string) hatmill.Attrib { func Type(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "type", - Value: value, + Value: String(value), } } @@ -728,7 +728,7 @@ func Type(value string) hatmill.Attrib { func Usemap(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "usemap", - Value: value, + Value: String(value), } } @@ -736,7 +736,7 @@ func Usemap(value string) hatmill.Attrib { func Value(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "value", - Value: value, + Value: String(value), } } @@ -744,7 +744,7 @@ func Value(value string) hatmill.Attrib { func Width(value int) hatmill.Attrib { return hatmill.Attrib{ Key: "width", - Value: strconv.FormatInt(int64(value), 10), + Value: String(strconv.FormatInt(int64(value), 10)), } } @@ -752,6 +752,6 @@ func Width(value int) hatmill.Attrib { func Wrap(value string) hatmill.Attrib { return hatmill.Attrib{ Key: "wrap", - Value: value, + Value: String(value), } } diff --git a/attribute/generated_test.go b/attribute/generated_test.go index cb17874..bf7b096 100644 --- a/attribute/generated_test.go +++ b/attribute/generated_test.go @@ -9,8 +9,8 @@ import ( func testAttribValue(t *testing.T, attrib hatmill.Attrib, expectedValue string) { t.Helper() - if attrib.Value != expectedValue { - t.Fatalf("expected attribute value to be %#v, but got %#v", expectedValue, attrib.Value) + if attrib.ValueString() != expectedValue { + t.Fatalf("expected attribute value to be %#v, but got %#v", expectedValue, attrib.ValueString()) } } diff --git a/attribute/step.go b/attribute/step.go index 87e2d8c..9109510 100644 --- a/attribute/step.go +++ b/attribute/step.go @@ -17,9 +17,9 @@ func StepFloat(value float32) StepValue { func Step(value StepValue) hatmill.Attrib { var attr = hatmill.Attrib{Key: "step"} if value == nil { - attr.Value = "any" + attr.Value = String("any") } else { - attr.Value = strconv.FormatFloat(float64(*value), 'G', -1, 32) + attr.Value = String(strconv.FormatFloat(float64(*value), 'G', -1, 32)) } return attr } diff --git a/attribute/string.go b/attribute/string.go new file mode 100644 index 0000000..8eb46fe --- /dev/null +++ b/attribute/string.go @@ -0,0 +1,7 @@ +package attribute + +type String string + +func (s String) String() string { + return string(s) +} diff --git a/hatmill.go b/hatmill.go index c7df3cb..9b4e396 100644 --- a/hatmill.go +++ b/hatmill.go @@ -3,6 +3,7 @@ package hatmill //go:generate go run internal/codegen/codegen.go -input defs.json -elemfile element/generated.go -elempkg element -attribfile attribute/generated.go -attribpkg attribute import ( "bytes" + "fmt" "html" "io" ) @@ -32,7 +33,14 @@ func writeStringsTo(w io.Writer, n *int64, ss ...string) error { // Attrib represents an HTML attribute. type Attrib struct { Key string - Value string + Value fmt.Stringer +} + +func (a Attrib) ValueString() string { + if a.Value == nil { + return "" + } + return html.EscapeString(a.Value.String()) } // WriteTo writes a to w as an HTML attribute in the form key="value", or @@ -44,9 +52,8 @@ func (a Attrib) WriteTo(w io.Writer) (n int64, err error) { if err != nil { return } - if a.Value != "" { - escaped := html.EscapeString(a.Value) - err = writeStringsTo(w, &n, "='", escaped, "'") + if a.ValueString() != "" { + err = writeStringsTo(w, &n, "='", a.ValueString(), "'") } return } diff --git a/hatmill_test.go b/hatmill_test.go index cd80fd9..d394c1f 100644 --- a/hatmill_test.go +++ b/hatmill_test.go @@ -70,10 +70,18 @@ func stringNotEmpty(v interface{}) bool { var nonEmptyAlphaString = gen.AlphaString().SuchThat(stringNotEmpty) +type valueString string + +func (s valueString) String() string { + return string(s) +} + func attribGen(value gopter.Gen) gopter.Gen { return gen.Struct(reflect.TypeOf(hatmill.Attrib{}), map[string]gopter.Gen{ - "Key": nonEmptyAlphaString, - "Value": value, + "Key": nonEmptyAlphaString, + "Value": value.Map(func(v string) fmt.Stringer { + return valueString(v) + }), }) } @@ -90,9 +98,9 @@ func TestAttrib(t *testing.T) { nonEmptyAlphaString, )) - properties.Property("writes key=value with escaped value when value is not empty", prop.ForAll( + properties.Property("writes key=value with ValueString() when value is not empty", prop.ForAll( func(attrib hatmill.Attrib) bool { - expected := fmt.Sprintf("%s='%s'", attrib.Key, html.EscapeString(attrib.Value)) + expected := fmt.Sprintf("%s='%s'", attrib.Key, attrib.ValueString()) return checkWrite(attrib, expected) }, attribGen(dangerousASCII.SuchThat(stringNotEmpty)), diff --git a/internal/codegen/codegen.go b/internal/codegen/codegen.go index cf0114b..80081f7 100644 --- a/internal/codegen/codegen.go +++ b/internal/codegen/codegen.go @@ -29,7 +29,7 @@ func simpleTemplate(paramType, convertExpr string) string { return fmt.Sprintf(`func %%s(value %s) hatmill.Attrib { return hatmill.Attrib{ Key: "%%s", - Value: %s, + Value: String(%s), } } `, paramType, conversion) @@ -39,7 +39,7 @@ func listTemplate(separator string) string { return fmt.Sprintf(`func %%s(value ...string) hatmill.Attrib { return hatmill.Attrib{ Key: "%%s", - Value: strings.Join(value, "%s"), + Value: String(strings.Join(value, "%s")), } } `, separator)