Added attribute function generator and more element defs
This commit is contained in:
parent
8517cab0f5
commit
328e865231
22
attribs.go
Normal file
22
attribs.go
Normal 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
3
attribs.txt
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
id string
|
||||||
|
disabled bool
|
||||||
|
src string
|
44
elements.go
44
elements.go
@ -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,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
html parent
|
||||||
|
head parent
|
||||||
|
body parent
|
||||||
div parent
|
div parent
|
||||||
img empty
|
img empty
|
||||||
span parent
|
span parent
|
||||||
html parent
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
35
hatmill.go
35
hatmill.go
@ -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) {
|
||||||
|
130
hatmill_test.go
130
hatmill_test.go
@ -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)
|
||||||
}
|
}
|
||||||
|
87
internal/attribgen/main.go
Normal file
87
internal/attribgen/main.go
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user