2016-03-23 20:53:09 +01:00
// Copyright 2014 Alvaro J. Genial. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package form
import (
2016-05-04 17:48:12 +02:00
"encoding"
"errors"
"fmt"
"io"
"net/url"
"reflect"
"strconv"
"strings"
"time"
2016-03-23 20:53:09 +01:00
)
2017-04-18 20:30:54 +02:00
// NewEncoder returns a new form Encoder.
func NewEncoder ( w io . Writer ) * Encoder {
return & Encoder { w , defaultDelimiter , defaultEscape , false }
2016-03-23 20:53:09 +01:00
}
2017-04-18 20:30:54 +02:00
// Encoder provides a way to encode to a Writer.
type Encoder struct {
2016-05-04 17:48:12 +02:00
w io . Writer
2016-07-21 21:10:33 +02:00
d rune
e rune
2017-04-18 20:30:54 +02:00
z bool
2016-07-21 21:10:33 +02:00
}
2017-04-18 20:30:54 +02:00
// DelimitWith sets r as the delimiter used for composite keys by Encoder e and returns the latter; it is '.' by default.
func ( e * Encoder ) DelimitWith ( r rune ) * Encoder {
2016-07-21 21:10:33 +02:00
e . d = r
return e
}
2017-04-18 20:30:54 +02:00
// EscapeWith sets r as the escape used for delimiters (and to escape itself) by Encoder e and returns the latter; it is '\\' by default.
func ( e * Encoder ) EscapeWith ( r rune ) * Encoder {
2016-07-21 21:10:33 +02:00
e . e = r
return e
2016-03-23 20:53:09 +01:00
}
2017-04-18 20:30:54 +02:00
// KeepZeros sets whether Encoder e should keep zero (default) values in their literal form when encoding, and returns the former; by default zero values are not kept, but are rather encoded as the empty string.
func ( e * Encoder ) KeepZeros ( z bool ) * Encoder {
e . z = z
return e
}
// Encode encodes dst as form and writes it out using the Encoder's Writer.
func ( e Encoder ) Encode ( dst interface { } ) error {
2016-05-04 17:48:12 +02:00
v := reflect . ValueOf ( dst )
2017-04-18 20:30:54 +02:00
n , err := encodeToNode ( v , e . z )
2016-05-04 17:48:12 +02:00
if err != nil {
return err
}
2016-07-21 21:10:33 +02:00
s := n . values ( e . d , e . e ) . Encode ( )
2016-05-04 17:48:12 +02:00
l , err := io . WriteString ( e . w , s )
switch {
case err != nil :
return err
case l != len ( s ) :
return errors . New ( "could not write data completely" )
}
return nil
2016-03-23 20:53:09 +01:00
}
// EncodeToString encodes dst as a form and returns it as a string.
func EncodeToString ( dst interface { } ) ( string , error ) {
2016-05-04 17:48:12 +02:00
v := reflect . ValueOf ( dst )
2017-04-18 20:30:54 +02:00
n , err := encodeToNode ( v , false )
2016-05-04 17:48:12 +02:00
if err != nil {
return "" , err
}
2016-07-21 21:10:33 +02:00
vs := n . values ( defaultDelimiter , defaultEscape )
return vs . Encode ( ) , nil
2016-03-23 20:53:09 +01:00
}
// EncodeToValues encodes dst as a form and returns it as Values.
func EncodeToValues ( dst interface { } ) ( url . Values , error ) {
2016-05-04 17:48:12 +02:00
v := reflect . ValueOf ( dst )
2017-04-18 20:30:54 +02:00
n , err := encodeToNode ( v , false )
2016-05-04 17:48:12 +02:00
if err != nil {
return nil , err
}
2016-07-21 21:10:33 +02:00
vs := n . values ( defaultDelimiter , defaultEscape )
return vs , nil
2016-03-23 20:53:09 +01:00
}
2017-04-18 20:30:54 +02:00
func encodeToNode ( v reflect . Value , z bool ) ( n node , err error ) {
2016-05-04 17:48:12 +02:00
defer func ( ) {
if e := recover ( ) ; e != nil {
err = fmt . Errorf ( "%v" , e )
}
} ( )
2017-04-18 20:30:54 +02:00
return getNode ( encodeValue ( v , z ) ) , nil
2016-03-23 20:53:09 +01:00
}
2017-04-18 20:30:54 +02:00
func encodeValue ( v reflect . Value , z bool ) interface { } {
2016-05-04 17:48:12 +02:00
t := v . Type ( )
k := v . Kind ( )
if s , ok := marshalValue ( v ) ; ok {
return s
2017-04-18 20:30:54 +02:00
} else if ! z && isEmptyValue ( v ) {
2016-05-04 17:48:12 +02:00
return "" // Treat the zero value as the empty string.
}
switch k {
case reflect . Ptr , reflect . Interface :
2017-04-18 20:30:54 +02:00
return encodeValue ( v . Elem ( ) , z )
2016-05-04 17:48:12 +02:00
case reflect . Struct :
if t . ConvertibleTo ( timeType ) {
return encodeTime ( v )
} else if t . ConvertibleTo ( urlType ) {
return encodeURL ( v )
}
2017-04-18 20:30:54 +02:00
return encodeStruct ( v , z )
2016-05-04 17:48:12 +02:00
case reflect . Slice :
2017-04-18 20:30:54 +02:00
return encodeSlice ( v , z )
2016-05-04 17:48:12 +02:00
case reflect . Array :
2017-04-18 20:30:54 +02:00
return encodeArray ( v , z )
2016-05-04 17:48:12 +02:00
case reflect . Map :
2017-04-18 20:30:54 +02:00
return encodeMap ( v , z )
2016-05-04 17:48:12 +02:00
case reflect . Invalid , reflect . Uintptr , reflect . UnsafePointer , reflect . Chan , reflect . Func :
panic ( t . String ( ) + " has unsupported kind " + t . Kind ( ) . String ( ) )
default :
return encodeBasic ( v )
}
2016-03-23 20:53:09 +01:00
}
2017-04-18 20:30:54 +02:00
func encodeStruct ( v reflect . Value , z bool ) interface { } {
2016-05-04 17:48:12 +02:00
t := v . Type ( )
n := node { }
for i := 0 ; i < t . NumField ( ) ; i ++ {
f := t . Field ( i )
k , oe := fieldInfo ( f )
if k == "-" {
continue
} else if fv := v . Field ( i ) ; oe && isEmptyValue ( fv ) {
delete ( n , k )
} else {
2017-04-18 20:30:54 +02:00
n [ k ] = encodeValue ( fv , z )
2016-05-04 17:48:12 +02:00
}
}
return n
2016-03-23 20:53:09 +01:00
}
2017-04-18 20:30:54 +02:00
func encodeMap ( v reflect . Value , z bool ) interface { } {
2016-05-04 17:48:12 +02:00
n := node { }
for _ , i := range v . MapKeys ( ) {
2017-04-18 20:30:54 +02:00
k := getString ( encodeValue ( i , z ) )
n [ k ] = encodeValue ( v . MapIndex ( i ) , z )
2016-05-04 17:48:12 +02:00
}
return n
2016-03-23 20:53:09 +01:00
}
2017-04-18 20:30:54 +02:00
func encodeArray ( v reflect . Value , z bool ) interface { } {
2016-05-04 17:48:12 +02:00
n := node { }
for i := 0 ; i < v . Len ( ) ; i ++ {
2017-04-18 20:30:54 +02:00
n [ strconv . Itoa ( i ) ] = encodeValue ( v . Index ( i ) , z )
2016-05-04 17:48:12 +02:00
}
return n
2016-03-23 20:53:09 +01:00
}
2017-04-18 20:30:54 +02:00
func encodeSlice ( v reflect . Value , z bool ) interface { } {
2016-05-04 17:48:12 +02:00
t := v . Type ( )
if t . Elem ( ) . Kind ( ) == reflect . Uint8 {
return string ( v . Bytes ( ) ) // Encode byte slices as a single string by default.
}
n := node { }
for i := 0 ; i < v . Len ( ) ; i ++ {
2017-04-18 20:30:54 +02:00
n [ strconv . Itoa ( i ) ] = encodeValue ( v . Index ( i ) , z )
2016-05-04 17:48:12 +02:00
}
return n
2016-03-23 20:53:09 +01:00
}
func encodeTime ( v reflect . Value ) string {
2016-05-04 17:48:12 +02:00
t := v . Convert ( timeType ) . Interface ( ) . ( time . Time )
if t . Year ( ) == 0 && ( t . Month ( ) == 0 || t . Month ( ) == 1 ) && ( t . Day ( ) == 0 || t . Day ( ) == 1 ) {
return t . Format ( "15:04:05.999999999Z07:00" )
} else if t . Hour ( ) == 0 && t . Minute ( ) == 0 && t . Second ( ) == 0 && t . Nanosecond ( ) == 0 {
return t . Format ( "2006-01-02" )
}
return t . Format ( "2006-01-02T15:04:05.999999999Z07:00" )
2016-03-23 20:53:09 +01:00
}
func encodeURL ( v reflect . Value ) string {
2016-05-04 17:48:12 +02:00
u := v . Convert ( urlType ) . Interface ( ) . ( url . URL )
return u . String ( )
2016-03-23 20:53:09 +01:00
}
func encodeBasic ( v reflect . Value ) string {
2016-05-04 17:48:12 +02:00
t := v . Type ( )
switch k := t . Kind ( ) ; k {
case reflect . Bool :
return strconv . FormatBool ( v . Bool ( ) )
case reflect . Int ,
reflect . Int8 ,
reflect . Int16 ,
reflect . Int32 ,
reflect . Int64 :
return strconv . FormatInt ( v . Int ( ) , 10 )
case reflect . Uint ,
reflect . Uint8 ,
reflect . Uint16 ,
reflect . Uint32 ,
reflect . Uint64 :
return strconv . FormatUint ( v . Uint ( ) , 10 )
case reflect . Float32 :
return strconv . FormatFloat ( v . Float ( ) , 'g' , - 1 , 32 )
case reflect . Float64 :
return strconv . FormatFloat ( v . Float ( ) , 'g' , - 1 , 64 )
case reflect . Complex64 , reflect . Complex128 :
s := fmt . Sprintf ( "%g" , v . Complex ( ) )
return strings . TrimSuffix ( strings . TrimPrefix ( s , "(" ) , ")" )
case reflect . String :
return v . String ( )
}
panic ( t . String ( ) + " has unsupported kind " + t . Kind ( ) . String ( ) )
2016-03-23 20:53:09 +01:00
}
func isEmptyValue ( v reflect . Value ) bool {
2016-05-04 17:48:12 +02:00
switch t := v . Type ( ) ; v . Kind ( ) {
case reflect . Array , reflect . Map , reflect . Slice , reflect . String :
return v . Len ( ) == 0
case reflect . Bool :
return ! v . Bool ( )
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
return v . Int ( ) == 0
case reflect . Uint , reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uintptr :
return v . Uint ( ) == 0
case reflect . Float32 , reflect . Float64 :
return v . Float ( ) == 0
case reflect . Complex64 , reflect . Complex128 :
return v . Complex ( ) == 0
case reflect . Interface , reflect . Ptr :
return v . IsNil ( )
case reflect . Struct :
if t . ConvertibleTo ( timeType ) {
return v . Convert ( timeType ) . Interface ( ) . ( time . Time ) . IsZero ( )
}
return reflect . DeepEqual ( v , reflect . Zero ( t ) )
}
return false
2016-03-23 20:53:09 +01:00
}
// canIndexOrdinally returns whether a value contains an ordered sequence of elements.
func canIndexOrdinally ( v reflect . Value ) bool {
2016-05-04 17:48:12 +02:00
if ! v . IsValid ( ) {
return false
}
switch t := v . Type ( ) ; t . Kind ( ) {
case reflect . Ptr , reflect . Interface :
return canIndexOrdinally ( v . Elem ( ) )
case reflect . Slice , reflect . Array :
return true
}
return false
2016-03-23 20:53:09 +01:00
}
func fieldInfo ( f reflect . StructField ) ( k string , oe bool ) {
2016-05-04 17:48:12 +02:00
if f . PkgPath != "" { // Skip private fields.
return omittedKey , oe
}
k = f . Name
tag := f . Tag . Get ( "form" )
if tag == "" {
return k , oe
}
ps := strings . SplitN ( tag , "," , 2 )
if ps [ 0 ] != "" {
k = ps [ 0 ]
}
if len ( ps ) == 2 {
oe = ps [ 1 ] == "omitempty"
}
return k , oe
2016-03-23 20:53:09 +01:00
}
2016-07-21 21:10:33 +02:00
func findField ( v reflect . Value , n string , ignoreCase bool ) ( reflect . Value , bool ) {
2016-05-04 17:48:12 +02:00
t := v . Type ( )
l := v . NumField ( )
2016-07-21 21:10:33 +02:00
var lowerN string
caseInsensitiveMatch := - 1
if ignoreCase {
lowerN = strings . ToLower ( n )
}
2016-05-04 17:48:12 +02:00
// First try named fields.
for i := 0 ; i < l ; i ++ {
f := t . Field ( i )
k , _ := fieldInfo ( f )
if k == omittedKey {
continue
} else if n == k {
return v . Field ( i ) , true
2016-07-21 21:10:33 +02:00
} else if ignoreCase && lowerN == strings . ToLower ( k ) {
caseInsensitiveMatch = i
2016-05-04 17:48:12 +02:00
}
}
2016-07-21 21:10:33 +02:00
// If no exact match was found try case insensitive match.
if caseInsensitiveMatch != - 1 {
return v . Field ( caseInsensitiveMatch ) , true
}
2016-05-04 17:48:12 +02:00
// Then try anonymous (embedded) fields.
for i := 0 ; i < l ; i ++ {
f := t . Field ( i )
k , _ := fieldInfo ( f )
if k == omittedKey || ! f . Anonymous { // || k != "" ?
continue
}
fv := v . Field ( i )
fk := fv . Kind ( )
for fk == reflect . Ptr || fk == reflect . Interface {
fv = fv . Elem ( )
fk = fv . Kind ( )
}
if fk != reflect . Struct {
continue
}
2016-07-21 21:10:33 +02:00
if ev , ok := findField ( fv , n , ignoreCase ) ; ok {
2016-05-04 17:48:12 +02:00
return ev , true
}
}
return reflect . Value { } , false
2016-03-23 20:53:09 +01:00
}
var (
2016-05-04 17:48:12 +02:00
stringType = reflect . TypeOf ( string ( "" ) )
stringMapType = reflect . TypeOf ( map [ string ] interface { } { } )
timeType = reflect . TypeOf ( time . Time { } )
timePtrType = reflect . TypeOf ( & time . Time { } )
urlType = reflect . TypeOf ( url . URL { } )
2016-03-23 20:53:09 +01:00
)
func skipTextMarshalling ( t reflect . Type ) bool {
2016-05-04 17:48:12 +02:00
/ * // Skip time.Time because its text unmarshaling is overly rigid:
return t == timeType || t == timePtrType * /
// Skip time.Time & convertibles because its text unmarshaling is overly rigid:
return t . ConvertibleTo ( timeType ) || t . ConvertibleTo ( timePtrType )
2016-03-23 20:53:09 +01:00
}
func unmarshalValue ( v reflect . Value , x interface { } ) bool {
2016-05-04 17:48:12 +02:00
if skipTextMarshalling ( v . Type ( ) ) {
return false
}
tu , ok := v . Interface ( ) . ( encoding . TextUnmarshaler )
if ! ok && ! v . CanAddr ( ) {
return false
} else if ! ok {
return unmarshalValue ( v . Addr ( ) , x )
}
s := getString ( x )
if err := tu . UnmarshalText ( [ ] byte ( s ) ) ; err != nil {
panic ( err )
}
return true
2016-03-23 20:53:09 +01:00
}
func marshalValue ( v reflect . Value ) ( string , bool ) {
2016-05-04 17:48:12 +02:00
if skipTextMarshalling ( v . Type ( ) ) {
return "" , false
}
tm , ok := v . Interface ( ) . ( encoding . TextMarshaler )
if ! ok && ! v . CanAddr ( ) {
return "" , false
} else if ! ok {
return marshalValue ( v . Addr ( ) )
}
bs , err := tm . MarshalText ( )
if err != nil {
panic ( err )
}
return string ( bs ) , true
2016-03-23 20:53:09 +01:00
}