diff --git a/.github/workflows/code_quality.yml b/.github/workflows/code_quality.yml new file mode 100644 index 0000000..85b7024 --- /dev/null +++ b/.github/workflows/code_quality.yml @@ -0,0 +1,22 @@ +name: Code quality + +on: + push: + branches: + - master + +jobs: + code-quality: + name: Code quality + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@master + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.17' + + - name: Unit tests + run: go test -v ./... diff --git a/Makefile b/Makefile index 4dc4163..9cc3a15 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ SHELL = /bin/bash TARGETS = zek PKGNAME = zek ARCH = amd64 -VERSION = 0.1.16 +VERSION = 0.1.21 .PHONY: all all: $(TARGETS) @@ -52,7 +52,7 @@ rpm: $(TARGETS) docs/$(PKGNAME).1 .PHONY: release release: - @export GITHUB_TOKEN="..." + @echo export GITHUB_TOKEN="..." @echo go tag $(VERSION) @echo goreleaser release --rm-dist diff --git a/README.md b/README.md index 0389271..3e71cb1 100644 --- a/README.md +++ b/README.md @@ -78,7 +78,8 @@ type Rss struct { ## Online -Try it online at [https://www.onlinetool.io/xmltogo/](https://www.onlinetool.io/xmltogo/) -- thanks, [kjk](https://github.com/kjk)! +* try online via WASM: [https://xml-to-go.github.io/](https://xml-to-go.github.io/), thanks [YaroslavPodorvanov](https://github.com/YaroslavPodorvanov)! +* try it online at [https://blog.kowalczyk.info/tools/xmltogo/](https://blog.kowalczyk.info/tools/xmltogo/) -- thanks, [kjk](https://github.com/kjk)! ## About @@ -98,7 +99,7 @@ Downsides: * experimental, early, buggy, unstable prototype, * no support for recursive types (similar to *Russian Doll* strategy, [[1](https://medbiq.org/std_specs/techguidelines/xmldesignguidelines.pdf#page=7)]) -* no type inference, everything is accessible as string. +* no type inference, everything is accessible as string (without a schema, type inference may fail if the type *guess* is wrong) Bugs: @@ -113,6 +114,7 @@ Related projects: * https://github.com/bemasher/JSONGen * https://github.com/dutchcoders/XMLGen * https://github.com/gnewton/chidley +* https://github.com/twpayne/go-xmlstruct And other [awesome XML utilities](https://github.com/avelino/awesome-go#xml). @@ -144,6 +146,8 @@ Usage of zek: -F skip formatting -P string if set, write out struct within a package with the given name + -S int + read at most this many tags, approximately (0=unlimited) -c emit more compact struct (noop, as this is the default since 0.1.7) -d debug output -e add comments with example diff --git a/cmd/zek/main.go b/cmd/zek/main.go index 90102fc..9c4a8ae 100644 --- a/cmd/zek/main.go +++ b/cmd/zek/main.go @@ -23,6 +23,7 @@ var ( debug = flag.Bool("d", false, "debug output") createExampleProgram = flag.Bool("p", false, "write out an example program") tagName = flag.String("t", "", "emit struct for tag matching this name") + notInlineStructs = flag.Bool("I", false, "do not inline children tags as anonymous structs") skipFormatting = flag.Bool("F", false, "skip formatting") strict = flag.Bool("s", false, "strict parsing and writing") exampleMaxChars = flag.Int("x", 25, "max chars for example") @@ -35,6 +36,7 @@ var ( outputFile = flag.String("o", "", "if set, write to output file, not stdout") packageName = flag.String("P", "", "if set, write out struct within a package with the given name") fixedBanner = flag.Bool("B", false, "use a fixed banner string (e.g. for CI)") + readAtMost = flag.Int64("S", 0, "read at most this many tags, approximately (0=unlimited)") ) func main() { @@ -73,7 +75,11 @@ func main() { } reader = io.MultiReader(rs...) } - if _, err := root.ReadFrom(reader); err != nil { + opts := zek.ReadOpts{ + MaxExamples: *maxExamples, + MaxTokens: *readAtMost, + } + if _, err := root.ReadFrom(reader, &opts); err != nil { log.Fatal(err) } // Move root, if we have a tagName. Ignore unknown names. @@ -99,6 +105,7 @@ func main() { sw.Strict = *strict sw.ExampleMaxChars = *exampleMaxChars sw.Compact = !*nonCompact + sw.InlineStructs = !*notInlineStructs sw.UniqueExamples = *uniqueExamples sw.OmitEmptyText = *omitEmptyText if *fixedBanner { diff --git a/docs/zek.md b/docs/zek.md index 1d81fe5..8299e89 100644 --- a/docs/zek.md +++ b/docs/zek.md @@ -75,6 +75,9 @@ at https://www.onlinetool.io/xmltogo/. -s : Strict parsing. +-S +: raed at most this many tokens, approximately (0=unlimited) + -t : Emit struct for tag matching this name. diff --git a/go.mod b/go.mod index e85cd95..52a29b7 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/miku/zek -go 1.12 +go 1.17 require ( - github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0 - golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 // indirect - golang.org/x/net v0.0.0-20210917221730-978cfadd31cf - golang.org/x/text v0.3.7 // indirect + github.com/sethgrid/pester v1.2.0 + golang.org/x/net v0.19.0 ) + +require golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index 6816a97..7069ebf 100644 --- a/go.sum +++ b/go.sum @@ -1,18 +1,43 @@ -github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0 h1:X9XMOYjxEfAYSy3xK1DzO5dMkkWhs9E9UCcS1IERx2k= -github.com/sethgrid/pester v0.0.0-20190127155807-68a33a018ad0/go.mod h1:Ad7IjTpvzZO8Fl0vh9AzQ+j/jYZfyp2diGwI8m5q+ns= +github.com/sethgrid/pester v1.2.0 h1:adC9RS29rRUef3rIKWPOuP1Jm3/MmB6ke+OhE5giENI= +github.com/sethgrid/pester v1.2.0/go.mod h1:hEUINb4RqvDxtoCaU0BNT/HV4ig5kfgOasrf1xcvr0A= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0= -golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf h1:R150MpwJIv1MpS0N/pc+NhTM8ajzvlmxlY5OYsrevXQ= -golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/node.go b/node.go index ff0bf52..305639d 100644 --- a/node.go +++ b/node.go @@ -45,10 +45,16 @@ type Node struct { childFreqs map[xml.Name]int // Count child tag occurrences, used temporarily. } +// ReadOpts groups options for parsing. +type ReadOpts struct { + MaxExamples int + MaxTokens int64 +} + // readNode reads XML from a reader and returns a parsed node. If node is // given, it is reused, allowing for multiple passes (e.g. from multiple // files). XXX: maxExamples should be factored out into options. -func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, err error) { +func readNode(r io.Reader, root *Node, opts *ReadOpts) (node *Node, n int64, err error) { var ( cw = countwriter{} dec = xml.NewDecoder(io.TeeReader(r, &cw)) @@ -60,6 +66,8 @@ func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, er } stack := Stack{} stack.Put(root) + var i int64 +OUTER: for { token, err := dec.Token() if err == io.EOF { @@ -68,6 +76,7 @@ func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, er if err != nil { return root, cw.n, err } + i++ switch t := token.(type) { case xml.StartElement: parent := stack.Peek().(*Node) @@ -76,13 +85,16 @@ func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, er case xml.EndElement: n := stack.Pop().(*Node) n.End() + if opts.MaxTokens > 0 && i > opts.MaxTokens { + break OUTER + } case xml.CharData: v := strings.TrimSpace(string(t)) if v == "" { break } n := stack.Peek().(*Node) - if len(n.Examples) < maxExamples { + if len(n.Examples) < opts.MaxExamples { // XXX: sample better, e.g. reservoir dictionary. n.Examples = append(n.Examples, v) } @@ -92,9 +104,9 @@ func readNode(r io.Reader, root *Node, maxExamples int) (node *Node, n int64, er return root, cw.n, nil } -// ReadFrom reads XML from a reader. -func (node *Node) ReadFrom(r io.Reader) (int64, error) { - nn, n, err := readNode(r, nil, node.MaxExamples) +// ReadFrom reads XML from a reader. TODO: pass read options. +func (node *Node) ReadFrom(r io.Reader, opts *ReadOpts) (int64, error) { + nn, n, err := readNode(r, nil, opts) if err != nil { return n, err } diff --git a/node_test.go b/node_test.go index f409df8..f4eeac3 100644 --- a/node_test.go +++ b/node_test.go @@ -419,7 +419,7 @@ func TestNodeReadFrom(t *testing.T) { for _, c := range cases { r := strings.NewReader(c.input) node := new(Node) - _, err := node.ReadFrom(r) + _, err := node.ReadFrom(r, &ReadOpts{MaxExamples: 10}) if err != c.err { t.Errorf("got %v, want %v", err, c.err) } @@ -462,7 +462,7 @@ func TestHeight(t *testing.T) { for _, c := range cases { r := strings.NewReader(c.input) node := new(Node) - _, err := node.ReadFrom(r) + _, err := node.ReadFrom(r, &ReadOpts{MaxExamples: 10}) if err != nil { t.Errorf("failed to parse tree: %s", err) } @@ -475,7 +475,7 @@ func TestHeight(t *testing.T) { func TestByName(t *testing.T) { r := strings.NewReader(``) root := new(Node) - if _, err := root.ReadFrom(r); err != nil { + if _, err := root.ReadFrom(r, &ReadOpts{MaxExamples: 10}); err != nil { t.Errorf("got %v, want nil", err) } @@ -892,7 +892,7 @@ func TestNodeReadFromAll(t *testing.T) { } node := new(Node) - _, err := node.ReadFrom(io.MultiReader(readers...)) + _, err := node.ReadFrom(io.MultiReader(readers...), &ReadOpts{MaxExamples: 10}) if err != c.err { t.Errorf("got %v, want %v", err, c.err) } diff --git a/packaging/deb/control.amd64 b/packaging/deb/control.amd64 index 9ead8fa..d1ee457 100644 --- a/packaging/deb/control.amd64 +++ b/packaging/deb/control.amd64 @@ -1,5 +1,5 @@ Package: zek -Version: 0.1.16 +Version: 0.1.21 Section: utils Priority: optional Architecture: amd64 diff --git a/packaging/deb/control.any b/packaging/deb/control.any index 0524882..71e5712 100644 --- a/packaging/deb/control.any +++ b/packaging/deb/control.any @@ -1,5 +1,5 @@ Package: zek -Version: 0.1.16 +Version: 0.1.21 Section: utils Priority: optional Architecture: any diff --git a/packaging/deb/zek/DEBIAN/control b/packaging/deb/zek/DEBIAN/control index 9ead8fa..d1ee457 100644 --- a/packaging/deb/zek/DEBIAN/control +++ b/packaging/deb/zek/DEBIAN/control @@ -1,5 +1,5 @@ Package: zek -Version: 0.1.16 +Version: 0.1.21 Section: utils Priority: optional Architecture: amd64 diff --git a/packaging/rpm/zek.spec b/packaging/rpm/zek.spec index 5ab0deb..e469b57 100644 --- a/packaging/rpm/zek.spec +++ b/packaging/rpm/zek.spec @@ -1,6 +1,6 @@ Summary: Generate a Go struct from an XML document. Name: zek -Version: 0.1.16 +Version: 0.1.21 Release: 0 License: GPL BuildArch: x86_64 diff --git a/structwriter.go b/structwriter.go index ece6f37..17840e0 100644 --- a/structwriter.go +++ b/structwriter.go @@ -100,7 +100,8 @@ type StructWriter struct { Strict bool // Whether to ignore implementation holes. WithJSONTags bool // Include JSON struct tags. Compact bool // Emit more compact struct. - UniqueExamples bool // Filter out duplicated examples + InlineStructs bool // Inlines children tags as anonymous structs. + UniqueExamples bool // Filter out duplicated examples. OmitEmptyText bool // Don't generate Text fields if no example elements have chardata. } @@ -242,53 +243,59 @@ func (sw *StructWriter) writeNode(node *Node, top bool) (err error) { fmt.Fprintf(sew, "%s\n", s) return err } - io.WriteString(sew, "struct {\n") - if top { - sw.writeNameField(sew, node) - } - if !sw.OmitEmptyText || len(node.Examples) > 0 { - sw.writeChardataField(sew, node) - } - // Helper to check for name clash of attribute with any generated field name. - isValidName := func(name string) bool { - if name == sw.TextFieldNames[0] { - return false + + if sw.InlineStructs || top { + io.WriteString(sew, "struct {\n") + if top { + sw.writeNameField(sew, node) } - for _, child := range node.Children { - if name == sw.NameFunc(child.Name.Local) { + if !sw.OmitEmptyText || len(node.Examples) > 0 { + sw.writeChardataField(sew, node) + } + // Helper to check for name clash of attribute with any generated field name. + isValidName := func(name string) bool { + if name == sw.TextFieldNames[0] { return false } - } - return true - } - // Write attributes. XXX: Better handling of duplicate attributes. - written := make(map[string]bool) - for _, attr := range node.Attr { - name := sw.NameFunc(attr.Name.Local) - for _, prefix := range sw.AttributePrefixes { - if isValidName(name) { - break + for _, child := range node.Children { + if name == sw.NameFunc(child.Name.Local) { + return false + } } - name = fmt.Sprintf("%s%s", prefix, name) - } - if !isValidName(name) { - return fmt.Errorf("name clash: %s", attr.Name.Local) + return true } - if _, ok := written[attr.Name.Local]; ok { - if sw.Strict { - log.Fatalf("[not implemented] duplicate local attribute name: %s", attr) - } else { - log.Printf("warning: duplicate local attribute name: %s", attr) + // Write attributes. XXX: Better handling of duplicate attributes. + written := make(map[string]bool) + for _, attr := range node.Attr { + name := sw.NameFunc(attr.Name.Local) + for _, prefix := range sw.AttributePrefixes { + if isValidName(name) { + break + } + name = fmt.Sprintf("%s%s", prefix, name) + } + if !isValidName(name) { + return fmt.Errorf("name clash: %s", attr.Name.Local) + } + if _, ok := written[attr.Name.Local]; ok { + if sw.Strict { + log.Fatalf("[not implemented] duplicate local attribute name: %s", attr) + } else { + log.Printf("warning: duplicate local attribute name: %s", attr) + } + continue } - continue + sw.writeAttrField(sew, name, "string", attr) + written[attr.Name.Local] = true } - sw.writeAttrField(sew, name, "string", attr) - written[attr.Name.Local] = true - } - for _, child := range node.Children { - sw.writeNode(child, false) + for _, child := range node.Children { + sw.writeNode(child, false) + } + io.WriteString(sew, "} ") + } else { + io.WriteString(sew, sw.NameFunc(node.Name.Local)+" ") } - io.WriteString(sew, "} ") + if !top { sw.writeStructTag(sew, node) } diff --git a/structwriter_test.go b/structwriter_test.go index 2eec9b5..8bbeab8 100644 --- a/structwriter_test.go +++ b/structwriter_test.go @@ -78,72 +78,85 @@ func TestWriteNode(t *testing.T) { withComments bool omitEmptyText bool uniqueExamples bool + inlineStructs bool err error }{ { - input: "testdata/w.1.xml", - result: "testdata/w.1.go", - err: nil, + input: "testdata/w.1.xml", + result: "testdata/w.1.go", + inlineStructs: true, + err: nil, }, { - input: "testdata/w.2.xml", - result: "testdata/w.2.go", - err: nil, + input: "testdata/w.2.xml", + result: "testdata/w.2.go", + inlineStructs: true, + err: nil, }, { - input: "testdata/w.3.xml", - result: "testdata/w.3.go", - err: nil, + input: "testdata/w.3.xml", + result: "testdata/w.3.go", + inlineStructs: true, + err: nil, }, { - input: "testdata/w.4.xml", - result: "testdata/w.4.go", - err: nil, + input: "testdata/w.4.xml", + result: "testdata/w.4.go", + inlineStructs: true, + err: nil, }, { - input: "testdata/w.5.xml", - result: "testdata/w.5.go", - err: nil, + input: "testdata/w.5.xml", + result: "testdata/w.5.go", + inlineStructs: true, + err: nil, }, { - input: "testdata/w.6.xml", - result: "testdata/w.6.go", - err: nil, + input: "testdata/w.6.xml", + result: "testdata/w.6.go", + inlineStructs: true, + err: nil, }, { - input: "testdata/w.7.xml", - result: "testdata/w.7.go", - err: nil, + input: "testdata/w.7.xml", + result: "testdata/w.7.go", + inlineStructs: true, + err: nil, }, { - input: "testdata/w.8.xml", - result: "testdata/w.8.go", - withComments: true, - err: nil, + input: "testdata/w.8.xml", + result: "testdata/w.8.go", + withComments: true, + inlineStructs: true, + err: nil, }, { - input: "testdata/w.9.xml", - result: "testdata/w.9.go", - withComments: true, - err: nil, + input: "testdata/w.9.xml", + result: "testdata/w.9.go", + withComments: true, + inlineStructs: true, + err: nil, }, { - input: "testdata/w.10.xml", - result: "testdata/w.10.go", - withComments: true, - err: nil, + input: "testdata/w.10.xml", + result: "testdata/w.10.go", + withComments: true, + inlineStructs: true, + err: nil, }, { - input: "testdata/w.11.xml", - result: "testdata/w.11.go", - withComments: true, - err: nil, + input: "testdata/w.11.xml", + result: "testdata/w.11.go", + withComments: true, + inlineStructs: true, + err: nil, }, { input: "testdata/w.12.xml", result: "testdata/w.12.go", withComments: true, uniqueExamples: false, + inlineStructs: true, err: nil, }, { @@ -151,6 +164,7 @@ func TestWriteNode(t *testing.T) { result: "testdata/w.13.go", withComments: true, uniqueExamples: true, + inlineStructs: true, err: nil, }, { @@ -158,13 +172,21 @@ func TestWriteNode(t *testing.T) { result: "testdata/w.14.go", withComments: true, uniqueExamples: true, + inlineStructs: true, omitEmptyText: true, err: nil, }, { - input: "testdata/w.15.xml", - result: "testdata/w.15.go", - err: nil, + input: "testdata/w.15.xml", + result: "testdata/w.15.go", + inlineStructs: true, + err: nil, + }, + { + input: "testdata/w.16.xml", + result: "testdata/w.16.go", + inlineStructs: false, + err: nil, }, } @@ -178,7 +200,7 @@ func TestWriteNode(t *testing.T) { node := new(Node) node.MaxExamples = 10 - if _, err := node.ReadFrom(f); err != nil { + if _, err := node.ReadFrom(f, &ReadOpts{MaxExamples: 10}); err != nil { t.Errorf("failed to read XML input: %s", err) } @@ -187,6 +209,7 @@ func TestWriteNode(t *testing.T) { sw.WithComments = c.withComments sw.UniqueExamples = c.uniqueExamples sw.OmitEmptyText = c.omitEmptyText + sw.InlineStructs = c.inlineStructs if err := sw.WriteNode(node); err != c.err { t.Errorf("WriteNode failed: got %v, want %v", err, c.err) diff --git a/testdata/w.16.go b/testdata/w.16.go new file mode 100644 index 0000000..8d8aeb7 --- /dev/null +++ b/testdata/w.16.go @@ -0,0 +1,10 @@ +package main + +import "encoding/xml" + +type A struct { + XMLName xml.Name `xml:"a"` + Text string `xml:",chardata"` + B B `xml:"b"` + A A `xml:"a"` +} diff --git a/testdata/w.16.xml b/testdata/w.16.xml new file mode 100644 index 0000000..37fad67 --- /dev/null +++ b/testdata/w.16.xml @@ -0,0 +1,9 @@ + + Europe + + Germany + + Leipzig + + + diff --git a/version.go b/version.go index 5b8741e..ed29d55 100644 --- a/version.go +++ b/version.go @@ -22,4 +22,4 @@ package zek // Version of application. -const Version = "0.1.16" +const Version = "0.1.21"