Create v2/soap/types, refactored from v1.
This commit is contained in:
parent
0926402f8c
commit
47e825446b
864
v2/soap/types/types.go
Normal file
864
v2/soap/types/types.go
Normal file
@ -0,0 +1,864 @@
|
||||
// Package types defines types that encode values in SOAP requests and responses.
|
||||
package types
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
var (
|
||||
// localLoc acts like time.Local for this package, but is faked out by the
|
||||
// unit tests to ensure that things stay constant (especially when running
|
||||
// this test in a place where local time is UTC which might mask bugs).
|
||||
localLoc = time.Local
|
||||
)
|
||||
|
||||
type SOAPValue interface {
|
||||
Marshal() (string, error)
|
||||
Unmarshal(s string) error
|
||||
}
|
||||
|
||||
type UI1 uint8
|
||||
|
||||
var _ SOAPValue = new(UI1)
|
||||
|
||||
func NewUI1(v uint8) *UI1 {
|
||||
v2 := UI1(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *UI1) String() string {
|
||||
return strconv.FormatUint(uint64(*v), 10)
|
||||
}
|
||||
|
||||
func (v *UI1) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *UI1) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseUint(s, 10, 8)
|
||||
*v = UI1(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
type UI2 uint16
|
||||
|
||||
var _ SOAPValue = new(UI2)
|
||||
|
||||
func NewUI2(v uint16) *UI2 {
|
||||
v2 := UI2(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *UI2) String() string {
|
||||
return strconv.FormatUint(uint64(*v), 10)
|
||||
}
|
||||
|
||||
func (v *UI2) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *UI2) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseUint(s, 10, 16)
|
||||
*v = UI2(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
type UI4 uint32
|
||||
|
||||
var _ SOAPValue = new(UI4)
|
||||
|
||||
func NewUI4(v uint32) *UI4 {
|
||||
v2 := UI4(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *UI4) String() string {
|
||||
return strconv.FormatUint(uint64(*v), 10)
|
||||
}
|
||||
|
||||
func (v *UI4) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *UI4) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseUint(s, 10, 32)
|
||||
*v = UI4(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
type UI8 uint64
|
||||
|
||||
var _ SOAPValue = new(UI8)
|
||||
|
||||
func NewUI8(v uint64) *UI8 {
|
||||
v2 := UI8(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *UI8) String() string {
|
||||
return strconv.FormatUint(uint64(*v), 10)
|
||||
}
|
||||
|
||||
func (v *UI8) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *UI8) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseUint(s, 10, 64)
|
||||
*v = UI8(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
type I1 int8
|
||||
|
||||
var _ SOAPValue = new(I1)
|
||||
|
||||
func NewI1(v int8) *I1 {
|
||||
v2 := I1(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *I1) String() string {
|
||||
return strconv.FormatInt(int64(*v), 10)
|
||||
}
|
||||
|
||||
func (v *I1) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *I1) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseInt(s, 10, 8)
|
||||
*v = I1(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
type I2 int16
|
||||
|
||||
var _ SOAPValue = new(I2)
|
||||
|
||||
func NewI2(v int16) *I2 {
|
||||
v2 := I2(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *I2) String() string {
|
||||
return strconv.FormatInt(int64(*v), 10)
|
||||
}
|
||||
|
||||
func (v *I2) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *I2) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseInt(s, 10, 16)
|
||||
*v = I2(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
type I4 int32
|
||||
|
||||
var _ SOAPValue = new(I4)
|
||||
|
||||
func NewI4(v int32) *I4 {
|
||||
v2 := I4(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *I4) String() string {
|
||||
return strconv.FormatInt(int64(*v), 10)
|
||||
}
|
||||
|
||||
func (v *I4) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *I4) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseInt(s, 10, 32)
|
||||
*v = I4(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
type I8 int64
|
||||
|
||||
var _ SOAPValue = new(I8)
|
||||
|
||||
func NewI8(v int64) *I8 {
|
||||
v2 := I8(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *I8) String() string {
|
||||
return strconv.FormatInt(int64(*v), 10)
|
||||
}
|
||||
|
||||
func (v *I8) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *I8) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseInt(s, 10, 64)
|
||||
*v = I8(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
type R4 float32
|
||||
|
||||
var _ SOAPValue = new(R4)
|
||||
|
||||
func NewR4(v float32) *R4 {
|
||||
v2 := R4(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *R4) String() string {
|
||||
return strconv.FormatFloat(float64(*v), 'G', -1, 32)
|
||||
}
|
||||
|
||||
func (v *R4) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *R4) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseFloat(s, 32)
|
||||
*v = R4(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
type R8 float64
|
||||
|
||||
var _ SOAPValue = new(R8)
|
||||
|
||||
func NewR8(v float64) *R8 {
|
||||
v2 := R8(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *R8) String() string {
|
||||
return strconv.FormatFloat(float64(*v), 'G', -1, 64)
|
||||
}
|
||||
|
||||
func (v *R8) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *R8) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseFloat(s, 64)
|
||||
*v = R8(v2)
|
||||
return err
|
||||
}
|
||||
|
||||
// FloatFixed14_4 maps a float64 to the SOAP "fixed.14.4" type.
|
||||
type FloatFixed14_4 float64
|
||||
|
||||
var _ SOAPValue = new(FloatFixed14_4)
|
||||
|
||||
func NewFloatFixed14_4(v float64) *FloatFixed14_4 {
|
||||
v2 := FloatFixed14_4(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *FloatFixed14_4) String() string {
|
||||
return strconv.FormatFloat(float64(*v), 'f', 4, 64)
|
||||
}
|
||||
|
||||
func (v *FloatFixed14_4) Marshal() (string, error) {
|
||||
if *v >= 1e14 || *v <= -1e14 {
|
||||
return "", fmt.Errorf("soap fixed14.4: value %v out of bounds", v)
|
||||
}
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *FloatFixed14_4) Unmarshal(s string) error {
|
||||
v2, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if v2 >= 1e14 || v2 <= -1e14 {
|
||||
return fmt.Errorf("soap fixed14.4: value %q out of bounds", s)
|
||||
}
|
||||
*v = FloatFixed14_4(v2)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Char maps rune to SOAP "char" type.
|
||||
type Char rune
|
||||
|
||||
var _ SOAPValue = new(Char)
|
||||
|
||||
func NewChar(v rune) *Char {
|
||||
v2 := Char(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *Char) String() string {
|
||||
return string(*v)
|
||||
}
|
||||
|
||||
func (v *Char) Marshal() (string, error) {
|
||||
if *v == 0 {
|
||||
return "", errors.New("soap char: rune 0 is not allowed")
|
||||
}
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *Char) Unmarshal(s string) error {
|
||||
if len(s) == 0 {
|
||||
return errors.New("soap char: got empty string")
|
||||
}
|
||||
v2, n := utf8.DecodeRune([]byte(s))
|
||||
if n != len(s) {
|
||||
return fmt.Errorf("soap char: value %q is not a single rune", s)
|
||||
}
|
||||
*v = Char(v2)
|
||||
return nil
|
||||
}
|
||||
|
||||
type String string
|
||||
|
||||
var _ SOAPValue = new(String)
|
||||
|
||||
func NewString(v string) *String {
|
||||
v2 := String(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *String) Marshal() (string, error) {
|
||||
return string(*v), nil
|
||||
}
|
||||
|
||||
func (v *String) Unmarshal(s string) error {
|
||||
*v = String(s)
|
||||
return nil
|
||||
}
|
||||
|
||||
func parseInt(s string, err *error) int {
|
||||
v, parseErr := strconv.ParseInt(s, 10, 64)
|
||||
if parseErr != nil {
|
||||
*err = parseErr
|
||||
}
|
||||
return int(v)
|
||||
}
|
||||
|
||||
var dateRegexps = []*regexp.Regexp{
|
||||
// yyyy[-mm[-dd]]
|
||||
regexp.MustCompile(`^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$`),
|
||||
// yyyy[mm[dd]]
|
||||
regexp.MustCompile(`^(\d{4})(?:(\d{2})(?:(\d{2}))?)?$`),
|
||||
}
|
||||
|
||||
func parseDateParts(s string) (year, month, day int, err error) {
|
||||
var parts []string
|
||||
for _, re := range dateRegexps {
|
||||
parts = re.FindStringSubmatch(s)
|
||||
if parts != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if parts == nil {
|
||||
err = fmt.Errorf("soap date: value %q is not in a recognized ISO8601 date format", s)
|
||||
return
|
||||
}
|
||||
|
||||
year = parseInt(parts[1], &err)
|
||||
month = 1
|
||||
day = 1
|
||||
if len(parts[2]) != 0 {
|
||||
month = parseInt(parts[2], &err)
|
||||
if len(parts[3]) != 0 {
|
||||
day = parseInt(parts[3], &err)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("soap date: %q: %v", s, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var timeRegexps = []*regexp.Regexp{
|
||||
// hh[:mm[:ss]]
|
||||
regexp.MustCompile(`^(\d{2})(?::(\d{2})(?::(\d{2}))?)?$`),
|
||||
// hh[mm[ss]]
|
||||
regexp.MustCompile(`^(\d{2})(?:(\d{2})(?:(\d{2}))?)?$`),
|
||||
}
|
||||
|
||||
func parseTimeParts(s string) (TimeOfDay, error) {
|
||||
var parts []string
|
||||
for _, re := range timeRegexps {
|
||||
parts = re.FindStringSubmatch(s)
|
||||
if parts != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
if parts == nil {
|
||||
return TimeOfDay{}, fmt.Errorf("soap time: value %q is not in ISO8601 time format", s)
|
||||
}
|
||||
|
||||
var err error
|
||||
var hour, minute, second int8
|
||||
hour = int8(parseInt(parts[1], &err))
|
||||
if len(parts[2]) != 0 {
|
||||
minute = int8(parseInt(parts[2], &err))
|
||||
if len(parts[3]) != 0 {
|
||||
second = int8(parseInt(parts[3], &err))
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return TimeOfDay{}, fmt.Errorf("soap time: %q: %v", s, err)
|
||||
}
|
||||
|
||||
return TimeOfDay{hour, minute, second}, nil
|
||||
}
|
||||
|
||||
// (+|-)hh[[:]mm]
|
||||
var timezoneRegexp = regexp.MustCompile(`^([+-])(\d{2})(?::?(\d{2}))?$`)
|
||||
|
||||
func parseTimezone(s string) (offset int, err error) {
|
||||
if s == "Z" {
|
||||
return 0, nil
|
||||
}
|
||||
parts := timezoneRegexp.FindStringSubmatch(s)
|
||||
if parts == nil {
|
||||
err = fmt.Errorf("soap timezone: value %q is not in ISO8601 timezone format", s)
|
||||
return
|
||||
}
|
||||
|
||||
offset = parseInt(parts[2], &err) * 3600
|
||||
if len(parts[3]) != 0 {
|
||||
offset += parseInt(parts[3], &err) * 60
|
||||
}
|
||||
if parts[1] == "-" {
|
||||
offset = -offset
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
err = fmt.Errorf("soap timezone: %q: %v", s, err)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
var completeDateTimeZoneRegexp = regexp.MustCompile(`^([^T]+)(?:T([^-+Z]+)(.+)?)?$`)
|
||||
|
||||
// splitCompleteDateTimeZone splits date, time and timezone apart from an
|
||||
// ISO8601 string. It does not ensure that the contents of each part are
|
||||
// correct, it merely splits on certain delimiters.
|
||||
// e.g "2010-09-08T12:15:10+0700" => "2010-09-08", "12:15:10", "+0700".
|
||||
// Timezone can only be present if time is also present.
|
||||
func splitCompleteDateTimeZone(s string) (dateStr, timeStr, zoneStr string, err error) {
|
||||
parts := completeDateTimeZoneRegexp.FindStringSubmatch(s)
|
||||
if parts == nil {
|
||||
err = fmt.Errorf("soap date/time/zone: value %q is not in ISO8601 datetime format", s)
|
||||
return
|
||||
}
|
||||
dateStr = parts[1]
|
||||
timeStr = parts[2]
|
||||
zoneStr = parts[3]
|
||||
return
|
||||
}
|
||||
|
||||
// TimeOfDay is used in cases where SOAP "time" or "time.tz" is used.
|
||||
// It contains non-timezone aware components.
|
||||
type TimeOfDay struct {
|
||||
Hour int8
|
||||
Minute int8
|
||||
Second int8
|
||||
}
|
||||
|
||||
var _ SOAPValue = &TimeOfDay{}
|
||||
|
||||
// Sets components based on duration since midnight.
|
||||
func (v *TimeOfDay) SetFromDuration(d time.Duration) error {
|
||||
if d < 0 || d > 24*time.Hour {
|
||||
return fmt.Errorf("out of range of SOAP time type: %v", d)
|
||||
}
|
||||
v.Hour = int8(d / time.Hour)
|
||||
d = d % time.Hour
|
||||
v.Minute = int8(d / time.Minute)
|
||||
d = d % time.Minute
|
||||
v.Second = int8(d / time.Second)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns duration since midnight.
|
||||
func (v *TimeOfDay) ToDuration() time.Duration {
|
||||
return time.Duration(v.Hour)*time.Hour +
|
||||
time.Duration(v.Minute)*time.Minute +
|
||||
time.Duration(v.Second)*time.Second
|
||||
}
|
||||
|
||||
func (v *TimeOfDay) String() string {
|
||||
return fmt.Sprintf("%02d:%02d:%02d", v.Hour, v.Minute, v.Second)
|
||||
}
|
||||
|
||||
func (v *TimeOfDay) Equal(o *TimeOfDay) bool {
|
||||
return v.Hour == o.Hour && v.Minute == o.Minute && v.Second == o.Second
|
||||
}
|
||||
|
||||
// IsValid returns true iff v is positive and <= 24 hours.
|
||||
// It allows equal to 24 hours as a special case as 24:00:00 is an allowed
|
||||
// value by the SOAP type.
|
||||
func (v *TimeOfDay) CheckValid() error {
|
||||
if (v.Hour < 0 || v.Minute < 0 || v.Second < 0) ||
|
||||
(v.Hour == 24 && (v.Minute > 0 || v.Second > 0)) ||
|
||||
v.Hour > 24 || v.Minute >= 60 || v.Second >= 60 {
|
||||
return fmt.Errorf("soap time: value %v has components(s) out of range", v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (v *TimeOfDay) Marshal() (string, error) {
|
||||
if err := v.CheckValid(); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *TimeOfDay) Unmarshal(s string) error {
|
||||
var err error
|
||||
*v, err = parseTimeParts(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return v.CheckValid()
|
||||
}
|
||||
|
||||
// TimeOfDayTZ is used in cases where SOAP "time.tz" is used.
|
||||
type TimeOfDayTZ struct {
|
||||
// Components of the time of day.
|
||||
TimeOfDay TimeOfDay
|
||||
|
||||
// Set to true if Offset is specified. If false, then the timezone is
|
||||
// unspecified (and by ISO8601 - implies some "local" time).
|
||||
HasOffset bool
|
||||
|
||||
// Offset is non-zero only if time.tz is used. It is otherwise ignored. If
|
||||
// non-zero, then it is regarded as a UTC offset in seconds. Note that the
|
||||
// sub-minutes is ignored by the marshal function.
|
||||
Offset int
|
||||
}
|
||||
|
||||
var _ SOAPValue = &TimeOfDayTZ{}
|
||||
|
||||
func (v *TimeOfDayTZ) String() string {
|
||||
return fmt.Sprintf("%v %t %+03d:%02d:%02d", v.TimeOfDay, v.HasOffset, v.Offset/3600, (v.Offset%3600)/60, v.Offset%60)
|
||||
}
|
||||
|
||||
func (v *TimeOfDayTZ) Equal(o *TimeOfDayTZ) bool {
|
||||
return v.TimeOfDay.Equal(&o.TimeOfDay) &&
|
||||
v.HasOffset == o.HasOffset && v.Offset == o.Offset
|
||||
}
|
||||
|
||||
func (v *TimeOfDayTZ) Marshal() (string, error) {
|
||||
tod, err := v.TimeOfDay.Marshal()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
tz := ""
|
||||
if v.HasOffset {
|
||||
if v.Offset == 0 {
|
||||
tz = "Z"
|
||||
} else {
|
||||
offsetMins := v.Offset / 60
|
||||
sign := '+'
|
||||
if offsetMins < 1 {
|
||||
offsetMins = -offsetMins
|
||||
sign = '-'
|
||||
}
|
||||
tz = fmt.Sprintf("%c%02d:%02d", sign, offsetMins/60, offsetMins%60)
|
||||
}
|
||||
}
|
||||
|
||||
return tod + tz, nil
|
||||
}
|
||||
|
||||
func (v *TimeOfDayTZ) Unmarshal(s string) error {
|
||||
zoneIndex := strings.IndexAny(s, "Z+-")
|
||||
var timePart string
|
||||
if zoneIndex == -1 {
|
||||
v.HasOffset = false
|
||||
v.Offset = 0
|
||||
timePart = s
|
||||
} else {
|
||||
v.HasOffset = true
|
||||
timePart = s[:zoneIndex]
|
||||
var err error
|
||||
v.Offset, err = parseTimezone(s[zoneIndex:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := v.TimeOfDay.Unmarshal(timePart); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// DateLocal maps time.Time to the SOAP "date" type. Dates map to midnight in
|
||||
// the local time zone. The time of day components are ignored when
|
||||
// marshalling.
|
||||
type DateLocal time.Time
|
||||
|
||||
var _ SOAPValue = &DateLocal{}
|
||||
|
||||
func NewDateLocal(v time.Time) *DateLocal {
|
||||
v2 := DateLocal(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v DateLocal) String() string {
|
||||
return v.ToTime().String()
|
||||
}
|
||||
|
||||
func (v DateLocal) ToTime() time.Time {
|
||||
return time.Time(v)
|
||||
}
|
||||
|
||||
func (v *DateLocal) Marshal() (string, error) {
|
||||
return time.Time(*v).In(localLoc).Format("2006-01-02"), nil
|
||||
}
|
||||
|
||||
func (v *DateLocal) Unmarshal(s string) error {
|
||||
year, month, day, err := parseDateParts(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = DateLocal(time.Date(year, time.Month(month), day, 0, 0, 0, 0, localLoc))
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalDateTime maps time.Time to SOAP "dateTime" type, with the local timezone.
|
||||
type DateTimeLocal time.Time
|
||||
|
||||
var _ SOAPValue = &DateTimeLocal{}
|
||||
|
||||
func NewDateTimeLocal(v time.Time) *DateTimeLocal {
|
||||
v2 := DateTimeLocal(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v DateTimeLocal) String() string {
|
||||
return v.ToTime().String()
|
||||
}
|
||||
|
||||
func (v DateTimeLocal) ToTime() time.Time {
|
||||
return time.Time(v)
|
||||
}
|
||||
|
||||
func (v *DateTimeLocal) Marshal() (string, error) {
|
||||
return v.ToTime().In(localLoc).Format("2006-01-02T15:04:05"), nil
|
||||
}
|
||||
|
||||
func (v *DateTimeLocal) Unmarshal(s string) error {
|
||||
dateStr, timeStr, zoneStr, err := splitCompleteDateTimeZone(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(zoneStr) != 0 {
|
||||
return fmt.Errorf("soap datetime: unexpected timezone in %q", s)
|
||||
}
|
||||
|
||||
year, month, day, err := parseDateParts(dateStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tod TimeOfDay
|
||||
if len(timeStr) != 0 {
|
||||
tod, err = parseTimeParts(timeStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
*v = DateTimeLocal(time.Date(year, time.Month(month), day,
|
||||
int(tod.Hour), int(tod.Minute), int(tod.Second), 0,
|
||||
localLoc))
|
||||
return nil
|
||||
}
|
||||
|
||||
// DateTimeLocal maps time.Time to SOAP "dateTime.tz" type, using the local
|
||||
// timezone when one is unspecified.
|
||||
type DateTimeTZLocal time.Time
|
||||
|
||||
var _ SOAPValue = &DateTimeTZLocal{}
|
||||
|
||||
func NewDateTimeTZLocal(v time.Time) *DateTimeTZLocal {
|
||||
v2 := DateTimeTZLocal(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v DateTimeTZLocal) String() string {
|
||||
return v.ToTime().String()
|
||||
}
|
||||
|
||||
func (v DateTimeTZLocal) ToTime() time.Time {
|
||||
return time.Time(v)
|
||||
}
|
||||
|
||||
func (v *DateTimeTZLocal) Marshal() (string, error) {
|
||||
return time.Time(*v).Format("2006-01-02T15:04:05-07:00"), nil
|
||||
}
|
||||
|
||||
func (v *DateTimeTZLocal) Unmarshal(s string) error {
|
||||
dateStr, timeStr, zoneStr, err := splitCompleteDateTimeZone(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
year, month, day, err := parseDateParts(dateStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var tod TimeOfDay
|
||||
var location *time.Location = localLoc
|
||||
if len(timeStr) != 0 {
|
||||
tod, err = parseTimeParts(timeStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(zoneStr) != 0 {
|
||||
var offset int
|
||||
offset, err = parseTimezone(zoneStr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if offset == 0 {
|
||||
location = time.UTC
|
||||
} else {
|
||||
location = time.FixedZone("", offset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*v = DateTimeTZLocal(time.Date(year, time.Month(month), day,
|
||||
int(tod.Hour), int(tod.Minute), int(tod.Second), 0,
|
||||
location))
|
||||
return nil
|
||||
}
|
||||
|
||||
type Boolean bool
|
||||
|
||||
var _ SOAPValue = new(Boolean)
|
||||
|
||||
func NewBoolean(v bool) *Boolean {
|
||||
v2 := Boolean(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *Boolean) String() string {
|
||||
if *v {
|
||||
return "true"
|
||||
}
|
||||
return "false"
|
||||
}
|
||||
|
||||
func (v *Boolean) Marshal() (string, error) {
|
||||
if *v {
|
||||
return "1", nil
|
||||
}
|
||||
return "0", nil
|
||||
}
|
||||
|
||||
func (v *Boolean) Unmarshal(s string) error {
|
||||
switch s {
|
||||
case "0", "false", "no":
|
||||
*v = false
|
||||
case "1", "true", "yes":
|
||||
*v = true
|
||||
default:
|
||||
return fmt.Errorf("soap boolean: %q is not a valid boolean value", s)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// BinBase64 maps []byte to SOAP "bin.base64" type.
|
||||
type BinBase64 []byte
|
||||
|
||||
var _ SOAPValue = new(BinBase64)
|
||||
|
||||
func NewBinBase64(v []byte) *BinBase64 {
|
||||
v2 := BinBase64(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *BinBase64) String() string {
|
||||
return base64.StdEncoding.EncodeToString(*v)
|
||||
}
|
||||
|
||||
func (v *BinBase64) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *BinBase64) Unmarshal(s string) error {
|
||||
v2, err := base64.StdEncoding.DecodeString(s)
|
||||
*v = v2
|
||||
return err
|
||||
}
|
||||
|
||||
// BinHex maps []byte to SOAP "bin.hex" type.
|
||||
type BinHex []byte
|
||||
|
||||
var _ SOAPValue = new(BinHex)
|
||||
|
||||
func NewBinHex(v []byte) *BinHex {
|
||||
v2 := BinHex(v)
|
||||
return &v2
|
||||
}
|
||||
|
||||
func (v *BinHex) String() string {
|
||||
return hex.EncodeToString(*v)
|
||||
}
|
||||
|
||||
func (v *BinHex) Marshal() (string, error) {
|
||||
return v.String(), nil
|
||||
}
|
||||
|
||||
func (v *BinHex) Unmarshal(s string) error {
|
||||
v2, err := hex.DecodeString(s)
|
||||
*v = v2
|
||||
return err
|
||||
}
|
||||
|
||||
// URI maps *url.URL to SOAP "uri" type.
|
||||
type URI url.URL
|
||||
|
||||
var _ SOAPValue = new(URI)
|
||||
|
||||
func (v *URI) String() string {
|
||||
return v.ToURL().String()
|
||||
}
|
||||
|
||||
func (v *URI) ToURL() *url.URL {
|
||||
return (*url.URL)(v)
|
||||
}
|
||||
|
||||
func (v *URI) Marshal() (string, error) {
|
||||
return (*url.URL)(v).String(), nil
|
||||
}
|
||||
|
||||
func (v *URI) Unmarshal(s string) error {
|
||||
v2, err := url.Parse(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*v = URI(*v2)
|
||||
return nil
|
||||
}
|
424
v2/soap/types/types_test.go
Normal file
424
v2/soap/types/types_test.go
Normal file
@ -0,0 +1,424 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
type isEqual func(got, want SOAPValue) bool
|
||||
|
||||
type typeTestCase struct {
|
||||
makeValue func() SOAPValue
|
||||
isEqual isEqual
|
||||
marshalTests []marshalCase
|
||||
marshalErrs []SOAPValue
|
||||
unmarshalTests []unmarshalCase
|
||||
unmarshalErrs []string
|
||||
}
|
||||
|
||||
type marshalCase struct {
|
||||
input SOAPValue
|
||||
want string
|
||||
}
|
||||
|
||||
type unmarshalCase struct {
|
||||
input string
|
||||
want SOAPValue
|
||||
}
|
||||
|
||||
func Test(t *testing.T) {
|
||||
// Fake out the local time for the implementation.
|
||||
localLoc = time.FixedZone("Fake/Local", 6*3600)
|
||||
defer func() {
|
||||
localLoc = time.Local
|
||||
}()
|
||||
|
||||
badNumbers := []string{"", " ", "abc"}
|
||||
|
||||
typeTestCases := []typeTestCase{
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(UI1) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*UI1) == *want.(*UI1) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewUI1(0), "0"},
|
||||
{NewUI1(1), "1"},
|
||||
{NewUI1(255), "255"},
|
||||
},
|
||||
unmarshalErrs: append([]string{"-1", "256"}, badNumbers...),
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(UI2) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*UI2) == *want.(*UI2) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewUI2(0), "0"},
|
||||
{NewUI2(1), "1"},
|
||||
{NewUI2(65535), "65535"},
|
||||
},
|
||||
unmarshalErrs: append([]string{"-1", "65536"}, badNumbers...),
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(UI4) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*UI4) == *want.(*UI4) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewUI4(0), "0"},
|
||||
{NewUI4(1), "1"},
|
||||
{NewUI4(4294967295), "4294967295"},
|
||||
},
|
||||
unmarshalErrs: append([]string{"-1", "4294967296"}, badNumbers...),
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(UI8) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*UI8) == *want.(*UI8) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewUI8(0), "0"},
|
||||
{NewUI8(1), "1"},
|
||||
{NewUI8(18446744073709551615), "18446744073709551615"},
|
||||
},
|
||||
unmarshalErrs: append([]string{"-1", "18446744073709551616"}, badNumbers...),
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(I1) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*I1) == *want.(*I1) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewI1(0), "0"},
|
||||
{NewI1(1), "1"},
|
||||
{NewI1(-1), "-1"},
|
||||
{NewI1(127), "127"},
|
||||
{NewI1(-128), "-128"},
|
||||
},
|
||||
unmarshalErrs: append([]string{"-129", "128"}, badNumbers...),
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(I2) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*I2) == *want.(*I2) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewI2(0), "0"},
|
||||
{NewI2(1), "1"},
|
||||
{NewI2(-1), "-1"},
|
||||
{NewI2(32767), "32767"},
|
||||
{NewI2(-32768), "-32768"},
|
||||
},
|
||||
unmarshalErrs: append([]string{"-32769", "32768"}, badNumbers...),
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(I4) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*I4) == *want.(*I4) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewI4(0), "0"},
|
||||
{NewI4(1), "1"},
|
||||
{NewI4(-1), "-1"},
|
||||
{NewI4(2147483647), "2147483647"},
|
||||
{NewI4(-2147483648), "-2147483648"},
|
||||
},
|
||||
unmarshalErrs: append([]string{"-2147483649", "2147483648"}, badNumbers...),
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(I8) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*I8) == *want.(*I8) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewI8(0), "0"},
|
||||
{NewI8(1), "1"},
|
||||
{NewI8(-1), "-1"},
|
||||
{NewI8(9223372036854775807), "9223372036854775807"},
|
||||
{NewI8(-9223372036854775808), "-9223372036854775808"},
|
||||
},
|
||||
unmarshalErrs: append([]string{"-9223372036854775809", "9223372036854775808"}, badNumbers...),
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(FloatFixed14_4) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*FloatFixed14_4) == *want.(*FloatFixed14_4) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewFloatFixed14_4(0), "0.0000"},
|
||||
{NewFloatFixed14_4(1), "1.0000"},
|
||||
{NewFloatFixed14_4(1.2346), "1.2346"},
|
||||
{NewFloatFixed14_4(-1), "-1.0000"},
|
||||
{NewFloatFixed14_4(-1.2346), "-1.2346"},
|
||||
{NewFloatFixed14_4(1e13), "10000000000000.0000"},
|
||||
{NewFloatFixed14_4(-1e13), "-10000000000000.0000"},
|
||||
},
|
||||
marshalErrs: []SOAPValue{
|
||||
NewFloatFixed14_4(1e14),
|
||||
NewFloatFixed14_4(-1e14),
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(Char) },
|
||||
isEqual: func(got, want SOAPValue) bool { return *got.(*Char) == *want.(*Char) },
|
||||
marshalTests: []marshalCase{
|
||||
{NewChar('a'), "a"},
|
||||
{NewChar('z'), "z"},
|
||||
{NewChar('\u1234'), "\u1234"},
|
||||
},
|
||||
marshalErrs: []SOAPValue{NewChar(0)},
|
||||
unmarshalErrs: []string{"aa", ""},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(DateLocal) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return got.(*DateLocal).ToTime().Equal(want.(*DateLocal).ToTime())
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{NewDateLocal(time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)), "2013-10-08"},
|
||||
},
|
||||
unmarshalTests: []unmarshalCase{
|
||||
{"20131008", NewDateLocal(time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc))},
|
||||
},
|
||||
unmarshalErrs: []string{"", "-1"},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(TimeOfDay) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return got.(*TimeOfDay).Equal(want.(*TimeOfDay))
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{&TimeOfDay{}, "00:00:00"},
|
||||
// ISO8601 special case
|
||||
{&TimeOfDay{Hour: 24}, "24:00:00"},
|
||||
},
|
||||
unmarshalTests: []unmarshalCase{
|
||||
{"000000", &TimeOfDay{}},
|
||||
},
|
||||
unmarshalErrs: []string{
|
||||
// Misformatted values:
|
||||
"foo 01:02:03", "foo\n01:02:03", "01:02:03 foo", "01:02:03\nfoo", "01:02:03Z",
|
||||
"01:02:03+01", "01:02:03+01:23", "01:02:03+0123", "01:02:03-01", "01:02:03-01:23",
|
||||
"01:02:03-0123",
|
||||
// Values out of range:
|
||||
"24:01:00",
|
||||
"24:00:01",
|
||||
"25:00:00",
|
||||
"00:60:00",
|
||||
"00:00:60",
|
||||
// Unexpected timezone component:
|
||||
"01:02:03Z",
|
||||
"01:02:03+01",
|
||||
"01:02:03+01:23",
|
||||
"01:02:03+0123",
|
||||
"01:02:03-01",
|
||||
"01:02:03-01:23",
|
||||
"01:02:03-0123",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(TimeOfDayTZ) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return got.(*TimeOfDayTZ).Equal(want.(*TimeOfDayTZ))
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{&TimeOfDayTZ{}, "00:00:00"},
|
||||
// ISO8601 special case
|
||||
{&TimeOfDayTZ{TimeOfDay{24, 0, 0}, false, 0}, "24:00:00"},
|
||||
{&TimeOfDayTZ{TimeOfDay{1, 2, 3}, true, 0}, "01:02:03Z"},
|
||||
{&TimeOfDayTZ{TimeOfDay{1, 2, 3}, true, 3600 + 23*60}, "01:02:03+01:23"},
|
||||
{&TimeOfDayTZ{TimeOfDay{1, 2, 3}, true, -(3600 + 23*60)}, "01:02:03-01:23"},
|
||||
},
|
||||
unmarshalTests: []unmarshalCase{
|
||||
{"000000", &TimeOfDayTZ{}},
|
||||
{"01Z", &TimeOfDayTZ{TimeOfDay{1, 0, 0}, true, 0}},
|
||||
{"01+01", &TimeOfDayTZ{TimeOfDay{1, 0, 0}, true, 3600}},
|
||||
{"01:02:03+01", &TimeOfDayTZ{TimeOfDay{1, 2, 3}, true, 3600}},
|
||||
{"01:02:03+0123", &TimeOfDayTZ{TimeOfDay{1, 2, 3}, true, 3600 + 23*60}},
|
||||
{"01:02:03-01", &TimeOfDayTZ{TimeOfDay{1, 2, 3}, true, -3600}},
|
||||
{"01:02:03-0123", &TimeOfDayTZ{TimeOfDay{1, 2, 3}, true, -(3600 + 23*60)}},
|
||||
},
|
||||
unmarshalErrs: []string{
|
||||
// Misformatted values:
|
||||
"foo 01:02:03", "foo\n01:02:03", "01:02:03 foo", "01:02:03\nfoo",
|
||||
// Values out of range:
|
||||
"24:01:00",
|
||||
"24:00:01",
|
||||
"25:00:00",
|
||||
"00:60:00",
|
||||
"00:00:60",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(DateLocal) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return got.(*DateLocal).ToTime().Equal(want.(*DateLocal).ToTime())
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{NewDateLocal(time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)), "2013-10-08"},
|
||||
},
|
||||
unmarshalTests: []unmarshalCase{
|
||||
{"20131008", NewDateLocal(time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc))},
|
||||
},
|
||||
unmarshalErrs: []string{
|
||||
// Unexpected time component.
|
||||
"2013-10-08T10:30:50",
|
||||
// Unexpected timezone component.
|
||||
"2013-10-08+01",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(DateTimeLocal) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return got.(*DateTimeLocal).ToTime().Equal(want.(*DateTimeLocal).ToTime())
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{NewDateTimeLocal(time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)), "2013-10-08T00:00:00"},
|
||||
{NewDateTimeLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, localLoc)), "2013-10-08T10:30:50"},
|
||||
},
|
||||
unmarshalTests: []unmarshalCase{
|
||||
{"20131008", NewDateTimeLocal(time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc))},
|
||||
},
|
||||
unmarshalErrs: []string{
|
||||
// Unexpected timezone component.
|
||||
"2013-10-08T10:30:50+01",
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(DateTimeTZLocal) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return got.(*DateTimeTZLocal).ToTime().Equal(want.(*DateTimeTZLocal).ToTime())
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{NewDateTimeTZLocal(time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc)), "2013-10-08T00:00:00+06:00"},
|
||||
{NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, localLoc)), "2013-10-08T10:30:50+06:00"},
|
||||
{NewDateTimeTZLocal(time.Date(2013, 10, 8, 0, 0, 0, 0, time.UTC)), "2013-10-08T00:00:00+00:00"},
|
||||
{NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, time.UTC)), "2013-10-08T10:30:50+00:00"},
|
||||
{NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("+01:23", 3600+23*60))), "2013-10-08T10:30:50+01:23"},
|
||||
{NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("-01:23", -(3600+23*60)))), "2013-10-08T10:30:50-01:23"},
|
||||
},
|
||||
unmarshalTests: []unmarshalCase{
|
||||
{"20131008", NewDateTimeTZLocal(time.Date(2013, 10, 8, 0, 0, 0, 0, localLoc))},
|
||||
{"2013-10-08T10:30:50", NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, localLoc))},
|
||||
{"2013-10-08T10:30:50Z", NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, time.UTC))},
|
||||
{"2013-10-08T10:30:50+01", NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("+01:00", 3600)))},
|
||||
{"2013-10-08T10:30:50+0123", NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("+01:23", 3600+23*60)))},
|
||||
{"2013-10-08T10:30:50-01", NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("-01:00", -3600)))},
|
||||
{"2013-10-08T10:30:50-0123", NewDateTimeTZLocal(time.Date(2013, 10, 8, 10, 30, 50, 0, time.FixedZone("-01:23", -(3600+23*60))))},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(Boolean) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return *got.(*Boolean) == *want.(*Boolean)
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{NewBoolean(true), "1"},
|
||||
{NewBoolean(false), "0"},
|
||||
},
|
||||
unmarshalTests: []unmarshalCase{
|
||||
{"true", NewBoolean(true)},
|
||||
{"false", NewBoolean(false)},
|
||||
{"yes", NewBoolean(true)},
|
||||
{"no", NewBoolean(false)},
|
||||
},
|
||||
unmarshalErrs: []string{"", "2", "-1"},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(BinBase64) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return bytes.Equal(*got.(*BinBase64), *want.(*BinBase64))
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{&BinBase64{}, ""},
|
||||
{NewBinBase64([]byte("a")), "YQ=="},
|
||||
{NewBinBase64([]byte("Longer String.")), "TG9uZ2VyIFN0cmluZy4="},
|
||||
{NewBinBase64([]byte("Longer Aligned.")), "TG9uZ2VyIEFsaWduZWQu"},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(BinHex) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return bytes.Equal(*got.(*BinHex), *want.(*BinHex))
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{&BinHex{}, ""},
|
||||
{NewBinHex([]byte("a")), "61"},
|
||||
{NewBinHex([]byte("Longer String.")), "4c6f6e67657220537472696e672e"},
|
||||
},
|
||||
unmarshalTests: []unmarshalCase{
|
||||
{"4C6F6E67657220537472696E672E", NewBinHex([]byte("Longer String."))},
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
makeValue: func() SOAPValue { return new(URI) },
|
||||
isEqual: func(got, want SOAPValue) bool {
|
||||
return got.(*URI).ToURL().String() == want.(*URI).ToURL().String()
|
||||
},
|
||||
marshalTests: []marshalCase{
|
||||
{&URI{Scheme: "http", Host: "example.com", Path: "/path"}, "http://example.com/path"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range typeTestCases {
|
||||
tt := tt
|
||||
|
||||
// Convert marshalTests into additional round trip equivalent unmarshalTests
|
||||
for _, mt := range tt.marshalTests {
|
||||
tt.unmarshalTests = append(tt.unmarshalTests, unmarshalCase{
|
||||
input: mt.want,
|
||||
want: mt.input,
|
||||
})
|
||||
}
|
||||
|
||||
t.Run(fmt.Sprintf("%T", tt.makeValue()), func(t *testing.T) {
|
||||
for i, mt := range tt.marshalTests {
|
||||
mt := mt
|
||||
t.Run(fmt.Sprintf("marshalTest#%d_%v", i, mt.input), func(t *testing.T) {
|
||||
got, err := mt.input.Marshal()
|
||||
if err != nil {
|
||||
t.Errorf("got unexpected error: %v", err)
|
||||
}
|
||||
if got != mt.want {
|
||||
t.Errorf("got %q, want: %q", got, mt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
for i, input := range tt.marshalErrs {
|
||||
input := input
|
||||
t.Run(fmt.Sprintf("marshalErr#%d_%v", i, input), func(t *testing.T) {
|
||||
got, err := input.Marshal()
|
||||
if err == nil {
|
||||
t.Errorf("got %q, want error", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
for i, ut := range tt.unmarshalTests {
|
||||
ut := ut
|
||||
t.Run(fmt.Sprintf("unmarshalTest#%d_%q", i, ut.input), func(t *testing.T) {
|
||||
got := tt.makeValue()
|
||||
if err := got.Unmarshal(ut.input); err != nil {
|
||||
t.Errorf("got error, want success")
|
||||
}
|
||||
if !tt.isEqual(got, ut.want) {
|
||||
t.Errorf("got %v, want %v", got, ut.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
for i, input := range tt.unmarshalErrs {
|
||||
input := input
|
||||
t.Run(fmt.Sprintf("unmarshalErrs#%d_%q", i, input), func(t *testing.T) {
|
||||
got := tt.makeValue()
|
||||
if err := got.Unmarshal(input); err == nil {
|
||||
t.Errorf("got %v, want error", got)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user