diff --git a/xdr2/bench_test.go b/xdr2/bench_test.go index 0fea4db..531bb1a 100644 --- a/xdr2/bench_test.go +++ b/xdr2/bench_test.go @@ -14,14 +14,12 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -package xdr_test +package xdr import ( "bytes" "testing" "unsafe" - - "github.com/davecgh/go-xdr/xdr2" ) // BenchmarkUnmarshal benchmarks the Unmarshal function by using a dummy @@ -47,7 +45,7 @@ func BenchmarkUnmarshal(b *testing.B) { for i := 0; i < b.N; i++ { r := bytes.NewReader(encodedData) - _, _ = xdr.Unmarshal(r, &h) + _, _ = Unmarshal(r, &h) } b.SetBytes(int64(len(encodedData))) } @@ -70,7 +68,7 @@ func BenchmarkMarshal(b *testing.B) { for i := 0; i < b.N; i++ { w.Reset() - _, _ = xdr.Marshal(w, &h) + _, _ = Marshal(w, &h) } b.SetBytes(int64(size)) } diff --git a/xdr2/decode.go b/xdr2/decode.go index 494dae6..6f37f76 100644 --- a/xdr2/decode.go +++ b/xdr2/decode.go @@ -21,6 +21,7 @@ import ( "io" "math" "reflect" + "strconv" "time" ) @@ -507,6 +508,7 @@ func (d *Decoder) decodeArray(v reflect.Value, ignoreOpaque bool) (int, error) { // XDR encoded elements in the order of their declaration in the struct func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { var n int + var union string vt := v.Type() for i := 0; i < v.NumField(); i++ { // Skip unexported fields. @@ -515,13 +517,36 @@ func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { continue } + vf := v.Field(i) + tag := parseTag(vtf.Tag) + + // RFC Section 4.19 - Optional data + if tag.Get("optional") == "true" { + if vf.Type().Kind() != reflect.Ptr { + msg := fmt.Sprintf("optional must be a pointer, not '%v'", + vf.Type().String()) + err := unmarshalError("decodeStruct", ErrBadOptional, + msg, nil, nil) + return n, err + } + + hasopt, n2, err := d.DecodeBool() + n += n2 + if err != nil { + return n, err + } + if !hasopt { + continue + } + } + // Indirect through pointers allocating them as needed and // ensure the field is settable. - vf := v.Field(i) vf, err := d.indirect(vf) if err != nil { return n, err } + if !vf.CanSet() { msg := fmt.Sprintf("can't decode to unsettable '%v'", vf.Type().String()) @@ -532,8 +557,7 @@ func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { // Handle non-opaque data to []uint8 and [#]uint8 based on // struct tag. - tag := vtf.Tag.Get("xdropaque") - if tag == "false" { + if tag.Get("opaque") == "false" { switch vf.Kind() { case reflect.Slice: n2, err := d.decodeArray(vf, true) @@ -553,25 +577,46 @@ func (d *Decoder) decodeStruct(v reflect.Value) (int, error) { } } + if union != "" { + ucase := tag.Get("unioncase") + if ucase != "" && ucase != union { + continue + } + } + // Decode each struct field. n2, err := d.decode(vf) n += n2 if err != nil { return n, err } + + if tag.Get("union") == "true" { + if vf.Type().ConvertibleTo(reflect.TypeOf(0)) { + union = strconv.Itoa(int(vf.Convert(reflect.TypeOf(0)).Int())) + } else if vf.Kind() == reflect.Bool { + if vf.Bool() { + union = "1" + } else { + union = "0" + } + } else { + msg := fmt.Sprintf("type '%s' is not valid", vf.Kind().String()) + return n, unmarshalError("decodeStruct", ErrBadDiscriminant, msg, nil, nil) + } + } + } return n, nil } -// RFC Section 4.15 - Discriminated Union // RFC Section 4.16 - Void // RFC Section 4.17 - Constant // RFC Section 4.18 - Typedef // RFC Section 4.19 - Optional data // RFC Sections 4.15 though 4.19 only apply to the data specification language -// which is not implemented by this package. In the case of discriminated -// unions, struct tags are used to perform a similar function. +// which is not implemented by this package. // decodeMap treats the next bytes as an XDR encoded variable array of 2-element // structures whose fields are of the same type as the map keys and elements diff --git a/xdr2/decode_test.go b/xdr2/decode_test.go index 310629d..fef1e7d 100644 --- a/xdr2/decode_test.go +++ b/xdr2/decode_test.go @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -package xdr_test +package xdr import ( "bytes" @@ -23,8 +23,6 @@ import ( "reflect" "testing" "time" - - . "github.com/davecgh/go-xdr/xdr2" ) // subTest is used to allow testing of the Unmarshal function into struct fields @@ -60,8 +58,35 @@ type allTypesTest struct { // opaqueStruct is used to test handling of uint8 slices and arrays. type opaqueStruct struct { - Slice []uint8 `xdropaque:"false"` - Array [1]uint8 `xdropaque:"false"` + Slice []uint8 `xdr:"opaque=false"` + Array [1]uint8 `xdropaque:"false"` // old syntax, backward compatibility +} + +type unionStruct struct { + UV int `xdr:"union"` + V0 byte `xdr:"unioncase=0"` + V1 byte `xdr:"unioncase=1"` + VA byte +} + +type unionBoolStruct struct { + UV bool `xdr:"union"` + V0 byte `xdr:"unioncase=0"` + V1 byte `xdr:"unioncase=1"` + VA byte +} + +type invalidUnionStruct struct { + UV string `xdr:"union"` +} + +type optionalDataStruct struct { + Data int + Next *optionalDataStruct `xdr:"optional"` +} + +type invalidOptionalDataStruct struct { + Data int `xdr:"optional"` } // testExpectedURet is a convenience method to test an expected number of bytes @@ -350,6 +375,37 @@ func TestUnmarshal(t *testing.T) { {[]byte{0x00, 0x00, 0x00}, opaqueStruct{}, 3, &UnmarshalError{ErrorCode: ErrIO}}, {[]byte{0x00, 0x00, 0x00, 0x00, 0x00}, opaqueStruct{}, 5, &UnmarshalError{ErrorCode: ErrIO}}, + // Discriminated unions + {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01}, + unionStruct{0, 1, 0, 1}, + 12, nil}, + {[]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01}, + unionStruct{1, 0, 1, 1}, + 12, nil}, + {[]byte{0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01}, + unionStruct{2, 0, 0, 1}, + 8, nil}, + {[]byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01}, + unionBoolStruct{false, 1, 0, 1}, + 12, nil}, + {[]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01}, + unionBoolStruct{true, 0, 1, 1}, + 12, nil}, + {[]byte{0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01}, + invalidUnionStruct{}, + 8, &UnmarshalError{ErrorCode: ErrBadDiscriminant}}, + + // Optional data + {[]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00}, + optionalDataStruct{1, &optionalDataStruct{2, nil}}, + 16, nil}, + {[]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, + optionalDataStruct{1, nil}, + 7, &UnmarshalError{ErrorCode: ErrIO}}, + {[]byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00}, + invalidOptionalDataStruct{}, + 0, &UnmarshalError{ErrorCode: ErrBadOptional}}, + // Expected errors {nil, nilInterface, 0, &UnmarshalError{ErrorCode: ErrNilInterface}}, {nil, &nilInterface, 0, &UnmarshalError{ErrorCode: ErrNilInterface}}, diff --git a/xdr2/doc.go b/xdr2/doc.go index 8823d62..1904d2b 100644 --- a/xdr2/doc.go +++ b/xdr2/doc.go @@ -66,14 +66,15 @@ and Unmarshal functions has specific details of how the mapping proceeds. [#]byte <-> XDR Fixed-Length Opaque Data [] <-> XDR Variable-Length Array [#] <-> XDR Fixed-Length Array - struct <-> XDR Structure + * <-> XDR Optional data (when marked with struct tag `xdr:"optional"`) + struct <-> XDR Structure or Discriminated Unions map <-> XDR Variable-Length Array of two-element XDR Structures time.Time <-> XDR String encoded with RFC3339 nanosecond precision Notes and Limitations: * Automatic marshalling and unmarshalling of variable and fixed-length - arrays of uint8s require a special struct tag `xdropaque:"false"` + arrays of uint8s require a special struct tag `xdr:"opaque=false"` since byte slices and byte arrays are assumed to be opaque data and byte is a Go alias for uint8 thus indistinguishable under reflection * Channel, complex, and function types cannot be encoded @@ -159,6 +160,40 @@ manually decode XDR primitives for complex scenarios where automatic reflection-based decoding won't work. The included examples provide a sample of manual usage via a Decoder. + +Discriminated Unions + +Discriminated unions are marshalled via Go structs, using special struct tags +to mark the discriminant and the different cases. For instance: + + type ReturnValue struct { + Status int `xdr:"union"` + StatusOk struct { + Width int + Height int + } `xdr:"unioncase=0"` + StatusError struct { + ErrMsg string + } `xdr:"unioncase=-1"` + } + +The Status field is the discriminant of the union, and is always serialized; +if its value is 0, the StatusOK struct is serialized while the StatusErr struct +is ignored; if its value is -1, the opposite happens. If the value is different +from both 0 and -1, only the Status field is serialized. Any additional field +not marked with unioncase is always serialized as normal. + +You are not forced to use sub-structures; for instance, the following is also +valid: + + type ReturnValue struct { + Status int `xdr:"union"` + Width int `xdr:"unioncase=0"` + Height int `xdr:"unioncase=0"` + ErrMsg string `xdr:"unioncase=-1"` + } + + Errors All errors are either of type UnmarshalError or MarshalError. Both provide diff --git a/xdr2/encode.go b/xdr2/encode.go index 7bac268..73a2ff0 100644 --- a/xdr2/encode.go +++ b/xdr2/encode.go @@ -21,6 +21,7 @@ import ( "io" "math" "reflect" + "strconv" "time" ) @@ -440,6 +441,7 @@ func (enc *Encoder) encodeArray(v reflect.Value, ignoreOpaque bool) (int, error) // XDR encoded elements in the order of their declaration in the struct func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) { var n int + var union string vt := v.Type() for i := 0; i < v.NumField(); i++ { // Skip unexported fields and indirect through pointers. @@ -447,12 +449,35 @@ func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) { if vtf.PkgPath != "" { continue } + vf := v.Field(i) + tag := parseTag(vtf.Tag) + + // RFC Section 4.19 - Optional data + if tag.Get("optional") == "true" { + if vf.Type().Kind() != reflect.Ptr { + msg := fmt.Sprintf("optional must be a pointer, not '%v'", + vf.Type().String()) + err := marshalError("encodeStruct", ErrBadOptional, + msg, nil, nil) + return n, err + } + + hasopt := !vf.IsNil() + n2, err := enc.EncodeBool(hasopt) + n += n2 + if err != nil { + return n, err + } + if !hasopt { + continue + } + } + vf = enc.indirect(vf) // Handle non-opaque data to []uint8 and [#]uint8 based on struct tag. - tag := vtf.Tag.Get("xdropaque") - if tag == "false" { + if tag.Get("opaque") == "false" { switch vf.Kind() { case reflect.Slice: n2, err := enc.encodeArray(vf, true) @@ -472,6 +497,32 @@ func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) { } } + // RFC Section 4.15 - Discriminated Union + // The tag option "union" marks the discriminant in the struct; the tag + // option "unioncase=N" marks a struct field that is only serialized + // when the discriminant has the specified value. + if tag.Get("union") == "true" { + if vf.Type().ConvertibleTo(reflect.TypeOf(0)) { + union = strconv.Itoa(int(vf.Convert(reflect.TypeOf(0)).Int())) + } else if vf.Kind() == reflect.Bool { + if vf.Bool() { + union = "1" + } else { + union = "0" + } + } else { + msg := fmt.Sprintf("type '%s' is not valid", vf.Kind().String()) + return n, marshalError("encodeStruct", ErrBadDiscriminant, msg, nil, nil) + } + } + + if union != "" { + ucase := tag.Get("unioncase") + if ucase != "" && ucase != union { + continue + } + } + // Encode each struct field. n2, err := enc.encode(vf) n += n2 @@ -483,14 +534,12 @@ func (enc *Encoder) encodeStruct(v reflect.Value) (int, error) { return n, nil } -// RFC Section 4.15 - Discriminated Union // RFC Section 4.16 - Void // RFC Section 4.17 - Constant // RFC Section 4.18 - Typedef // RFC Section 4.19 - Optional data -// RFC Sections 4.15 though 4.19 only apply to the data specification language -// which is not implemented by this package. In the case of discriminated -// unions, struct tags are used to perform a similar function. +// RFC Sections 4.16 though 4.19 only apply to the data specification language +// which is not implemented by this package. // encodeMap treats the map represented by the passed reflection value as a // variable-length array of 2-element structures whose fields are of the same diff --git a/xdr2/encode_test.go b/xdr2/encode_test.go index 1b04cfa..7e59f30 100644 --- a/xdr2/encode_test.go +++ b/xdr2/encode_test.go @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -package xdr_test +package xdr import ( "fmt" @@ -22,8 +22,6 @@ import ( "reflect" "testing" "time" - - . "github.com/davecgh/go-xdr/xdr2" ) // testExpectedMRet is a convenience method to test an expected number of bytes @@ -307,6 +305,35 @@ func TestMarshal(t *testing.T) { []byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01}, 8, &MarshalError{ErrorCode: ErrIO}}, + // Discriminated unions + {unionStruct{0, 0, 1, 2}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + 12, nil}, + {unionStruct{1, 0, 1, 2}, + []byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02}, + 12, nil}, + {unionStruct{2, 0, 1, 2}, + []byte{0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02}, + 8, nil}, + {unionBoolStruct{false, 0, 1, 2}, + []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}, + 12, nil}, + {unionBoolStruct{true, 0, 1, 2}, + []byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02}, + 12, nil}, + {invalidUnionStruct{"err"}, []byte{}, 0, &MarshalError{ErrorCode: ErrBadDiscriminant}}, + + // Optional data + {optionalDataStruct{1, &optionalDataStruct{2, nil}}, + []byte{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00}, + 16, nil}, + {optionalDataStruct{1, nil}, + []uint8{0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, + 7, &MarshalError{ErrorCode: ErrIO}}, + {invalidOptionalDataStruct{1}, + []byte{}, + 0, &MarshalError{ErrorCode: ErrBadOptional}}, + // Expected errors {nilInterface, []byte{}, 0, &MarshalError{ErrorCode: ErrNilInterface}}, {&nilInterface, []byte{}, 0, &MarshalError{ErrorCode: ErrNilInterface}}, diff --git a/xdr2/error.go b/xdr2/error.go index 42079ad..8c1f3ae 100644 --- a/xdr2/error.go +++ b/xdr2/error.go @@ -61,6 +61,14 @@ const ( // RFC3339 formatted time value. The actual underlying error will be // available via the Err field of the UnmarshalError struct. ErrParseTime + + // ErrBadDiscriminant indicates that a non-integer field of a struct + // was marked as a union discriminant through a struct tag. + ErrBadDiscriminant + + // ErrBadOptional indicates that a non-pointer field of a struct + // was marked as an optional-data. + ErrBadOptional ) // Map of ErrorCode values back to their constant names for pretty printing. @@ -73,6 +81,8 @@ var errorCodeStrings = map[ErrorCode]string{ ErrNilInterface: "ErrNilInterface", ErrIO: "ErrIO", ErrParseTime: "ErrParseTime", + ErrBadDiscriminant: "ErrBadDiscriminant", + ErrBadOptional: "ErrBadOptional", } // String returns the ErrorCode as a human-readable name. diff --git a/xdr2/error_test.go b/xdr2/error_test.go index ac9c156..1a2c010 100644 --- a/xdr2/error_test.go +++ b/xdr2/error_test.go @@ -14,13 +14,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -package xdr_test +package xdr import ( "errors" "testing" - - . "github.com/davecgh/go-xdr/xdr2" ) // TestErrorCodeStringer tests the stringized output for the ErrorCode type. diff --git a/xdr2/example_test.go b/xdr2/example_test.go index 1272862..2e71251 100644 --- a/xdr2/example_test.go +++ b/xdr2/example_test.go @@ -14,13 +14,11 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -package xdr_test +package xdr import ( "bytes" "fmt" - - "github.com/davecgh/go-xdr/xdr2" ) // This example demonstrates how to use Marshal to automatically XDR encode @@ -40,7 +38,7 @@ func ExampleMarshal() { // Use marshal to automatically determine the appropriate underlying XDR // types and encode. var w bytes.Buffer - bytesWritten, err := xdr.Marshal(&w, &h) + bytesWritten, err := Marshal(&w, &h) if err != nil { fmt.Println(err) return @@ -78,7 +76,7 @@ func ExampleUnmarshal() { // Declare a variable to provide Unmarshal with a concrete type and // instance to decode into. var h ImageHeader - bytesRead, err := xdr.Unmarshal(bytes.NewReader(encodedData), &h) + bytesRead, err := Unmarshal(bytes.NewReader(encodedData), &h) if err != nil { fmt.Println(err) return @@ -111,7 +109,7 @@ func ExampleNewDecoder() { } // Get a new decoder for manual decoding. - dec := xdr.NewDecoder(bytes.NewReader(encodedData)) + dec := NewDecoder(bytes.NewReader(encodedData)) signature, _, err := dec.DecodeFixedOpaque(3) if err != nil { @@ -167,7 +165,7 @@ func ExampleNewEncoder() { // Get a new encoder for manual encoding. var w bytes.Buffer - enc := xdr.NewEncoder(&w) + enc := NewEncoder(&w) _, err := enc.EncodeFixedOpaque(signature) if err != nil { diff --git a/xdr2/fixedIO_test.go b/xdr2/fixedIO_test.go index d329a9a..baac54a 100644 --- a/xdr2/fixedIO_test.go +++ b/xdr2/fixedIO_test.go @@ -14,7 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -package xdr_test +package xdr import ( "io" diff --git a/xdr2/tag.go b/xdr2/tag.go new file mode 100644 index 0000000..7b5fe24 --- /dev/null +++ b/xdr2/tag.go @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2015-2017 Giovanni Bajo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package xdr + +import ( + "reflect" + "strings" +) + +// xdrtag represents a XDR struct tag, identified by the name "xdr:". +// The value of the tag is a string that is parsed as a comma-separated +// list of =-separated key-value options. If an option has no value, +// "true" is assumed to be the default value. +// +// For instance: +// +// `xdr:"foo,bar=2,baz=false" +// +// After parsing this tag, Get("foo") will return "true", Get("bar") +// will return "2", and Get("baz") will return "false". +type xdrtag string + +// parseTag extracts a xdrtag from the original reflect.StructTag as found in +// in the struct field. If the tag was not specified, an empty strtag is +// returned. +func parseTag(tag reflect.StructTag) xdrtag { + t := tag.Get("xdr") + // Handle backward compatibility with the previous "xdropaque" + // tag which is now deprecated. + if tag.Get("xdropaque") == "false" { + if t == "" { + t = "," + } + t += ",opaque=false" + } + return xdrtag(t) +} + +// Get returns the value for the specified option. If the option is not +// present in the tag, an empty string is returned. If the option is +// present but has no value, the string "true" is returned as default value. +func (t xdrtag) Get(opt string) string { + tag := string(t) + for tag != "" { + var next string + i := strings.Index(tag, ",") + if i >= 0 { + tag, next = tag[:i], tag[i+1:] + } + if tag == opt { + return "true" + } + if len(tag) > len(opt) && tag[:len(opt)] == opt && tag[len(opt)] == '=' { + return tag[len(opt)+1:] + } + tag = next + } + return "" +} diff --git a/xdr2/tag_test.go b/xdr2/tag_test.go new file mode 100644 index 0000000..26a438b --- /dev/null +++ b/xdr2/tag_test.go @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015-2017 Giovanni Bajo + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +package xdr + +import ( + "reflect" + "testing" +) + +type structWithTag struct { + A int `xdr:"a=123,b,c=true,d=false"` +} + +func TestTag(t *testing.T) { + s := structWithTag{} + rt := reflect.TypeOf(s).Field(0) + tag := parseTag(rt.Tag) + + if tag.Get("a") != "123" { + t.Errorf("wrong value for a: %v", tag.Get("a")) + } + if tag.Get("b") != "true" { + t.Errorf("wrong value for b: %v", tag.Get("b")) + } + if tag.Get("c") != "true" { + t.Errorf("wrong value for b: %v", tag.Get("c")) + } + if tag.Get("d") != "false" { + t.Errorf("wrong value for b: %v", tag.Get("d")) + } +}