Implement error-free marshalText methods.

This commit is contained in:
John Beisley 2021-09-05 12:40:35 +01:00 committed by Huin
parent 6183f45568
commit e043d6666b

View File

@ -29,24 +29,13 @@ type UI1 uint8
var _ SOAPValue = new(UI1)
// toStringNoError converts `v` to a string, returning empty string on error.
// This should only be used for String() implementations if no error can be
// returned by v.MarshalText().
func toStringNoError(v encoding.TextMarshaler) string {
b, err := v.MarshalText()
if err != nil {
return ""
}
return string(b)
}
func NewUI1(v uint8) *UI1 {
v2 := UI1(v)
return &v2
}
func (v *UI1) String() string {
return toStringNoError(v)
return strconv.FormatUint(uint64(*v), 10)
}
func (v *UI1) MarshalText() ([]byte, error) {
@ -69,7 +58,7 @@ func NewUI2(v uint16) *UI2 {
}
func (v *UI2) String() string {
return toStringNoError(v)
return strconv.FormatUint(uint64(*v), 10)
}
func (v *UI2) MarshalText() ([]byte, error) {
@ -92,7 +81,7 @@ func NewUI4(v uint32) *UI4 {
}
func (v *UI4) String() string {
return toStringNoError(v)
return strconv.FormatUint(uint64(*v), 10)
}
func (v *UI4) MarshalText() ([]byte, error) {
@ -115,7 +104,7 @@ func NewUI8(v uint64) *UI8 {
}
func (v *UI8) String() string {
return toStringNoError(v)
return strconv.FormatUint(uint64(*v), 10)
}
func (v *UI8) MarshalText() ([]byte, error) {
@ -138,7 +127,7 @@ func NewI1(v int8) *I1 {
}
func (v *I1) String() string {
return toStringNoError(v)
return strconv.FormatInt(int64(*v), 10)
}
func (v *I1) MarshalText() ([]byte, error) {
@ -161,7 +150,7 @@ func NewI2(v int16) *I2 {
}
func (v *I2) String() string {
return toStringNoError(v)
return strconv.FormatInt(int64(*v), 10)
}
func (v *I2) MarshalText() ([]byte, error) {
@ -184,7 +173,7 @@ func NewI4(v int32) *I4 {
}
func (v *I4) String() string {
return toStringNoError(v)
return strconv.FormatInt(int64(*v), 10)
}
func (v *I4) MarshalText() ([]byte, error) {
@ -207,7 +196,7 @@ func NewI8(v int64) *I8 {
}
func (v *I8) String() string {
return toStringNoError(v)
return strconv.FormatInt(int64(*v), 10)
}
func (v *I8) MarshalText() ([]byte, error) {
@ -230,11 +219,15 @@ func NewR4(v float32) *R4 {
}
func (v *R4) String() string {
return toStringNoError(v)
return string(v.marshalText(nil))
}
func (v *R4) marshalText(b []byte) []byte {
return strconv.AppendFloat(b, float64(*v), 'g', -1, 32)
}
func (v *R4) MarshalText() ([]byte, error) {
return strconv.AppendFloat(nil, float64(*v), 'g', -1, 32), nil
return v.marshalText(nil), nil
}
func (v *R4) UnmarshalText(b []byte) error {
@ -253,11 +246,15 @@ func NewR8(v float64) *R8 {
}
func (v *R8) String() string {
return toStringNoError(v)
return string(v.marshalText(nil))
}
func (v *R8) marshalText(b []byte) []byte {
return strconv.AppendFloat(nil, float64(*v), 'g', -1, 64)
}
func (v *R8) MarshalText() ([]byte, error) {
return strconv.AppendFloat(nil, float64(*v), 'g', -1, 64), nil
return v.marshalText(nil), nil
}
func (v *R8) UnmarshalText(b []byte) error {
@ -360,15 +357,22 @@ func (v Fixed14_4) Float64() float64 {
}
func (v *Fixed14_4) String() string {
return toStringNoError(v)
return string(v.marshalText(nil))
}
func (v *Fixed14_4) MarshalText() ([]byte, error) {
func (v *Fixed14_4) marshalText(b []byte) []byte {
intPart, fracPart := v.Parts()
if fracPart < 0 {
fracPart = -fracPart
}
return []byte(fmt.Sprintf("%d.%04d", intPart, fracPart)), nil
b = strconv.AppendInt(b, intPart, 10)
b = append(b, '.')
b = appendInt(b, int64(fracPart), 4)
return b
}
func (v *Fixed14_4) MarshalText() ([]byte, error) {
return v.marshalText(nil), nil
}
var decimalByte = []byte{'.'}
@ -482,6 +486,32 @@ func parseInt(b []byte, err *error) int {
return int(v)
}
var zeroDigits []byte = []byte("000")
// appendInt appends `n` in decimal to `b`, with up to 3 digits of
// zero-padding to fill to a minimum of 4 digits.
func appendInt(b []byte, n int64, padding int) []byte {
if n > -1000 && n < 1000 {
if n < 0 {
n = -n
b = append(b, '-')
}
var digits int
if n < 10 {
digits = 1
} else if n < 100 {
digits = 2
} else if n < 1000 {
digits = 3
}
numZeros := padding - digits
if numZeros > 0 {
b = append(b, zeroDigits[:numZeros]...)
}
}
return strconv.AppendInt(b, n, 10)
}
var dateRegexps = []*regexp.Regexp{
// yyyy[-mm[-dd]]
regexp.MustCompile(`^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$`),
@ -551,7 +581,7 @@ func (tod TimeOfDay) ToDuration() time.Duration {
}
func (tod *TimeOfDay) String() string {
return toStringNoError(tod)
return string(tod.marshalText(nil))
}
// IsValid returns true iff v is positive and <= 24 hours.
@ -573,11 +603,20 @@ func (tod *TimeOfDay) clear() {
tod.Second = 0
}
func (tod *TimeOfDay) marshalText(b []byte) []byte {
b = appendInt(b, int64(tod.Hour), 2)
b = append(b, ':')
b = appendInt(b, int64(tod.Minute), 2)
b = append(b, ':')
b = appendInt(b, int64(tod.Second), 2)
return b
}
func (tod *TimeOfDay) MarshalText() ([]byte, error) {
if err := tod.CheckValid(); err != nil {
return nil, err
}
return []byte(fmt.Sprintf("%02d:%02d:%02d", tod.Hour, tod.Minute, tod.Second)), nil
return tod.marshalText(nil), nil
}
var timeRegexps = []*regexp.Regexp{
@ -623,7 +662,7 @@ type TimeOfDayTZ struct {
var _ SOAPValue = &TimeOfDayTZ{}
func (todz *TimeOfDayTZ) String() string {
return toStringNoError(todz)
return string(todz.TZ.marshalText(nil))
}
// clear removes data from v, setting to default values.
@ -633,12 +672,9 @@ func (todz *TimeOfDayTZ) clear() {
}
func (todz *TimeOfDayTZ) MarshalText() ([]byte, error) {
result, err := todz.TimeOfDay.MarshalText()
if err != nil {
return nil, err
}
result = append(result, []byte(todz.TZ.String())...)
return result, nil
b := todz.TimeOfDay.marshalText(nil)
b = todz.TZ.marshalText(b)
return b, nil
}
func (todz *TimeOfDayTZ) UnmarshalText(b []byte) error {
@ -679,7 +715,7 @@ func (d Date) ToTime(loc *time.Location) time.Time {
}
func (d *Date) String() string {
return toStringNoError(d)
return string(d.marshalText(nil))
}
// CheckValid returns an error if the date components are out of range.
@ -698,8 +734,17 @@ func (d *Date) clear() {
d.Day = 0
}
func (d *Date) marshalText(b []byte) []byte {
b = appendInt(b, int64(d.Year), 2)
b = append(b, '-')
b = appendInt(b, int64(d.Month), 2)
b = append(b, '-')
b = appendInt(b, int64(d.Day), 2)
return b
}
func (d *Date) MarshalText() ([]byte, error) {
return []byte(fmt.Sprintf("%04d-%02d-%02d", d.Year, d.Month, d.Day)), nil
return d.marshalText(nil), nil
}
func (d *Date) UnmarshalText(b []byte) error {
@ -749,7 +794,7 @@ func DateTimeFromTime(v time.Time) DateTime {
}
func (dt *DateTime) String() string {
return toStringNoError(dt)
return string(dt.marshalText(nil))
}
func (dt DateTime) ToTime(loc *time.Location) time.Time {
@ -764,20 +809,15 @@ func (dt *DateTime) clear() {
dt.TimeOfDay.clear()
}
func (dt *DateTime) marshalText(b []byte) []byte {
b = dt.Date.marshalText(b)
b = append(b, 'T')
b = dt.TimeOfDay.marshalText(b)
return b
}
func (dt *DateTime) MarshalText() ([]byte, error) {
var result []byte
d, err := dt.Date.MarshalText()
if err != nil {
return nil, err
}
t, err := dt.TimeOfDay.MarshalText()
if err != nil {
return nil, err
}
result = append(result, d...)
result = append(result, 'T')
result = append(result, t...)
return result, nil
return dt.marshalText(nil), nil
}
func (dt *DateTime) UnmarshalText(b []byte) error {
@ -816,7 +856,7 @@ func DateTimeTZFromTime(t time.Time) DateTimeTZ {
}
func (dtz *DateTimeTZ) String() string {
return toStringNoError(dtz)
return string(dtz.marshalText(nil))
}
// Time converts `dtz` to time.Time, using defaultLoc as the default location if
@ -841,21 +881,16 @@ func (dtz *DateTimeTZ) clear() {
dtz.TZ.clear()
}
func (dtz *DateTimeTZ) marshalText(b []byte) []byte {
b = dtz.Date.marshalText(b)
b = append(b, 'T')
b = dtz.TimeOfDay.marshalText(b)
b = dtz.TZ.marshalText(b)
return b
}
func (dtz *DateTimeTZ) MarshalText() ([]byte, error) {
var result []byte
d, err := dtz.Date.MarshalText()
if err != nil {
return nil, err
}
t, err := dtz.TimeOfDay.MarshalText()
if err != nil {
return nil, err
}
result = append(result, d...)
result = append(result, 'T')
result = append(result, t...)
result = append(result, []byte(dtz.TZ.String())...)
return result, nil
return dtz.marshalText(nil), nil
}
func (dtz *DateTimeTZ) UnmarshalText(b []byte) error {
@ -921,23 +956,8 @@ func (tzd TZD) Location(defaultLoc *time.Location) *time.Location {
return time.FixedZone(tzd.String(), tzd.Offset)
}
func (tzd TZD) String() string {
if !tzd.HasTZ {
return ""
}
if tzd.Offset == 0 {
return "Z"
}
offsetMins := tzd.Offset / 60
sign := '+'
if offsetMins < 1 {
offsetMins = -offsetMins
sign = '-'
}
h, m := offsetMins/60, offsetMins%60
return fmt.Sprintf("%c%02d:%02d", sign, h, m)
func (tzd *TZD) String() string {
return string(tzd.marshalText(nil))
}
// clear removes offset information from `v`.
@ -946,6 +966,30 @@ func (tzd *TZD) clear() {
tzd.HasTZ = false
}
func (tzd *TZD) marshalText(b []byte) []byte {
if !tzd.HasTZ {
return b
}
if tzd.Offset == 0 {
b = append(b, 'Z')
return b
}
offsetMins := tzd.Offset / 60
var sign byte = '+'
if offsetMins < 1 {
offsetMins = -offsetMins
sign = '-'
}
h, m := offsetMins/60, offsetMins%60
b = append(b, sign)
b = appendInt(b, int64(h), 2)
b = append(b, ':')
b = appendInt(b, int64(m), 2)
return b
}
// (+|-)(hh):(mm)
var timezoneRegexp = regexp.MustCompile(`^([+-])(\d{2}):(\d{2})$`)