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