gojay

high performance JSON encoder/decoder with stream API for Golang
git clone git://git.lair.cx/gojay
Log | Files | Refs | README | LICENSE

commit 9d67e07523a5848a7723c933d46d8514a2c9a30a
parent f5750264fb7b954483282400685ca5a75d6f5537
Author: francoispqt <francois@parquet.ninja>
Date:   Mon, 18 Jun 2018 21:28:42 +0800

add deps for generator

Diffstat:
Agojay/Gopkg.lock | 14++++++++++++++
Agojay/Gopkg.toml | 26++++++++++++++++++++++++++
Agojay/vendor/github.com/fatih/structtag/.travis.yml | 4++++
Agojay/vendor/github.com/fatih/structtag/LICENSE | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/vendor/github.com/fatih/structtag/README.md | 73+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/vendor/github.com/fatih/structtag/tags.go | 303+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Agojay/vendor/github.com/fatih/structtag/tags_test.go | 390+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 870 insertions(+), 0 deletions(-)

diff --git a/gojay/Gopkg.lock b/gojay/Gopkg.lock @@ -0,0 +1,14 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + +[[projects]] + name = "github.com/fatih/structtag" + packages = ["."] + revision = "da3d9ab5b78fdc25d3a7614853b085200bd10da9" + version = "v0.1.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "859116499e74f6c88afba2065cef9e2ee1caa81d532e01ed144541f9d36faae8" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/gojay/Gopkg.toml b/gojay/Gopkg.toml @@ -0,0 +1,26 @@ + +# Gopkg.toml example +# +# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md +# for detailed Gopkg.toml documentation. +# +# required = ["github.com/user/thing/cmd/thing"] +# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"] +# +# [[constraint]] +# name = "github.com/user/project" +# version = "1.0.0" +# +# [[constraint]] +# name = "github.com/user/project2" +# branch = "dev" +# source = "github.com/myfork/project2" +# +# [[override]] +# name = "github.com/x/y" +# version = "2.4.0" + + +[[constraint]] + name = "github.com/fatih/structtag" + version = "0.1.0" diff --git a/gojay/vendor/github.com/fatih/structtag/.travis.yml b/gojay/vendor/github.com/fatih/structtag/.travis.yml @@ -0,0 +1,4 @@ +language: go +go: + - 1.7.x + - tip diff --git a/gojay/vendor/github.com/fatih/structtag/LICENSE b/gojay/vendor/github.com/fatih/structtag/LICENSE @@ -0,0 +1,60 @@ +Copyright (c) 2017, Fatih Arslan +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of structtag nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +This software includes some portions from Go. Go is used under the terms of the +BSD like license. + +Copyright (c) 2012 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The Go gopher was designed by Renee French. http://reneefrench.blogspot.com/ The design is licensed under the Creative Commons 3.0 Attributions license. Read this article for more details: https://blog.golang.org/gopher diff --git a/gojay/vendor/github.com/fatih/structtag/README.md b/gojay/vendor/github.com/fatih/structtag/README.md @@ -0,0 +1,73 @@ +# structtag [![GoDoc](http://img.shields.io/badge/go-documentation-blue.svg?style=flat-square)](http://godoc.org/github.com/fatih/structtag) [![Build Status](https://travis-ci.org/fatih/structtag.svg?branch=master)](https://travis-ci.org/fatih/structtag) + +structtag provides an easy way of parsing and manipulating struct tag fields. +Please vendor the library as it might change in future versions. + +# Install + +```bash +go get github.com/fatih/structtag +``` + +# Example + +```go +package main + +import ( + "fmt" + "reflect" + "sort" + + "github.com/fatih/structtag" +) + +func main() { + type t struct { + t string `json:"foo,omitempty,string" xml:"foo"` + } + + // get field tag + tag := reflect.TypeOf(t{}).Field(0).Tag + + // ... and start using structtag by parsing the tag + tags, err := structtag.Parse(string(tag)) + if err != nil { + panic(err) + } + + // iterate over all tags + for _, t := range tags.Tags() { + fmt.Printf("tag: %+v\n", t) + } + + // get a single tag + jsonTag, err := tags.Get("json") + if err != nil { + panic(err) + } + fmt.Println(jsonTag) // Output: json:"foo,omitempty,string" + fmt.Println(jsonTag.Key) // Output: json + fmt.Println(jsonTag.Name) // Output: foo + fmt.Println(jsonTag.Options) // Output: [omitempty string] + + // change existing tag + jsonTag.Name = "foo_bar" + jsonTag.Options = nil + tags.Set(jsonTag) + + // add new tag + tags.Set(&structtag.Tag{ + Key: "hcl", + Name: "foo", + Options: []string{"squash"}, + }) + + // print the tags + fmt.Println(tags) // Output: json:"foo_bar" xml:"foo" hcl:"foo,squash" + + // sort tags according to keys + sort.Sort(tags) + fmt.Println(tags) // Output: hcl:"foo,squash" json:"foo_bar" xml:"foo" +} +``` diff --git a/gojay/vendor/github.com/fatih/structtag/tags.go b/gojay/vendor/github.com/fatih/structtag/tags.go @@ -0,0 +1,303 @@ +package structtag + +import ( + "bytes" + "errors" + "fmt" + "strconv" + "strings" +) + +var ( + errTagSyntax = errors.New("bad syntax for struct tag pair") + errTagKeySyntax = errors.New("bad syntax for struct tag key") + errTagValueSyntax = errors.New("bad syntax for struct tag value") + + errKeyNotSet = errors.New("tag key does not exist") + errTagNotExist = errors.New("tag does not exist") + errTagKeyMismatch = errors.New("mismatch between key and tag.key") +) + +// Tags represent a set of tags from a single struct field +type Tags struct { + tags []*Tag +} + +// Tag defines a single struct's string literal tag +type Tag struct { + // Key is the tag key, such as json, xml, etc.. + // i.e: `json:"foo,omitempty". Here key is: "json" + Key string + + // Name is a part of the value + // i.e: `json:"foo,omitempty". Here name is: "foo" + Name string + + // Options is a part of the value. It contains a slice of tag options i.e: + // `json:"foo,omitempty". Here options is: ["omitempty"] + Options []string +} + +// Parse parses a single struct field tag and returns the set of tags. +func Parse(tag string) (*Tags, error) { + var tags []*Tag + + // NOTE(arslan) following code is from reflect and vet package with some + // modifications to collect all necessary information and extend it with + // usable methods + for tag != "" { + // Skip leading space. + i := 0 + for i < len(tag) && tag[i] == ' ' { + i++ + } + tag = tag[i:] + if tag == "" { + return nil, nil + } + + // Scan to colon. A space, a quote or a control character is a syntax + // error. Strictly speaking, control chars include the range [0x7f, + // 0x9f], not just [0x00, 0x1f], but in practice, we ignore the + // multi-byte control characters as it is simpler to inspect the tag's + // bytes than the tag's runes. + i = 0 + for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f { + i++ + } + + if i == 0 { + return nil, errTagKeySyntax + } + if i+1 >= len(tag) || tag[i] != ':' { + return nil, errTagSyntax + } + if tag[i+1] != '"' { + return nil, errTagValueSyntax + } + + key := string(tag[:i]) + tag = tag[i+1:] + + // Scan quoted string to find value. + i = 1 + for i < len(tag) && tag[i] != '"' { + if tag[i] == '\\' { + i++ + } + i++ + } + if i >= len(tag) { + return nil, errTagValueSyntax + } + + qvalue := string(tag[:i+1]) + tag = tag[i+1:] + + value, err := strconv.Unquote(qvalue) + if err != nil { + return nil, errTagValueSyntax + } + + res := strings.Split(value, ",") + name := res[0] + options := res[1:] + if len(options) == 0 { + options = nil + } + + tags = append(tags, &Tag{ + Key: key, + Name: name, + Options: options, + }) + } + + return &Tags{ + tags: tags, + }, nil +} + +// Get returns the tag associated with the given key. If the key is present +// in the tag the value (which may be empty) is returned. Otherwise the +// returned value will be the empty string. The ok return value reports whether +// the tag exists or not (which the return value is nil). +func (t *Tags) Get(key string) (*Tag, error) { + for _, tag := range t.tags { + if tag.Key == key { + return tag, nil + } + } + + return nil, errTagNotExist +} + +// Set sets the given tag. If the tag key already exists it'll override it +func (t *Tags) Set(tag *Tag) error { + if tag.Key == "" { + return errKeyNotSet + } + + added := false + for i, tg := range t.tags { + if tg.Key == tag.Key { + added = true + t.tags[i] = tag + } + } + + if !added { + // this means this is a new tag, add it + t.tags = append(t.tags, tag) + } + + return nil +} + +// AddOptions adds the given option for the given key. If the option already +// exists it doesn't add it again. +func (t *Tags) AddOptions(key string, options ...string) { + for i, tag := range t.tags { + if tag.Key != key { + continue + } + + for _, opt := range options { + if !tag.HasOption(opt) { + tag.Options = append(tag.Options, opt) + } + } + + t.tags[i] = tag + } +} + +// DeleteOptions deletes the given options for the given key +func (t *Tags) DeleteOptions(key string, options ...string) { + hasOption := func(option string) bool { + for _, opt := range options { + if opt == option { + return true + } + } + return false + } + + for i, tag := range t.tags { + if tag.Key != key { + continue + } + + var updated []string + for _, opt := range tag.Options { + if !hasOption(opt) { + updated = append(updated, opt) + } + } + + tag.Options = updated + t.tags[i] = tag + } +} + +// Delete deletes the tag for the given keys +func (t *Tags) Delete(keys ...string) { + hasKey := func(key string) bool { + for _, k := range keys { + if k == key { + return true + } + } + return false + } + + var updated []*Tag + for _, tag := range t.tags { + if !hasKey(tag.Key) { + updated = append(updated, tag) + } + } + + t.tags = updated +} + +// Tags returns a slice of tags. The order is the original tag order unless it +// was changed. +func (t *Tags) Tags() []*Tag { + return t.tags +} + +// Tags returns a slice of tags. The order is the original tag order unless it +// was changed. +func (t *Tags) Keys() []string { + var keys []string + for _, tag := range t.tags { + keys = append(keys, tag.Key) + } + return keys +} + +// String reassembles the tags into a valid literal tag field representation +func (t *Tags) String() string { + tags := t.Tags() + if len(tags) == 0 { + return "" + } + + var buf bytes.Buffer + for i, tag := range t.Tags() { + buf.WriteString(tag.String()) + if i != len(tags)-1 { + buf.WriteString(" ") + } + } + return buf.String() +} + +// HasOption returns true if the given option is available in options +func (t *Tag) HasOption(opt string) bool { + for _, tagOpt := range t.Options { + if tagOpt == opt { + return true + } + } + + return false +} + +// String reassembles the tag into a valid tag field representation +func (t *Tag) String() string { + options := strings.Join(t.Options, ",") + if options != "" { + return fmt.Sprintf(`%s:"%s,%s"`, t.Key, t.Name, options) + } + return fmt.Sprintf(`%s:"%s"`, t.Key, t.Name) +} + +// GoString implements the fmt.GoStringer interface +func (t *Tag) GoString() string { + template := `{ + Key: '%s', + Name: '%s', + Option: '%s', + }` + + if t.Options == nil { + return fmt.Sprintf(template, t.Key, t.Name, "nil") + } + + options := strings.Join(t.Options, ",") + return fmt.Sprintf(template, t.Key, t.Name, options) +} + +func (t *Tags) Len() int { + return len(t.tags) +} + +func (t *Tags) Less(i int, j int) bool { + return t.tags[i].Key < t.tags[j].Key +} + +func (t *Tags) Swap(i int, j int) { + t.tags[i], t.tags[j] = t.tags[j], t.tags[i] +} diff --git a/gojay/vendor/github.com/fatih/structtag/tags_test.go b/gojay/vendor/github.com/fatih/structtag/tags_test.go @@ -0,0 +1,390 @@ +package structtag + +import ( + "reflect" + "sort" + "testing" +) + +func TestParse(t *testing.T) { + test := []struct { + name string + tag string + exp []*Tag + invalid bool + }{ + { + name: "empty tag", + tag: "", + }, + { + name: "tag with one key (invalid)", + tag: "json", + invalid: true, + }, + { + name: "tag with one key (valid)", + tag: `json:""`, + exp: []*Tag{ + { + Key: "json", + }, + }, + }, + { + name: "tag with one key and dash name", + tag: `json:"-"`, + exp: []*Tag{ + { + Key: "json", + Name: "-", + }, + }, + }, + { + name: "tag with key and name", + tag: `json:"foo"`, + exp: []*Tag{ + { + Key: "json", + Name: "foo", + }, + }, + }, + { + name: "tag with key, name and option", + tag: `json:"foo,omitempty"`, + exp: []*Tag{ + { + Key: "json", + Name: "foo", + Options: []string{"omitempty"}, + }, + }, + }, + { + name: "tag with multiple keys", + tag: `json:"" hcl:""`, + exp: []*Tag{ + { + Key: "json", + }, + { + Key: "hcl", + }, + }, + }, + { + name: "tag with multiple keys and names", + tag: `json:"foo" hcl:"foo"`, + exp: []*Tag{ + { + Key: "json", + Name: "foo", + }, + { + Key: "hcl", + Name: "foo", + }, + }, + }, + { + name: "tag with multiple keys and names", + tag: `json:"foo" hcl:"foo"`, + exp: []*Tag{ + { + Key: "json", + Name: "foo", + }, + { + Key: "hcl", + Name: "foo", + }, + }, + }, + { + name: "tag with multiple keys and different names", + tag: `json:"foo" hcl:"bar"`, + exp: []*Tag{ + { + Key: "json", + Name: "foo", + }, + { + Key: "hcl", + Name: "bar", + }, + }, + }, + { + name: "tag with multiple keys, different names and options", + tag: `json:"foo,omitempty" structs:"bar,omitnested"`, + exp: []*Tag{ + { + Key: "json", + Name: "foo", + Options: []string{"omitempty"}, + }, + { + Key: "structs", + Name: "bar", + Options: []string{"omitnested"}, + }, + }, + }, + { + name: "tag with multiple keys, different names and options", + tag: `json:"foo" structs:"bar,omitnested" hcl:"-"`, + exp: []*Tag{ + { + Key: "json", + Name: "foo", + }, + { + Key: "structs", + Name: "bar", + Options: []string{"omitnested"}, + }, + { + Key: "hcl", + Name: "-", + }, + }, + }, + } + + for _, ts := range test { + t.Run(ts.name, func(t *testing.T) { + tags, err := Parse(ts.tag) + invalid := err != nil + + if invalid != ts.invalid { + t.Errorf("invalid case\n\twant: %+v\n\tgot : %+v\n\terr : %s", ts.invalid, invalid, err) + } + + if invalid { + return + } + + got := tags.Tags() + + if !reflect.DeepEqual(ts.exp, got) { + t.Errorf("parse\n\twant: %#v\n\tgot : %#v", ts.exp, got) + } + }) + } +} + +func TestTags_Get(t *testing.T) { + tag := `json:"foo,omitempty" structs:"bar,omitnested"` + + tags, err := Parse(tag) + if err != nil { + t.Fatal(err) + } + + found, err := tags.Get("json") + if err != nil { + t.Fatal(err) + } + + want := `json:"foo,omitempty"` + + if found.String() != want { + t.Errorf("get\n\twant: %#v\n\tgot : %#v", want, found.String()) + } +} + +func TestTags_Set(t *testing.T) { + tag := `json:"foo,omitempty" structs:"bar,omitnested"` + + tags, err := Parse(tag) + if err != nil { + t.Fatal(err) + } + + err = tags.Set(&Tag{ + Key: "json", + Name: "bar", + Options: []string{}, + }) + if err != nil { + t.Fatal(err) + } + + found, err := tags.Get("json") + if err != nil { + t.Fatal(err) + } + + want := `json:"bar"` + if found.String() != want { + t.Errorf("set\n\twant: %#v\n\tgot : %#v", want, found.String()) + } +} + +func TestTags_Set_Append(t *testing.T) { + tag := `json:"foo,omitempty"` + + tags, err := Parse(tag) + if err != nil { + t.Fatal(err) + } + + err = tags.Set(&Tag{ + Key: "structs", + Name: "bar", + Options: []string{"omitnested"}, + }) + if err != nil { + t.Fatal(err) + } + + found, err := tags.Get("structs") + if err != nil { + t.Fatal(err) + } + + want := `structs:"bar,omitnested"` + if found.String() != want { + t.Errorf("set append\n\twant: %#v\n\tgot : %#v", want, found.String()) + } + + wantFull := `json:"foo,omitempty" structs:"bar,omitnested"` + if tags.String() != wantFull { + t.Errorf("set append\n\twant: %#v\n\tgot : %#v", wantFull, tags.String()) + } +} + +func TestTags_Set_KeyDoesNotExist(t *testing.T) { + tag := `json:"foo,omitempty" structs:"bar,omitnested"` + + tags, err := Parse(tag) + if err != nil { + t.Fatal(err) + } + + err = tags.Set(&Tag{ + Key: "", + Name: "bar", + Options: []string{}, + }) + if err == nil { + t.Fatal("setting tag with a nonexisting key should error") + } + + if err != errKeyNotSet { + t.Errorf("set\n\twant: %#v\n\tgot : %#v", errTagKeyMismatch, err) + } +} + +func TestTags_Delete(t *testing.T) { + tag := `json:"foo,omitempty" structs:"bar,omitnested" hcl:"-"` + + tags, err := Parse(tag) + if err != nil { + t.Fatal(err) + } + + tags.Delete("structs") + if tags.Len() != 2 { + t.Fatalf("tag length should be 2, have %d", tags.Len()) + } + + found, err := tags.Get("json") + if err != nil { + t.Fatal(err) + } + + want := `json:"foo,omitempty"` + if found.String() != want { + t.Errorf("delete\n\twant: %#v\n\tgot : %#v", want, found.String()) + } + + wantFull := `json:"foo,omitempty" hcl:"-"` + if tags.String() != wantFull { + t.Errorf("delete\n\twant: %#v\n\tgot : %#v", wantFull, tags.String()) + } +} + +func TestTags_DeleteOptions(t *testing.T) { + tag := `json:"foo,omitempty" structs:"bar,omitnested,omitempty" hcl:"-"` + + tags, err := Parse(tag) + if err != nil { + t.Fatal(err) + } + + tags.DeleteOptions("json", "omitempty") + + want := `json:"foo" structs:"bar,omitnested,omitempty" hcl:"-"` + if tags.String() != want { + t.Errorf("delete option\n\twant: %#v\n\tgot : %#v", want, tags.String()) + } + + tags.DeleteOptions("structs", "omitnested") + want = `json:"foo" structs:"bar,omitempty" hcl:"-"` + if tags.String() != want { + t.Errorf("delete option\n\twant: %#v\n\tgot : %#v", want, tags.String()) + } +} + +func TestTags_AddOption(t *testing.T) { + tag := `json:"foo" structs:"bar,omitempty" hcl:"-"` + + tags, err := Parse(tag) + if err != nil { + t.Fatal(err) + } + + tags.AddOptions("json", "omitempty") + + want := `json:"foo,omitempty" structs:"bar,omitempty" hcl:"-"` + if tags.String() != want { + t.Errorf("add options\n\twant: %#v\n\tgot : %#v", want, tags.String()) + } + + // this shouldn't change anything + tags.AddOptions("structs", "omitempty") + + want = `json:"foo,omitempty" structs:"bar,omitempty" hcl:"-"` + if tags.String() != want { + t.Errorf("add options\n\twant: %#v\n\tgot : %#v", want, tags.String()) + } + + // this should append to the existing + tags.AddOptions("structs", "omitnested", "flatten") + want = `json:"foo,omitempty" structs:"bar,omitempty,omitnested,flatten" hcl:"-"` + if tags.String() != want { + t.Errorf("add options\n\twant: %#v\n\tgot : %#v", want, tags.String()) + } +} + +func TestTags_String(t *testing.T) { + tag := `json:"foo" structs:"bar,omitnested" hcl:"-"` + + tags, err := Parse(tag) + if err != nil { + t.Fatal(err) + } + + if tags.String() != tag { + t.Errorf("string\n\twant: %#v\n\tgot : %#v", tag, tags.String()) + } +} + +func TestTags_Sort(t *testing.T) { + tag := `json:"foo" structs:"bar,omitnested" hcl:"-"` + + tags, err := Parse(tag) + if err != nil { + t.Fatal(err) + } + + sort.Sort(tags) + + want := `hcl:"-" json:"foo" structs:"bar,omitnested"` + if tags.String() != want { + t.Errorf("string\n\twant: %#v\n\tgot : %#v", want, tags.String()) + } +}