From 3ec17311cc6a576c93d0fe4614ca24d8c5092a90 Mon Sep 17 00:00:00 2001 From: SoMuchForSubtlety Date: Wed, 7 Apr 2021 10:44:14 +0200 Subject: [PATCH] generate const values for enumerations --- xsdgen/cli.go | 11 +- xsdgen/testdata/base64.go.golden | 214 ++++++++++++++++++++++++ xsdgen/testdata/enumeration.go.golden | 34 ++++ xsdgen/testdata/enumeration.xsd | 42 +++++ xsdgen/testdata/library.go.golden | 98 +++++++++++ xsdgen/testdata/mixed-complex.go.golden | 13 ++ xsdgen/testdata/po1.go.golden | 14 ++ xsdgen/testdata/sdn.go.golden | 124 ++++++++++++++ xsdgen/testdata/simple-struct.go.golden | 13 ++ xsdgen/testdata/simple-union.go.golden | 5 + xsdgen/testdata/soap11.go.golden | 69 ++++++++ xsdgen/xsdgen.go | 50 +++++- xsdgen/xsdgen_test.go | 115 +++++++++---- 13 files changed, 754 insertions(+), 48 deletions(-) create mode 100644 xsdgen/testdata/base64.go.golden create mode 100644 xsdgen/testdata/enumeration.go.golden create mode 100644 xsdgen/testdata/enumeration.xsd create mode 100644 xsdgen/testdata/library.go.golden create mode 100644 xsdgen/testdata/mixed-complex.go.golden create mode 100644 xsdgen/testdata/po1.go.golden create mode 100644 xsdgen/testdata/sdn.go.golden create mode 100644 xsdgen/testdata/simple-struct.go.golden create mode 100644 xsdgen/testdata/simple-union.go.golden create mode 100644 xsdgen/testdata/soap11.go.golden diff --git a/xsdgen/cli.go b/xsdgen/cli.go index 0274086..1540688 100644 --- a/xsdgen/cli.go +++ b/xsdgen/cli.go @@ -55,6 +55,9 @@ func (cfg *Config) GenCode(data ...[]byte) (*Code, error) { // associated methods based on a set of XML schema. func (cfg *Config) GenAST(files ...string) (*ast.File, error) { data, err := cfg.readFiles(files...) + if err != nil { + return nil, err + } code, err := cfg.GenCode(data...) if err != nil { return nil, err @@ -62,7 +65,7 @@ func (cfg *Config) GenAST(files ...string) (*ast.File, error) { return code.GenAST() } -func (cfg *Config) readFiles(files ...string) ([][]byte,error) { +func (cfg *Config) readFiles(files ...string) ([][]byte, error) { data := make([][]byte, 0, len(files)) for _, filename := range files { b, err := ioutil.ReadFile(filename) @@ -83,10 +86,8 @@ func (cfg *Config) readFiles(files ...string) ([][]byte,error) { if err != nil { return nil, fmt.Errorf("error reading imported files: %v", err) } - for _, d := range referencedData { - // prepend imported refs (i.e. append before the referencing file) - data = append(data, d) - } + // prepend imported refs (i.e. append before the referencing file) + data = append(data, referencedData...) } data = append(data, b) } diff --git a/xsdgen/testdata/base64.go.golden b/xsdgen/testdata/base64.go.golden new file mode 100644 index 0000000..0baed34 --- /dev/null +++ b/xsdgen/testdata/base64.go.golden @@ -0,0 +1,214 @@ +// Code generated by xsdgen.test. DO NOT EDIT. + +package ws + +import ( + "bytes" + "encoding/base64" + "encoding/xml" + "time" +) + +type MyType1 []byte + +func (t *MyType1) UnmarshalText(text []byte) error { + return (*xsdBase64Binary)(t).UnmarshalText(text) +} +func (t MyType1) MarshalText() ([]byte, error) { + return xsdBase64Binary(t).MarshalText() +} + +type MyType2 struct { + Value []byte `xml:",chardata"` + Length int `xml:"length,attr,omitempty"` +} + +func (t *MyType2) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + type T MyType2 + var layout struct { + *T + Value *xsdBase64Binary `xml:",chardata"` + } + layout.T = (*T)(t) + layout.Value = (*xsdBase64Binary)(&layout.T.Value) + return e.EncodeElement(layout, start) +} +func (t *MyType2) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type T MyType2 + var overlay struct { + *T + Value *xsdBase64Binary `xml:",chardata"` + } + overlay.T = (*T)(t) + overlay.Value = (*xsdBase64Binary)(&overlay.T.Value) + return d.DecodeElement(&overlay, &start) +} + +type MyType3 struct { + Value time.Time `xml:",chardata"` + Length int `xml:"length,attr,omitempty"` +} + +func (t *MyType3) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + type T MyType3 + var layout struct { + *T + Value *xsdDate `xml:",chardata"` + } + layout.T = (*T)(t) + layout.Value = (*xsdDate)(&layout.T.Value) + return e.EncodeElement(layout, start) +} +func (t *MyType3) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type T MyType3 + var overlay struct { + *T + Value *xsdDate `xml:",chardata"` + } + overlay.T = (*T)(t) + overlay.Value = (*xsdDate)(&overlay.T.Value) + return d.DecodeElement(&overlay, &start) +} + +type MyType4 struct { + Title string `xml:"http://example.org/ title"` + Blob []byte `xml:"http://example.org/ blob"` + Timestamp time.Time `xml:"http://example.org/ timestamp"` +} + +func (t *MyType4) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + type T MyType4 + var layout struct { + *T + Blob *xsdBase64Binary `xml:"http://example.org/ blob"` + Timestamp *xsdDateTime `xml:"http://example.org/ timestamp"` + } + layout.T = (*T)(t) + layout.Blob = (*xsdBase64Binary)(&layout.T.Blob) + layout.Timestamp = (*xsdDateTime)(&layout.T.Timestamp) + return e.EncodeElement(layout, start) +} +func (t *MyType4) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type T MyType4 + var overlay struct { + *T + Blob *xsdBase64Binary `xml:"http://example.org/ blob"` + Timestamp *xsdDateTime `xml:"http://example.org/ timestamp"` + } + overlay.T = (*T)(t) + overlay.Blob = (*xsdBase64Binary)(&overlay.T.Blob) + overlay.Timestamp = (*xsdDateTime)(&overlay.T.Timestamp) + return d.DecodeElement(&overlay, &start) +} + +type MyType5 time.Time + +func (t *MyType5) UnmarshalText(text []byte) error { + return (*xsdGDay)(t).UnmarshalText(text) +} +func (t MyType5) MarshalText() ([]byte, error) { + return xsdGDay(t).MarshalText() +} + +type xsdBase64Binary []byte + +func (b *xsdBase64Binary) UnmarshalText(text []byte) (err error) { + *b, err = base64.StdEncoding.DecodeString(string(text)) + return +} +func (b xsdBase64Binary) MarshalText() ([]byte, error) { + var buf bytes.Buffer + enc := base64.NewEncoder(base64.StdEncoding, &buf) + enc.Write([]byte(b)) + enc.Close() + return buf.Bytes(), nil +} + +type xsdDate time.Time + +func (t *xsdDate) UnmarshalText(text []byte) error { + return _unmarshalTime(text, (*time.Time)(t), "2006-01-02") +} +func (t xsdDate) MarshalText() ([]byte, error) { + return _marshalTime((time.Time)(t), "2006-01-02") +} +func (t xsdDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if (time.Time)(t).IsZero() { + return nil + } + m, err := t.MarshalText() + if err != nil { + return err + } + return e.EncodeElement(m, start) +} +func (t xsdDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { + if (time.Time)(t).IsZero() { + return xml.Attr{}, nil + } + m, err := t.MarshalText() + return xml.Attr{Name: name, Value: string(m)}, err +} +func _unmarshalTime(text []byte, t *time.Time, format string) (err error) { + s := string(bytes.TrimSpace(text)) + *t, err = time.Parse(format, s) + if _, ok := err.(*time.ParseError); ok { + *t, err = time.Parse(format+"Z07:00", s) + } + return err +} +func _marshalTime(t time.Time, format string) ([]byte, error) { + return []byte(t.Format(format + "Z07:00")), nil +} + +type xsdDateTime time.Time + +func (t *xsdDateTime) UnmarshalText(text []byte) error { + return _unmarshalTime(text, (*time.Time)(t), "2006-01-02T15:04:05.999999999") +} +func (t xsdDateTime) MarshalText() ([]byte, error) { + return _marshalTime((time.Time)(t), "2006-01-02T15:04:05.999999999") +} +func (t xsdDateTime) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if (time.Time)(t).IsZero() { + return nil + } + m, err := t.MarshalText() + if err != nil { + return err + } + return e.EncodeElement(m, start) +} +func (t xsdDateTime) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { + if (time.Time)(t).IsZero() { + return xml.Attr{}, nil + } + m, err := t.MarshalText() + return xml.Attr{Name: name, Value: string(m)}, err +} + +type xsdGDay time.Time + +func (t *xsdGDay) UnmarshalText(text []byte) error { + return _unmarshalTime(text, (*time.Time)(t), "---02") +} +func (t xsdGDay) MarshalText() ([]byte, error) { + return _marshalTime((time.Time)(t), "---02") +} +func (t xsdGDay) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if (time.Time)(t).IsZero() { + return nil + } + m, err := t.MarshalText() + if err != nil { + return err + } + return e.EncodeElement(m, start) +} +func (t xsdGDay) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { + if (time.Time)(t).IsZero() { + return xml.Attr{}, nil + } + m, err := t.MarshalText() + return xml.Attr{Name: name, Value: string(m)}, err +} diff --git a/xsdgen/testdata/enumeration.go.golden b/xsdgen/testdata/enumeration.go.golden new file mode 100644 index 0000000..1970b23 --- /dev/null +++ b/xsdgen/testdata/enumeration.go.golden @@ -0,0 +1,34 @@ +// Code generated by xsdgen.test. DO NOT EDIT. + +package ws + +// May be one of MON, TUE, WED, THU, FRI, SAT, SUN +type DType string + +const ( + DType_MON DType = "MON" + DType_TUE DType = "TUE" + DType_WED DType = "WED" + DType_THU DType = "THU" + DType_FRI DType = "FRI" + DType_SAT DType = "SAT" + DType_SUN DType = "SUN" +) + +// May be one of 0.9, 0.333333333, 0.0 +type FloatType float64 + +const ( + FloatType_0_9 FloatType = 0.9 + FloatType_0_333333333 FloatType = 0.333333333 + FloatType_0_0 FloatType = 0.0 +) + +// May be one of 1, 2, 3 +type IntType int + +const ( + IntType_1 IntType = 1 + IntType_2 IntType = 2 + IntType_3 IntType = 3 +) diff --git a/xsdgen/testdata/enumeration.xsd b/xsdgen/testdata/enumeration.xsd new file mode 100644 index 0000000..5d8df29 --- /dev/null +++ b/xsdgen/testdata/enumeration.xsd @@ -0,0 +1,42 @@ + + + + + + type for weekday + + + + + + + + + + + + + + + integer enum + + + + + + + + + + + float enum + + + + + + + + + \ No newline at end of file diff --git a/xsdgen/testdata/library.go.golden b/xsdgen/testdata/library.go.golden new file mode 100644 index 0000000..4b12bf6 --- /dev/null +++ b/xsdgen/testdata/library.go.golden @@ -0,0 +1,98 @@ +// Code generated by xsdgen.test. DO NOT EDIT. + +package ws + +import ( + "bytes" + "encoding/xml" + "time" +) + +type Authors struct { + Person []string `xml:"http://dyomedea.com/ns/library person"` +} + +type BookType struct { + Isbn string `xml:"http://dyomedea.com/ns/library isbn"` + Title string `xml:"http://dyomedea.com/ns/library title"` + Authors Authors `xml:"http://dyomedea.com/ns/library authors"` + Characters Characters `xml:"http://dyomedea.com/ns/library characters"` + Available string `xml:"available,attr"` +} + +type Characters struct { + Person []string `xml:"http://dyomedea.com/ns/library person"` +} + +type Library struct { + Book BookType `xml:"http://dyomedea.com/ns/library book"` +} + +type Person struct { + Name string `xml:"http://dyomedea.com/ns/library name"` + Born time.Time `xml:"http://dyomedea.com/ns/library born"` + Dead time.Time `xml:"http://dyomedea.com/ns/library dead,omitempty"` + Qualification string `xml:"http://dyomedea.com/ns/library qualification,omitempty"` +} + +func (t *Person) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + type T Person + var layout struct { + *T + Born *xsdDate `xml:"http://dyomedea.com/ns/library born"` + Dead *xsdDate `xml:"http://dyomedea.com/ns/library dead,omitempty"` + } + layout.T = (*T)(t) + layout.Born = (*xsdDate)(&layout.T.Born) + layout.Dead = (*xsdDate)(&layout.T.Dead) + return e.EncodeElement(layout, start) +} +func (t *Person) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + type T Person + var overlay struct { + *T + Born *xsdDate `xml:"http://dyomedea.com/ns/library born"` + Dead *xsdDate `xml:"http://dyomedea.com/ns/library dead,omitempty"` + } + overlay.T = (*T)(t) + overlay.Born = (*xsdDate)(&overlay.T.Born) + overlay.Dead = (*xsdDate)(&overlay.T.Dead) + return d.DecodeElement(&overlay, &start) +} + +type xsdDate time.Time + +func (t *xsdDate) UnmarshalText(text []byte) error { + return _unmarshalTime(text, (*time.Time)(t), "2006-01-02") +} +func (t xsdDate) MarshalText() ([]byte, error) { + return _marshalTime((time.Time)(t), "2006-01-02") +} +func (t xsdDate) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + if (time.Time)(t).IsZero() { + return nil + } + m, err := t.MarshalText() + if err != nil { + return err + } + return e.EncodeElement(m, start) +} +func (t xsdDate) MarshalXMLAttr(name xml.Name) (xml.Attr, error) { + if (time.Time)(t).IsZero() { + return xml.Attr{}, nil + } + m, err := t.MarshalText() + return xml.Attr{Name: name, Value: string(m)}, err +} +func _unmarshalTime(text []byte, t *time.Time, format string) (err error) { + s := string(bytes.TrimSpace(text)) + *t, err = time.Parse(format, s) + if _, ok := err.(*time.ParseError); ok { + *t, err = time.Parse(format+"Z07:00", s) + } + return err +} +func _marshalTime(t time.Time, format string) ([]byte, error) { + return []byte(t.Format(format + "Z07:00")), nil +} diff --git a/xsdgen/testdata/mixed-complex.go.golden b/xsdgen/testdata/mixed-complex.go.golden new file mode 100644 index 0000000..8f51715 --- /dev/null +++ b/xsdgen/testdata/mixed-complex.go.golden @@ -0,0 +1,13 @@ +// Code generated by xsdgen.test. DO NOT EDIT. + +package ws + +type Number struct { + Value float64 `xml:",chardata"` + Precision int `xml:"precision,attr,omitempty"` +} + +type PositiveNumber struct { + Value float64 `xml:",chardata"` + Precision int `xml:"precision,attr,omitempty"` +} diff --git a/xsdgen/testdata/po1.go.golden b/xsdgen/testdata/po1.go.golden new file mode 100644 index 0000000..a613ebf --- /dev/null +++ b/xsdgen/testdata/po1.go.golden @@ -0,0 +1,14 @@ +// Code generated by xsdgen.test. DO NOT EDIT. + +package ws + +type PurchaseOrderType struct { + ShipTo USAddress `xml:"http://www.example.com/PO1 shipTo"` + BillTo USAddress `xml:"http://www.example.com/PO1 billTo"` + Comment string `xml:"http://www.example.com/PO1 comment,omitempty"` +} + +type USAddress struct { + Name string `xml:"http://www.example.com/PO1 name"` + Street string `xml:"http://www.example.com/PO1 street"` +} diff --git a/xsdgen/testdata/sdn.go.golden b/xsdgen/testdata/sdn.go.golden new file mode 100644 index 0000000..872cea3 --- /dev/null +++ b/xsdgen/testdata/sdn.go.golden @@ -0,0 +1,124 @@ +// Code generated by xsdgen.test. DO NOT EDIT. + +package ws + +type Address struct { + Uid int `xml:"http://tempuri.org/sdnList.xsd uid"` + Address1 string `xml:"http://tempuri.org/sdnList.xsd address1,omitempty"` + Address2 string `xml:"http://tempuri.org/sdnList.xsd address2,omitempty"` + Address3 string `xml:"http://tempuri.org/sdnList.xsd address3,omitempty"` + City string `xml:"http://tempuri.org/sdnList.xsd city,omitempty"` + StateOrProvince string `xml:"http://tempuri.org/sdnList.xsd stateOrProvince,omitempty"` + PostalCode string `xml:"http://tempuri.org/sdnList.xsd postalCode,omitempty"` + Country string `xml:"http://tempuri.org/sdnList.xsd country,omitempty"` +} + +type AddressList struct { + Address []Address `xml:"http://tempuri.org/sdnList.xsd address,omitempty"` +} + +type Aka struct { + Uid int `xml:"http://tempuri.org/sdnList.xsd uid"` + Type string `xml:"http://tempuri.org/sdnList.xsd type"` + Category string `xml:"http://tempuri.org/sdnList.xsd category"` + LastName string `xml:"http://tempuri.org/sdnList.xsd lastName,omitempty"` + FirstName string `xml:"http://tempuri.org/sdnList.xsd firstName,omitempty"` +} + +type AkaList struct { + Aka []Aka `xml:"http://tempuri.org/sdnList.xsd aka,omitempty"` +} + +type Citizenship struct { + Uid int `xml:"http://tempuri.org/sdnList.xsd uid"` + Country string `xml:"http://tempuri.org/sdnList.xsd country"` + MainEntry bool `xml:"http://tempuri.org/sdnList.xsd mainEntry"` +} + +type CitizenshipList struct { + Citizenship []Citizenship `xml:"http://tempuri.org/sdnList.xsd citizenship,omitempty"` +} + +type DateOfBirthItem struct { + Uid int `xml:"http://tempuri.org/sdnList.xsd uid"` + DateOfBirth string `xml:"http://tempuri.org/sdnList.xsd dateOfBirth"` + MainEntry bool `xml:"http://tempuri.org/sdnList.xsd mainEntry"` +} + +type DateOfBirthList struct { + DateOfBirthItem []DateOfBirthItem `xml:"http://tempuri.org/sdnList.xsd dateOfBirthItem,omitempty"` +} + +type Id struct { + Uid int `xml:"http://tempuri.org/sdnList.xsd uid"` + IdType string `xml:"http://tempuri.org/sdnList.xsd idType,omitempty"` + IdNumber string `xml:"http://tempuri.org/sdnList.xsd idNumber,omitempty"` + IdCountry string `xml:"http://tempuri.org/sdnList.xsd idCountry,omitempty"` + IssueDate string `xml:"http://tempuri.org/sdnList.xsd issueDate,omitempty"` + ExpirationDate string `xml:"http://tempuri.org/sdnList.xsd expirationDate,omitempty"` +} + +type IdList struct { + Id []Id `xml:"http://tempuri.org/sdnList.xsd id,omitempty"` +} + +type Nationality struct { + Uid int `xml:"http://tempuri.org/sdnList.xsd uid"` + Country string `xml:"http://tempuri.org/sdnList.xsd country"` + MainEntry bool `xml:"http://tempuri.org/sdnList.xsd mainEntry"` +} + +type NationalityList struct { + Nationality []Nationality `xml:"http://tempuri.org/sdnList.xsd nationality,omitempty"` +} + +type PlaceOfBirthItem struct { + Uid int `xml:"http://tempuri.org/sdnList.xsd uid"` + PlaceOfBirth string `xml:"http://tempuri.org/sdnList.xsd placeOfBirth"` + MainEntry bool `xml:"http://tempuri.org/sdnList.xsd mainEntry"` +} + +type PlaceOfBirthList struct { + PlaceOfBirthItem []PlaceOfBirthItem `xml:"http://tempuri.org/sdnList.xsd placeOfBirthItem,omitempty"` +} + +type ProgramList struct { + Program []string `xml:"http://tempuri.org/sdnList.xsd program,omitempty"` +} + +type PublshInformation struct { + PublishDate string `xml:"http://tempuri.org/sdnList.xsd Publish_Date,omitempty"` + RecordCount int `xml:"http://tempuri.org/sdnList.xsd Record_Count,omitempty"` +} + +type SdnEntry struct { + Uid int `xml:"http://tempuri.org/sdnList.xsd uid"` + FirstName string `xml:"http://tempuri.org/sdnList.xsd firstName,omitempty"` + LastName string `xml:"http://tempuri.org/sdnList.xsd lastName"` + Title string `xml:"http://tempuri.org/sdnList.xsd title,omitempty"` + SdnType string `xml:"http://tempuri.org/sdnList.xsd sdnType"` + Remarks string `xml:"http://tempuri.org/sdnList.xsd remarks,omitempty"` + ProgramList ProgramList `xml:"http://tempuri.org/sdnList.xsd programList"` + IdList IdList `xml:"http://tempuri.org/sdnList.xsd idList,omitempty"` + AkaList AkaList `xml:"http://tempuri.org/sdnList.xsd akaList,omitempty"` + AddressList AddressList `xml:"http://tempuri.org/sdnList.xsd addressList,omitempty"` + NationalityList NationalityList `xml:"http://tempuri.org/sdnList.xsd nationalityList,omitempty"` + CitizenshipList CitizenshipList `xml:"http://tempuri.org/sdnList.xsd citizenshipList,omitempty"` + DateOfBirthList DateOfBirthList `xml:"http://tempuri.org/sdnList.xsd dateOfBirthList,omitempty"` + PlaceOfBirthList PlaceOfBirthList `xml:"http://tempuri.org/sdnList.xsd placeOfBirthList,omitempty"` + VesselInfo VesselInfo `xml:"http://tempuri.org/sdnList.xsd vesselInfo,omitempty"` +} + +type SdnList struct { + PublshInformation PublshInformation `xml:"http://tempuri.org/sdnList.xsd publshInformation"` + SdnEntry []SdnEntry `xml:"http://tempuri.org/sdnList.xsd sdnEntry"` +} + +type VesselInfo struct { + CallSign string `xml:"http://tempuri.org/sdnList.xsd callSign,omitempty"` + VesselType string `xml:"http://tempuri.org/sdnList.xsd vesselType,omitempty"` + VesselFlag string `xml:"http://tempuri.org/sdnList.xsd vesselFlag,omitempty"` + VesselOwner string `xml:"http://tempuri.org/sdnList.xsd vesselOwner,omitempty"` + Tonnage int `xml:"http://tempuri.org/sdnList.xsd tonnage,omitempty"` + GrossRegisteredTonnage int `xml:"http://tempuri.org/sdnList.xsd grossRegisteredTonnage,omitempty"` +} diff --git a/xsdgen/testdata/simple-struct.go.golden b/xsdgen/testdata/simple-struct.go.golden new file mode 100644 index 0000000..5be9b79 --- /dev/null +++ b/xsdgen/testdata/simple-struct.go.golden @@ -0,0 +1,13 @@ +// Code generated by xsdgen.test. DO NOT EDIT. + +package ws + +type ComplexFoo struct { + Element1 Element1 `xml:"http://example.org/ns element1"` +} + +// May be no more than 300 items long +type Element1 string + +// Must be at least 1 items long +type NonEmptyString string diff --git a/xsdgen/testdata/simple-union.go.golden b/xsdgen/testdata/simple-union.go.golden new file mode 100644 index 0000000..40db1a4 --- /dev/null +++ b/xsdgen/testdata/simple-union.go.golden @@ -0,0 +1,5 @@ +// Code generated by xsdgen.test. DO NOT EDIT. + +package ws + +type ConditionalUintType string diff --git a/xsdgen/testdata/soap11.go.golden b/xsdgen/testdata/soap11.go.golden new file mode 100644 index 0000000..daa5710 --- /dev/null +++ b/xsdgen/testdata/soap11.go.golden @@ -0,0 +1,69 @@ +// Code generated by xsdgen.test. DO NOT EDIT. + +package ws + +import ( + "bytes" + "encoding/base64" + "encoding/xml" +) + +// 'Array' is a complex type for accessors identified by position +type Array struct { + Items []string `xml:",any"` + ArrayType string `xml:"arrayType,attr,omitempty"` +} + +type Base64 []byte + +func (t *Base64) UnmarshalText(text []byte) error { + return (*xsdBase64Binary)(t).UnmarshalText(text) +} +func (t Base64) MarshalText() ([]byte, error) { + return xsdBase64Binary(t).MarshalText() +} + +// Must match the pattern 0|1 +type Root bool + +type Struct []string + +func (a Struct) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + var output struct { + ArrayType string `xml:"http://schemas.xmlsoap.org/wsdl/ arrayType,attr"` + Items []string `xml:" item"` + } + output.Items = []string(a) + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{"", "xmlns:ns1"}, Value: "http://www.w3.org/2001/XMLSchema"}) + output.ArrayType = "ns1:anyType[]" + return e.EncodeElement(&output, start) +} +func (a *Struct) UnmarshalXML(d *xml.Decoder, start xml.StartElement) (err error) { + var tok xml.Token + for tok, err = d.Token(); err == nil; tok, err = d.Token() { + if tok, ok := tok.(xml.StartElement); ok { + var item string + if err = d.DecodeElement(&item, &tok); err == nil { + *a = append(*a, item) + } + } + if _, ok := tok.(xml.EndElement); ok { + break + } + } + return err +} + +type xsdBase64Binary []byte + +func (b *xsdBase64Binary) UnmarshalText(text []byte) (err error) { + *b, err = base64.StdEncoding.DecodeString(string(text)) + return +} +func (b xsdBase64Binary) MarshalText() ([]byte, error) { + var buf bytes.Buffer + enc := base64.NewEncoder(base64.StdEncoding, &buf) + enc.Write([]byte(b)) + enc.Close() + return buf.Bytes(), nil +} diff --git a/xsdgen/xsdgen.go b/xsdgen/xsdgen.go index 0604c4d..f84c734 100644 --- a/xsdgen/xsdgen.go +++ b/xsdgen/xsdgen.go @@ -7,6 +7,7 @@ import ( "go/ast" "go/token" "io" + "regexp" "sort" "strconv" "strings" @@ -34,7 +35,7 @@ type errorList []error func (l errorList) Error() string { var buf bytes.Buffer for _, err := range l { - io.WriteString(&buf, err.Error()+"\n") + _, _ = io.WriteString(&buf, err.Error()+"\n") } return buf.String() } @@ -82,7 +83,7 @@ type Code struct { // DocType retrieves the complexType for the provided target // namespace. func (c *Code) DocType(targetNS string) (*xsd.ComplexType, bool) { - key := xml.Name{targetNS, "_self"} + key := xml.Name{Space: targetNS, Local: "_self"} doc, ok := c.types[key].(*xsd.ComplexType) return doc, ok } @@ -247,6 +248,9 @@ func (code *Code) GenAST() (*ast.File, error) { }, } file.Decls = append(file.Decls, typeDecl) + if info.constants != nil { + file.Decls = append(file.Decls, info.constants.Decl) + } for _, f := range info.methods { file.Decls = append(file.Decls, f) } @@ -263,10 +267,12 @@ type spec struct { name, doc string expr ast.Expr private bool + enumValues []string methods []*ast.FuncDecl xsdType xsd.Type helperTypes []xml.Name helperFuncs []string + constants *ast.DeclStmt } // Simplifies complex types derived from other complex types by merging @@ -876,10 +882,11 @@ func (cfg *Config) genSimpleType(t *xsd.SimpleType) ([]spec, error) { // the value would be too complex. Need a use case // first. result = append(result, spec{ - doc: t.Doc, - name: cfg.public(t.Name), - expr: builtinExpr(xsd.String), - xsdType: t, + doc: t.Doc, + name: cfg.public(t.Name), + expr: builtinExpr(xsd.String), + enumValues: t.Restriction.Enum, + xsdType: t, }) return result, nil } @@ -898,6 +905,7 @@ func (cfg *Config) genSimpleType(t *xsd.SimpleType) ([]spec, error) { if err != nil { return result, err } + spec = cfg.addSpecConstants(t, spec) return append(result, spec), nil } @@ -932,6 +940,36 @@ func (cfg *Config) addSpecMethods(s spec) (spec, error) { return s, nil } +// add constants to enumerations +func (cfg *Config) addSpecConstants(t *xsd.SimpleType, s spec) spec { + if len(t.Restriction.Enum) == 0 || nonTrivialBuiltin(t.Base) { + return s + } + + var params []string + validChars := regexp.MustCompile("[^a-zA-Z0-9_]") + for _, option := range t.Restriction.Enum { + // create a const import with a sanitized identifier in the format {{.Type}}_{{.Value}} {{.Type}} = {{.Value}} + params = append(params, cfg.public(t.Name)+"_"+validChars.ReplaceAllString(option, "_"), cfg.public(t.Name), option) + } + + s.constants = &ast.DeclStmt{} + + switch t.Base.(xsd.Builtin) { + case xsd.Integer, xsd.NegativeInteger, xsd.PositiveInteger, xsd.NonNegativeInteger, xsd.NonPositiveInteger, xsd.Int, xsd.UnsignedInt, xsd.Long, xsd.UnsignedLong, xsd.Short, xsd.UnsignedShort: + s.constants.Decl = gen.ConstInt(params...) + case xsd.Float, xsd.Decimal, xsd.Double: + s.constants.Decl = gen.ConstFloat(params...) + case xsd.String: + s.constants.Decl = gen.ConstString(params...) + default: + cfg.logf("[WARNING] skipping const value generation for unknown enumerated type %s", t.Base) + s.constants = nil + } + + return s +} + // Generate a type declaration for a type, along with marshal/unmarshal // methods. func (cfg *Config) genSimpleListSpec(t *xsd.SimpleType) ([]spec, error) { diff --git a/xsdgen/xsdgen_test.go b/xsdgen/xsdgen_test.go index d206de7..fcebbed 100644 --- a/xsdgen/xsdgen_test.go +++ b/xsdgen/xsdgen_test.go @@ -1,41 +1,80 @@ package xsdgen import ( + "bytes" "io/ioutil" "os" - "regexp" + "path" "testing" ) -type testLogger testing.T +func TestExamples(t *testing.T) { + cases := []struct { + name string + sourceFiles []string + namespace string + }{ + { + name: "base64 binary", + sourceFiles: []string{"testdata/base64.xsd"}, + namespace: "http://example.org/", + }, + { + name: "simple union", + sourceFiles: []string{"testdata/simple-union.xsd"}, + namespace: "http://example.org/", + }, + { + name: "enumeration constants", + sourceFiles: []string{"testdata/enumeration.xsd"}, + namespace: "http://example.org/", + }, + { + name: "library schema", + namespace: "http://dyomedea.com/ns/library", + sourceFiles: []string{"testdata/library.xsd"}, + }, + { + name: "purchas order schema", + namespace: "http://www.example.com/PO1", + sourceFiles: []string{"testdata/po1.xsd"}, + }, + { + name: "US treasure SDN", + namespace: "http://tempuri.org/sdnList.xsd", + sourceFiles: []string{"testdata/sdn.xsd"}, + }, + { + name: "SOAP", + namespace: "http://schemas.xmlsoap.org/soap/encoding/", + sourceFiles: []string{"testdata/soap11.xsd"}, + }, + { + name: "simple struct", + namespace: "http://example.org/ns", + sourceFiles: []string{"testdata/simple-struct.xsd"}, + }, + { + name: "mixed data", + namespace: "http://example.org", + sourceFiles: []string{"testdata/mixed-complex.xsd"}, + }, + } -func grep(pattern, data string) bool { - matched, err := regexp.MatchString(pattern, data) - if err != nil { - panic(err) + for _, c := range cases { + c := c + t.Run(c.name, func(t *testing.T) { + testGen(t, c.namespace, c.sourceFiles...) + }) } - return matched } +type testLogger testing.T + func (t *testLogger) Printf(format string, v ...interface{}) { t.Logf(format, v...) } -func TestLibrarySchema(t *testing.T) { - testGen(t, "http://dyomedea.com/ns/library", "testdata/library.xsd") -} -func TestPurchasOrderSchema(t *testing.T) { - testGen(t, "http://www.example.com/PO1", "testdata/po1.xsd") -} -func TestUSTreasureSDN(t *testing.T) { - testGen(t, "http://tempuri.org/sdnList.xsd", "testdata/sdn.xsd") -} -func TestSoap(t *testing.T) { - testGen(t, "http://schemas.xmlsoap.org/soap/encoding/", "testdata/soap11.xsd") -} -func TestSimpleStruct(t *testing.T) { - testGen(t, "http://example.org/ns", "testdata/simple-struct.xsd") -} func testGen(t *testing.T, ns string, files ...string) string { file, err := ioutil.TempFile("", "xsdgen") if err != nil { @@ -56,22 +95,24 @@ func testGen(t *testing.T, ns string, files ...string) string { if err != nil { t.Fatal(err) } - return string(data) -} - -func TestMixedType(t *testing.T) { - data := testGen(t, "http://example.org", "testdata/mixed-complex.xsd") - if !grep(`PositiveNumber[^}]*,chardata`, data) { - t.Errorf("type decl for PositiveNumber did not contain chardata, got \n%s", data) + ext := path.Ext(files[0]) + goldenPath := files[0][0:len(files[0])-len(ext)] + ".go.golden" + // check if we have a golden file to compare to + if _, err := os.Stat(goldenPath); err == nil { + goldenData, err := ioutil.ReadFile(goldenPath) + if err != nil { + t.Fatal(err) + } + if !bytes.Equal(data, goldenData) { + t.Errorf("output does not match %s", goldenPath) + } } else { - t.Logf("got \n%s", data) + // create a new golden file if there is none + err = ioutil.WriteFile(goldenPath, data, 0644) + if err != nil { + t.Fatal(err) + } } -} - -func TestBase64Binary(t *testing.T) { - t.Logf("%s\n", testGen(t, "http://example.org/", "testdata/base64.xsd")) -} -func TestSimpleUnion(t *testing.T) { - t.Logf("%s\n", testGen(t, "http://example.org/", "testdata/simple-union.xsd")) + return string(data) }