pax_global_header00006660000000000000000000000064134440272460014520gustar00rootroot0000000000000052 comment=5c4e22684113ffc2a77577c178189940925f9aef form-1.5.1/000077500000000000000000000000001344402724600124675ustar00rootroot00000000000000form-1.5.1/.travis.yml000066400000000000000000000007771344402724600146130ustar00rootroot00000000000000## 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. language: go go: - tip - 1.6 - 1.5 - 1.4 - 1.3 # 1.2 before_install: # - go get -v golang.org/x/tools/cmd/cover # - go get -v golang.org/x/tools/cmd/vet # - go get -v golang.org/x/lint/golint - export PATH=$PATH:/home/travis/gopath/bin script: - go build -v ./... - go test -v -cover ./... - go vet ./... # - golint . form-1.5.1/LICENSE000066400000000000000000000027111344402724600134750ustar00rootroot00000000000000Copyright (c) 2014 Alvaro J. Genial. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. form-1.5.1/README.md000066400000000000000000000223401344402724600137470ustar00rootroot00000000000000form ==== A Form Encoding & Decoding Package for Go, written by [Alvaro J. Genial](http://alva.ro). [![Build Status](https://travis-ci.org/ajg/form.png?branch=master)](https://travis-ci.org/ajg/form) [![GoDoc](https://godoc.org/github.com/ajg/form?status.png)](https://godoc.org/github.com/ajg/form) Synopsis -------- This library is designed to allow seamless, high-fidelity encoding and decoding of arbitrary data in `application/x-www-form-urlencoded` format and as [`url.Values`](http://golang.org/pkg/net/url/#Values). It is intended to be useful primarily in dealing with web forms and URI query strings, both of which natively employ said format. Unsurprisingly, `form` is modeled after other Go [`encoding`](http://golang.org/pkg/encoding/) packages, in particular [`encoding/json`](http://golang.org/pkg/encoding/json/), and follows the same conventions (see below for more.) It aims to automatically handle any kind of concrete Go [data value](#values) (i.e., not functions, channels, etc.) while providing mechanisms for custom behavior. Status ------ The implementation is in usable shape and is fairly well tested with its accompanying test suite. The API is unlikely to change much, but still may. Lastly, the code has not yet undergone a security review to ensure it is free of vulnerabilities. Please file an issue or send a pull request for fixes & improvements. Dependencies ------------ The only requirement is [Go 1.2](http://golang.org/doc/go1.2) or later. Usage ----- ```go import "github.com/ajg/form" // or: "gopkg.in/ajg/form.v1" ``` Given a type like the following... ```go type User struct { Name string `form:"name"` Email string `form:"email"` Joined time.Time `form:"joined,omitempty"` Posts []int `form:"posts"` Preferences map[string]string `form:"prefs"` Avatar []byte `form:"avatar"` PasswordHash int64 `form:"-"` } ``` ...it is easy to encode data of that type... ```go func PostUser(url string, u User) error { var c http.Client _, err := c.PostForm(url, form.EncodeToValues(u)) return err } ``` ...as well as decode it... ```go func Handler(w http.ResponseWriter, r *http.Request) { var u User d := form.NewDecoder(r.Body) if err := d.Decode(&u); err != nil { http.Error(w, "Form could not be decoded", http.StatusBadRequest) return } fmt.Fprintf(w, "Decoded: %#v", u) } ``` ...without having to do any grunt work. Field Tags ---------- Like other encoding packages, `form` supports the following options for fields: - `` `form:"-"` ``: Causes the field to be ignored during encoding and decoding. - `` `form:""` ``: Overrides the field's name; useful especially when dealing with external identifiers in camelCase, as are commonly found on the web. - `` `form:",omitempty"` ``: Elides the field during encoding if it is empty (typically meaning equal to the type's zero value.) - `` `form:",omitempty"` ``: The way to combine the two options above. Values ------ ### Simple Values Values of the following types are all considered simple: - `bool` - `int`, `int8`, `int16`, `int32`, `int64`, `rune` - `uint`, `uint8`, `uint16`, `uint32`, `uint64`, `byte` - `float32`, `float64` - `complex64`, `complex128` - `string` - `[]byte` (see note) - [`time.Time`](http://golang.org/pkg/time/#Time) - [`url.URL`](http://golang.org/pkg/net/url/#URL) - An alias of any of the above - A pointer to any of the above ### Composite Values A composite value is one that can contain other values. Values of the following kinds... - Maps - Slices; except `[]byte` (see note) - Structs; except [`time.Time`](http://golang.org/pkg/time/#Time) and [`url.URL`](http://golang.org/pkg/net/url/#URL) - Arrays - An alias of any of the above - A pointer to any of the above ...are considered composites in general, unless they implement custom marshaling/unmarshaling. Composite values are encoded as a flat mapping of paths to values, where the paths are constructed by joining the parent and child paths with a period (`.`). (Note: a byte slice is treated as a `string` by default because it's more efficient, but can also be decoded as a slice—i.e., with indexes.) ### Untyped Values While encouraged, it is not necessary to define a type (e.g. a `struct`) in order to use `form`, since it is able to encode and decode untyped data generically using the following rules: - Simple values will be treated as a `string`. - Composite values will be treated as a `map[string]interface{}`, itself able to contain nested values (both scalar and compound) ad infinitum. - However, if there is a value (of any supported type) already present in a map for a given key, then it will be used when possible, rather than being replaced with a generic value as specified above; this makes it possible to handle partially typed, dynamic or schema-less values. ### Zero Values By default, and without custom marshaling, zero values (also known as empty/default values) are encoded as the empty string. To disable this behavior, meaning to keep zero values in their literal form (e.g. `0` for integral types), `Encoder` offers a `KeepZeros` setter method, which will do just that when set to `true`. ### Unsupported Values Values of the following kinds aren't supported and, if present, must be ignored. - Channel - Function - Unsafe pointer - An alias of any of the above - A pointer to any of the above Custom Marshaling ----------------- There is a default (generally lossless) marshaling & unmarshaling scheme for any concrete data value in Go, which is good enough in most cases. However, it is possible to override it and use a custom scheme. For instance, a "binary" field could be marshaled more efficiently using [base64](http://golang.org/pkg/encoding/base64/) to prevent it from being percent-escaped during serialization to `application/x-www-form-urlencoded` format. Because `form` provides support for [`encoding.TextMarshaler`](http://golang.org/pkg/encoding/#TextMarshaler) and [`encoding.TextUnmarshaler`](http://golang.org/pkg/encoding/#TextUnmarshaler) it is easy to do that; for instance, like this: ```go import "encoding" type Binary []byte var ( _ encoding.TextMarshaler = &Binary{} _ encoding.TextUnmarshaler = &Binary{} ) func (b Binary) MarshalText() ([]byte, error) { return []byte(base64.URLEncoding.EncodeToString([]byte(b))), nil } func (b *Binary) UnmarshalText(text []byte) error { bs, err := base64.URLEncoding.DecodeString(string(text)) if err == nil { *b = Binary(bs) } return err } ``` Now any value with type `Binary` will automatically be encoded using the [URL](http://golang.org/pkg/encoding/base64/#URLEncoding) variant of base64. It is left as an exercise to the reader to improve upon this scheme by eliminating the need for padding (which, besides being superfluous, uses `=`, a character that will end up percent-escaped.) Keys ---- In theory any value can be a key as long as it has a string representation. However, by default, periods have special meaning to `form`, and thus, under the hood (i.e. in encoded form) they are transparently escaped using a preceding backslash (`\`). Backslashes within keys, themselves, are also escaped in this manner (e.g. as `\\`) in order to permit representing `\.` itself (as `\\\.`). (Note: it is normally unnecessary to deal with this issue unless keys are being constructed manually—e.g. literally embedded in HTML or in a URI.) The default delimiter and escape characters used for encoding and decoding composite keys can be changed using the `DelimitWith` and `EscapeWith` setter methods of `Encoder` and `Decoder`, respectively. For example... ```go package main import ( "os" "github.com/ajg/form" ) func main() { type B struct { Qux string `form:"qux"` } type A struct { FooBar B `form:"foo.bar"` } a := A{FooBar: B{"XYZ"}} os.Stdout.WriteString("Default: ") form.NewEncoder(os.Stdout).Encode(a) os.Stdout.WriteString("\nCustom: ") form.NewEncoder(os.Stdout).DelimitWith('/').Encode(a) os.Stdout.WriteString("\n") } ``` ...will produce... ``` Default: foo%5C.bar.qux=XYZ Custom: foo.bar%2Fqux=XYZ ``` (`%5C` and `%2F` represent `\` and `/`, respectively.) Limitations ----------- - Circular (self-referential) values are untested. Future Work ----------- The following items would be nice to have in the future—though they are not being worked on yet: - An option to treat all values as if they had been tagged with `omitempty`. - An option to automatically treat all field names in `camelCase` or `underscore_case`. - Built-in support for the types in [`math/big`](http://golang.org/pkg/math/big/). - Built-in support for the types in [`image/color`](http://golang.org/pkg/image/color/). - Improve encoding/decoding by reading/writing directly from/to the `io.Reader`/`io.Writer` when possible, rather than going through an intermediate representation (i.e. `node`) which requires more memory. (Feel free to implement any of these and then send a pull request.) Related Work ------------ - Package [gorilla/schema](https://github.com/gorilla/schema), which only implements decoding. - Package [google/go-querystring](https://github.com/google/go-querystring), which only implements encoding. License ------- This library is distributed under a BSD-style [LICENSE](./LICENSE). form-1.5.1/TODO.md000066400000000000000000000001041344402724600135510ustar00rootroot00000000000000TODO ==== - Document IgnoreCase and IgnoreUnknownKeys in README. form-1.5.1/decode.go000066400000000000000000000221671344402724600142510ustar00rootroot00000000000000// 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 ( "fmt" "io" "io/ioutil" "net/url" "reflect" "strconv" "time" ) // NewDecoder returns a new form Decoder. func NewDecoder(r io.Reader) *Decoder { return &Decoder{r, defaultDelimiter, defaultEscape, false, false} } // Decoder decodes data from a form (application/x-www-form-urlencoded). type Decoder struct { r io.Reader d rune e rune ignoreUnknown bool ignoreCase bool } // DelimitWith sets r as the delimiter used for composite keys by Decoder d and returns the latter; it is '.' by default. func (d *Decoder) DelimitWith(r rune) *Decoder { d.d = r return d } // EscapeWith sets r as the escape used for delimiters (and to escape itself) by Decoder d and returns the latter; it is '\\' by default. func (d *Decoder) EscapeWith(r rune) *Decoder { d.e = r return d } // Decode reads in and decodes form-encoded data into dst. func (d Decoder) Decode(dst interface{}) error { bs, err := ioutil.ReadAll(d.r) if err != nil { return err } vs, err := url.ParseQuery(string(bs)) if err != nil { return err } v := reflect.ValueOf(dst) return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) } // IgnoreUnknownKeys if set to true it will make the Decoder ignore values // that are not found in the destination object instead of returning an error. func (d *Decoder) IgnoreUnknownKeys(ignoreUnknown bool) { d.ignoreUnknown = ignoreUnknown } // IgnoreCase if set to true it will make the Decoder try to set values in the // destination object even if the case does not match. func (d *Decoder) IgnoreCase(ignoreCase bool) { d.ignoreCase = ignoreCase } // DecodeString decodes src into dst. func (d Decoder) DecodeString(dst interface{}, src string) error { vs, err := url.ParseQuery(src) if err != nil { return err } v := reflect.ValueOf(dst) return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) } // DecodeValues decodes vs into dst. func (d Decoder) DecodeValues(dst interface{}, vs url.Values) error { v := reflect.ValueOf(dst) return d.decodeNode(v, parseValues(d.d, d.e, vs, canIndexOrdinally(v))) } // DecodeString decodes src into dst. func DecodeString(dst interface{}, src string) error { return NewDecoder(nil).DecodeString(dst, src) } // DecodeValues decodes vs into dst. func DecodeValues(dst interface{}, vs url.Values) error { return NewDecoder(nil).DecodeValues(dst, vs) } func (d Decoder) decodeNode(v reflect.Value, n node) (err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("%v", e) } }() if v.Kind() == reflect.Slice { return fmt.Errorf("could not decode directly into slice; use pointer to slice") } d.decodeValue(v, n) return nil } func (d Decoder) decodeValue(v reflect.Value, x interface{}) { t := v.Type() k := v.Kind() if k == reflect.Ptr && v.IsNil() { v.Set(reflect.New(t.Elem())) } if unmarshalValue(v, x) { return } empty := isEmpty(x) switch k { case reflect.Ptr: d.decodeValue(v.Elem(), x) return case reflect.Interface: if !v.IsNil() { d.decodeValue(v.Elem(), x) return } else if empty { return // Allow nil interfaces only if empty. } else { panic("form: cannot decode non-empty value into into nil interface") } } if empty { v.Set(reflect.Zero(t)) // Treat the empty string as the zero value. return } switch k { case reflect.Struct: if t.ConvertibleTo(timeType) { d.decodeTime(v, x) } else if t.ConvertibleTo(urlType) { d.decodeURL(v, x) } else { d.decodeStruct(v, x) } case reflect.Slice: d.decodeSlice(v, x) case reflect.Array: d.decodeArray(v, x) case reflect.Map: d.decodeMap(v, x) case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func: panic(t.String() + " has unsupported kind " + k.String()) default: d.decodeBasic(v, x) } } func (d Decoder) decodeStruct(v reflect.Value, x interface{}) { t := v.Type() for k, c := range getNode(x) { if f, ok := findField(v, k, d.ignoreCase); !ok && k == "" { panic(getString(x) + " cannot be decoded as " + t.String()) } else if !ok { if !d.ignoreUnknown { panic(k + " doesn't exist in " + t.String()) } } else if !f.CanSet() { panic(k + " cannot be set in " + t.String()) } else { d.decodeValue(f, c) } } } func (d Decoder) decodeMap(v reflect.Value, x interface{}) { t := v.Type() if v.IsNil() { v.Set(reflect.MakeMap(t)) } for k, c := range getNode(x) { i := reflect.New(t.Key()).Elem() d.decodeValue(i, k) w := v.MapIndex(i) if w.IsValid() { // We have an actual element value to decode into. if w.Kind() == reflect.Interface { w = w.Elem() } w = reflect.New(w.Type()).Elem() } else if t.Elem().Kind() != reflect.Interface { // The map's element type is concrete. w = reflect.New(t.Elem()).Elem() } else { // The best we can do here is to decode as either a string (for scalars) or a map[string]interface {} (for the rest). // We could try to guess the type based on the string (e.g. true/false => bool) but that'll get ugly fast, // especially if we have to guess the kind (slice vs. array vs. map) and index type (e.g. string, int, etc.) switch c.(type) { case node: w = reflect.MakeMap(stringMapType) case string: w = reflect.New(stringType).Elem() default: panic("value is neither node nor string") } } d.decodeValue(w, c) v.SetMapIndex(i, w) } } func (d Decoder) decodeArray(v reflect.Value, x interface{}) { t := v.Type() for k, c := range getNode(x) { i, err := strconv.Atoi(k) if err != nil { panic(k + " is not a valid index for type " + t.String()) } if l := v.Len(); i >= l { panic("index is above array size") } d.decodeValue(v.Index(i), c) } } func (d Decoder) decodeSlice(v reflect.Value, x interface{}) { t := v.Type() if t.Elem().Kind() == reflect.Uint8 { // Allow, but don't require, byte slices to be encoded as a single string. if s, ok := x.(string); ok { v.SetBytes([]byte(s)) return } } // NOTE: Implicit indexing is currently done at the parseValues level, // so if if an implicitKey reaches here it will always replace the last. implicit := 0 for k, c := range getNode(x) { var i int if k == implicitKey { i = implicit implicit++ } else { explicit, err := strconv.Atoi(k) if err != nil { panic(k + " is not a valid index for type " + t.String()) } i = explicit implicit = explicit + 1 } // "Extend" the slice if it's too short. if l := v.Len(); i >= l { delta := i - l + 1 v.Set(reflect.AppendSlice(v, reflect.MakeSlice(t, delta, delta))) } d.decodeValue(v.Index(i), c) } } func (d Decoder) decodeBasic(v reflect.Value, x interface{}) { t := v.Type() switch k, s := t.Kind(), getString(x); k { case reflect.Bool: if b, e := strconv.ParseBool(s); e == nil { v.SetBool(b) } else { panic("could not parse bool from " + strconv.Quote(s)) } case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: if i, e := strconv.ParseInt(s, 10, 64); e == nil { v.SetInt(i) } else { panic("could not parse int from " + strconv.Quote(s)) } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: if u, e := strconv.ParseUint(s, 10, 64); e == nil { v.SetUint(u) } else { panic("could not parse uint from " + strconv.Quote(s)) } case reflect.Float32, reflect.Float64: if f, e := strconv.ParseFloat(s, 64); e == nil { v.SetFloat(f) } else { panic("could not parse float from " + strconv.Quote(s)) } case reflect.Complex64, reflect.Complex128: var c complex128 if n, err := fmt.Sscanf(s, "%g", &c); n == 1 && err == nil { v.SetComplex(c) } else { panic("could not parse complex from " + strconv.Quote(s)) } case reflect.String: v.SetString(s) default: panic(t.String() + " has unsupported kind " + k.String()) } } func (d Decoder) decodeTime(v reflect.Value, x interface{}) { t := v.Type() s := getString(x) // TODO: Find a more efficient way to do this. for _, f := range allowedTimeFormats { if p, err := time.Parse(f, s); err == nil { v.Set(reflect.ValueOf(p).Convert(v.Type())) return } } panic("cannot decode string `" + s + "` as " + t.String()) } func (d Decoder) decodeURL(v reflect.Value, x interface{}) { t := v.Type() s := getString(x) if u, err := url.Parse(s); err == nil { v.Set(reflect.ValueOf(*u).Convert(v.Type())) return } panic("cannot decode string `" + s + "` as " + t.String()) } var allowedTimeFormats = []string{ "2006-01-02T15:04:05.999999999Z07:00", "2006-01-02T15:04:05.999999999Z07", "2006-01-02T15:04:05.999999999Z", "2006-01-02T15:04:05.999999999", "2006-01-02T15:04:05Z07:00", "2006-01-02T15:04:05Z07", "2006-01-02T15:04:05Z", "2006-01-02T15:04:05", "2006-01-02T15:04Z", "2006-01-02T15:04", "2006-01-02T15Z", "2006-01-02T15", "2006-01-02", "2006-01", "2006", "15:04:05.999999999Z07:00", "15:04:05.999999999Z07", "15:04:05.999999999Z", "15:04:05.999999999", "15:04:05Z07:00", "15:04:05Z07", "15:04:05Z", "15:04:05", "15:04Z", "15:04", "15Z", "15", } form-1.5.1/decode_test.go000066400000000000000000000056101344402724600153020ustar00rootroot00000000000000// 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 ( "net/url" "reflect" "strings" "testing" ) func TestDecodeString(t *testing.T) { for _, c := range testCases(decOnly) { if err := DecodeString(c.a, c.s); err != nil { t.Errorf("DecodeString(%#v): %s", c.s, err) } else if !reflect.DeepEqual(c.a, c.b) { t.Errorf("DecodeString(%#v)\n want (%#v)\n have (%#v)", c.s, c.b, c.a) } } } func TestDecodeValues(t *testing.T) { for _, c := range testCases(decOnly) { vs := mustParseQuery(c.s) if err := DecodeValues(c.a, vs); err != nil { t.Errorf("DecodeValues(%#v): %s", vs, err) } else if !reflect.DeepEqual(c.a, c.b) { t.Errorf("DecodeValues(%#v)\n want (%#v)\n have (%#v)", vs, c.b, c.a) } } } func TestDecode(t *testing.T) { for _, c := range testCases(decOnly) { r := strings.NewReader(c.s) d := NewDecoder(r) if err := d.Decode(c.a); err != nil { t.Errorf("Decode(%#v): %s", r, err) } else if !reflect.DeepEqual(c.a, c.b) { t.Errorf("Decode(%#v)\n want (%#v)\n have (%#v)", r, c.b, c.a) } } } func TestDecodeIgnoreUnknown(t *testing.T) { type simpleStruct struct{ A string } var dst simpleStruct values := url.Values{ "b": []string{"2"}, "A": []string{"1"}, } expected := simpleStruct{A: "1"} d := NewDecoder(nil) err := d.DecodeValues(&dst, values) if err == nil || err.Error() != "b doesn't exist in form.simpleStruct" { t.Errorf("Decode(%#v): expected error got nil", values) } d.IgnoreUnknownKeys(true) err = d.DecodeValues(&dst, values) if err != nil { t.Errorf("Decode(%#v): %s", values, err) } if !reflect.DeepEqual(dst, expected) { t.Errorf("Decode(%#v)\n want (%#v)\n have (%#v)", values, expected, dst) } } func TestDecodeIgnoreCase(t *testing.T) { type simpleStruct struct{ AaAA string } var dst simpleStruct values := url.Values{ "aAaA": []string{"1"}, } expected := simpleStruct{AaAA: "1"} d := NewDecoder(nil) err := d.DecodeValues(&dst, values) if err == nil || err.Error() != "aAaA doesn't exist in form.simpleStruct" { t.Errorf("Decode(%#v): expected error got nil", values) } d.IgnoreCase(true) err = d.DecodeValues(&dst, values) if err != nil { t.Errorf("Decode(%#v): %s", values, err) } if !reflect.DeepEqual(dst, expected) { t.Errorf("Decode(%#v)\n want (%#v)\n have (%#v)", values, expected, dst) } } func TestDecodeIgnoreCasePriority(t *testing.T) { type simpleStruct struct { Aaa string AaA string AAA string } var dst simpleStruct values := url.Values{ "AaA": []string{"1"}, } expected := simpleStruct{AaA: "1"} d := NewDecoder(nil) d.IgnoreCase(true) err := d.DecodeValues(&dst, values) if err != nil { t.Errorf("Decode(%#v): %s", values, err) } if !reflect.DeepEqual(dst, expected) { t.Errorf("Decode(%#v)\n want (%#v)\n have (%#v)", values, expected, dst) } } form-1.5.1/encode.go000066400000000000000000000223201344402724600142520ustar00rootroot00000000000000// 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 ( "encoding" "errors" "fmt" "io" "net/url" "reflect" "strconv" "strings" "time" ) // NewEncoder returns a new form Encoder. func NewEncoder(w io.Writer) *Encoder { return &Encoder{w, defaultDelimiter, defaultEscape, false} } // Encoder provides a way to encode to a Writer. type Encoder struct { w io.Writer d rune e rune z bool } // 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 { e.d = r return e } // 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 { e.e = r return e } // 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 { v := reflect.ValueOf(dst) n, err := encodeToNode(v, e.z) if err != nil { return err } s := n.values(e.d, e.e).Encode() 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 } // EncodeToString encodes dst as a form and returns it as a string. func EncodeToString(dst interface{}) (string, error) { v := reflect.ValueOf(dst) n, err := encodeToNode(v, false) if err != nil { return "", err } vs := n.values(defaultDelimiter, defaultEscape) return vs.Encode(), nil } // EncodeToValues encodes dst as a form and returns it as Values. func EncodeToValues(dst interface{}) (url.Values, error) { v := reflect.ValueOf(dst) n, err := encodeToNode(v, false) if err != nil { return nil, err } vs := n.values(defaultDelimiter, defaultEscape) return vs, nil } func encodeToNode(v reflect.Value, z bool) (n node, err error) { defer func() { if e := recover(); e != nil { err = fmt.Errorf("%v", e) } }() return getNode(encodeValue(v, z)), nil } func encodeValue(v reflect.Value, z bool) interface{} { t := v.Type() k := v.Kind() if s, ok := marshalValue(v); ok { return s } else if !z && isEmptyValue(v) { return "" // Treat the zero value as the empty string. } switch k { case reflect.Ptr, reflect.Interface: return encodeValue(v.Elem(), z) case reflect.Struct: if t.ConvertibleTo(timeType) { return encodeTime(v) } else if t.ConvertibleTo(urlType) { return encodeURL(v) } return encodeStruct(v, z) case reflect.Slice: return encodeSlice(v, z) case reflect.Array: return encodeArray(v, z) case reflect.Map: return encodeMap(v, z) case reflect.Invalid, reflect.Uintptr, reflect.UnsafePointer, reflect.Chan, reflect.Func: panic(t.String() + " has unsupported kind " + t.Kind().String()) default: return encodeBasic(v) } } func encodeStruct(v reflect.Value, z bool) interface{} { 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 { n[k] = encodeValue(fv, z) } } return n } func encodeMap(v reflect.Value, z bool) interface{} { n := node{} for _, i := range v.MapKeys() { k := getString(encodeValue(i, z)) n[k] = encodeValue(v.MapIndex(i), z) } return n } func encodeArray(v reflect.Value, z bool) interface{} { n := node{} for i := 0; i < v.Len(); i++ { n[strconv.Itoa(i)] = encodeValue(v.Index(i), z) } return n } func encodeSlice(v reflect.Value, z bool) interface{} { 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++ { n[strconv.Itoa(i)] = encodeValue(v.Index(i), z) } return n } func encodeTime(v reflect.Value) string { 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") } func encodeURL(v reflect.Value) string { u := v.Convert(urlType).Interface().(url.URL) return u.String() } func encodeBasic(v reflect.Value) string { 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()) } func isEmptyValue(v reflect.Value) bool { 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 } // canIndexOrdinally returns whether a value contains an ordered sequence of elements. func canIndexOrdinally(v reflect.Value) bool { 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 } func fieldInfo(f reflect.StructField) (k string, oe bool) { 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 } func findField(v reflect.Value, n string, ignoreCase bool) (reflect.Value, bool) { t := v.Type() l := v.NumField() var lowerN string caseInsensitiveMatch := -1 if ignoreCase { lowerN = strings.ToLower(n) } // 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 } else if ignoreCase && lowerN == strings.ToLower(k) { caseInsensitiveMatch = i } } // If no exact match was found try case insensitive match. if caseInsensitiveMatch != -1 { return v.Field(caseInsensitiveMatch), true } // 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 } if ev, ok := findField(fv, n, ignoreCase); ok { return ev, true } } return reflect.Value{}, false } var ( 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{}) ) func skipTextMarshalling(t reflect.Type) bool { /*// 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) } func unmarshalValue(v reflect.Value, x interface{}) bool { 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 } func marshalValue(v reflect.Value) (string, bool) { 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 } form-1.5.1/encode_test.go000066400000000000000000000052001344402724600153070ustar00rootroot00000000000000// 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 ( "bytes" "reflect" "testing" ) func TestEncodeToString(t *testing.T) { for _, c := range testCases(encOnly) { if s, err := EncodeToString(c.b); err != nil { t.Errorf("EncodeToString(%#v): %s", c.b, err) } else if !reflect.DeepEqual(c.s, s) { t.Errorf("EncodeToString(%#v)\n want (%#v)\n have (%#v)", c.b, c.s, s) } } } func TestEncodeToValues(t *testing.T) { for _, c := range testCases(encOnly) { cvs := mustParseQuery(c.s) if vs, err := EncodeToValues(c.b); err != nil { t.Errorf("EncodeToValues(%#v): %s", c.b, err) } else if !reflect.DeepEqual(cvs, vs) { t.Errorf("EncodeToValues(%#v)\n want (%#v)\n have (%#v)", c.b, cvs, vs) } } } func TestEncode(t *testing.T) { for _, c := range testCases(encOnly) { var w bytes.Buffer e := NewEncoder(&w) if err := e.Encode(c.b); err != nil { t.Errorf("Encode(%#v): %s", c.b, err) } else if s := w.String(); !reflect.DeepEqual(c.s, s) { t.Errorf("Encode(%#v)\n want (%#v)\n have (%#v)", c.b, c.s, s) } } } type Thing1 struct { String string `form:"name,omitempty"` Integer *uint `form:"num,omitempty"` } type Thing2 struct { String string `form:"name,omitempty"` Integer uint `form:"num,omitempty"` } type Thing3 struct { String string `form:"name"` Integer *uint `form:"num"` } type Thing4 struct { String string `form:"name"` Integer uint `form:"num"` } func TestEncode_KeepZero(t *testing.T) { num := uint(0) for _, c := range []struct { b interface{} s string z bool }{ {Thing1{"test", &num}, "name=test&num=", false}, {Thing1{"test", &num}, "name=test&num=0", true}, {Thing2{"test", num}, "name=test", false}, {Thing2{"test", num}, "name=test", true}, {Thing3{"test", &num}, "name=test&num=", false}, {Thing3{"test", &num}, "name=test&num=0", true}, {Thing4{"test", num}, "name=test&num=", false}, {Thing4{"test", num}, "name=test&num=0", true}, {Thing1{"", &num}, "num=", false}, {Thing1{"", &num}, "num=0", true}, {Thing2{"", num}, "", false}, {Thing2{"", num}, "", true}, {Thing3{"", &num}, "name=&num=", false}, {Thing3{"", &num}, "name=&num=0", true}, {Thing4{"", num}, "name=&num=", false}, {Thing4{"", num}, "name=&num=0", true}, } { var w bytes.Buffer e := NewEncoder(&w) if err := e.KeepZeros(c.z).Encode(c.b); err != nil { t.Errorf("KeepZeros(%#v).Encode(%#v): %s", c.z, c.b, err) } else if s := w.String(); c.s != s { t.Errorf("KeepZeros(%#v).Encode(%#v)\n want (%#v)\n have (%#v)", c.z, c.b, c.s, s) } } } form-1.5.1/form.go000066400000000000000000000005561344402724600137670ustar00rootroot00000000000000// 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 implements encoding and decoding of application/x-www-form-urlencoded data. package form const ( implicitKey = "_" omittedKey = "-" defaultDelimiter = '.' defaultEscape = '\\' ) form-1.5.1/form_test.go000066400000000000000000000222471344402724600150270ustar00rootroot00000000000000// 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 ( "encoding" "fmt" "net/url" "time" ) type Struct struct { B bool I int `form:"life"` F float64 C complex128 R rune `form:",omitempty"` // For testing when non-empty. Re rune `form:",omitempty"` // For testing when empty. S string T time.Time U url.URL A Array M Map Y interface{} `form:"-"` // For testing when non-empty. Ye interface{} `form:"-"` // For testing when empty. Zs Slice E // Embedded. P P `form:"P.D\\Q.B"` } type SXs map[string]interface{} type E struct { Bytes1 []byte // For testing explicit (qualified by embedder) name, e.g. "E.Bytes1". Bytes2 []byte // For testing implicit (unqualified) name, e.g. just "Bytes2" } type Z time.Time // Defined as such to test conversions. func (z Z) String() string { return time.Time(z).String() } type Array [3]string type Map map[string]int type Slice []struct { Z Z Q Q Qp *Q Q2 Q `form:"-"` E `form:"-"` } // Custom marshaling type Q struct { a, b uint16 } var ( _ encoding.TextMarshaler = &Q{} _ encoding.TextUnmarshaler = &Q{} ) func (u Q) MarshalText() ([]byte, error) { return []byte(fmt.Sprintf("%d_%d", u.a, u.b)), nil } func (u *Q) UnmarshalText(bs []byte) error { _, err := fmt.Sscanf(string(bs), "%d_%d", &u.a, &u.b) return err } func prepopulate(sxs SXs) SXs { var B bool var I int var F float64 var C complex128 var R rune var S string var T time.Time var U url.URL var A Array var M Map // Y is ignored. // Ye is ignored. var Zs Slice var E E var P P sxs["B"] = B sxs["life"] = I sxs["F"] = F sxs["C"] = C sxs["R"] = R // Re is omitted. sxs["S"] = S sxs["T"] = T sxs["U"] = U sxs["A"] = A sxs["M"] = M // Y is ignored. // Ye is ignored. sxs["Zs"] = Zs sxs["E"] = E sxs["P.D\\Q.B"] = P return sxs } type P struct { A, B string } type direction int const ( encOnly = 1 decOnly = 2 rndTrip = encOnly | decOnly ) func testCases(dir direction) (cs []testCase) { var B bool var I int var F float64 var C complex128 var R rune var S string var T time.Time var U url.URL const canonical = `A.0=x&A.1=y&A.2=z&B=true&C=42%2B6.6i&E.Bytes1=%00%01%02&E.Bytes2=%03%04%05&F=6.6&M.Bar=8&M.Foo=7&M.Qux=9&P%5C.D%5C%5CQ%5C.B.A=P%2FD&P%5C.D%5C%5CQ%5C.B.B=Q-B&R=8734&S=Hello%2C+there.&T=2013-10-01T07%3A05%3A34.000000088Z&U=http%3A%2F%2Fexample.org%2Ffoo%23bar&Zs.0.Q=11_22&Zs.0.Qp=33_44&Zs.0.Z=2006-12-01&life=42` const variation = `;C=42%2B6.6i;A.0=x;M.Bar=8;F=6.6;A.1=y;R=8734;A.2=z;Zs.0.Qp=33_44;B=true;M.Foo=7;T=2013-10-01T07:05:34.000000088Z;E.Bytes1=%00%01%02;Bytes2=%03%04%05;Zs.0.Q=11_22;Zs.0.Z=2006-12-01;M.Qux=9;life=42;S=Hello,+there.;P\.D\\Q\.B.A=P/D;P\.D\\Q\.B.B=Q-B;U=http%3A%2F%2Fexample.org%2Ffoo%23bar;` for _, c := range []testCase{ // Bools {rndTrip, &B, "=", b(false)}, {rndTrip, &B, "=true", b(true)}, {decOnly, &B, "=false", b(false)}, // Ints {rndTrip, &I, "=", i(0)}, {rndTrip, &I, "=42", i(42)}, {rndTrip, &I, "=-42", i(-42)}, {decOnly, &I, "=0", i(0)}, {decOnly, &I, "=-0", i(0)}, // Floats {rndTrip, &F, "=", f(0)}, {rndTrip, &F, "=6.6", f(6.6)}, {rndTrip, &F, "=-6.6", f(-6.6)}, // Complexes {rndTrip, &C, "=", c(complex(0, 0))}, {rndTrip, &C, "=42%2B6.6i", c(complex(42, 6.6))}, {rndTrip, &C, "=-42-6.6i", c(complex(-42, -6.6))}, // Runes {rndTrip, &R, "=", r(0)}, {rndTrip, &R, "=97", r('a')}, {rndTrip, &R, "=8734", r('\u221E')}, // Strings {rndTrip, &S, "=", s("")}, {rndTrip, &S, "=X+%26+Y+%26+Z", s("X & Y & Z")}, {rndTrip, &S, "=Hello%2C+there.", s("Hello, there.")}, {decOnly, &S, "=Hello, there.", s("Hello, there.")}, // Dates/Times {rndTrip, &T, "=", t(time.Time{})}, {rndTrip, &T, "=2013-10-01T07%3A05%3A34.000000088Z", t(time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC))}, {decOnly, &T, "=2013-10-01T07:05:34.000000088Z", t(time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC))}, {rndTrip, &T, "=07%3A05%3A34.000000088Z", t(time.Date(0, 1, 1, 7, 5, 34, 88, time.UTC))}, {decOnly, &T, "=07:05:34.000000088Z", t(time.Date(0, 1, 1, 7, 5, 34, 88, time.UTC))}, {rndTrip, &T, "=2013-10-01", t(time.Date(2013, 10, 1, 0, 0, 0, 0, time.UTC))}, // URLs {rndTrip, &U, "=", u(url.URL{})}, {rndTrip, &U, "=http%3A%2F%2Fexample.org%2Ffoo%23bar", u(url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"})}, {rndTrip, &U, "=git%3A%2F%2Fgithub.com%2Fajg%2Fform.git", u(url.URL{Scheme: "git", Host: "github.com", Path: "/ajg/form.git"})}, // Structs {rndTrip, &Struct{Y: 786}, canonical, &Struct{ true, 42, 6.6, complex(42, 6.6), '\u221E', rune(0), "Hello, there.", time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC), url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"}, Array{"x", "y", "z"}, Map{"Foo": 7, "Bar": 8, "Qux": 9}, 786, // Y: This value should not change. nil, // Ye: This value should not change. Slice{{Z(time.Date(2006, 12, 1, 0, 0, 0, 0, time.UTC)), Q{11, 22}, &Q{33, 44}, Q{}, E{}}}, E{[]byte{0, 1, 2}, []byte{3, 4, 5}}, P{"P/D", "Q-B"}, }, }, {decOnly, &Struct{Y: 786}, variation, &Struct{ true, 42, 6.6, complex(42, 6.6), '\u221E', rune(0), "Hello, there.", time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC), url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"}, Array{"x", "y", "z"}, Map{"Foo": 7, "Bar": 8, "Qux": 9}, 786, // Y: This value should not change. nil, // Ye: This value should not change. Slice{{Z(time.Date(2006, 12, 1, 0, 0, 0, 0, time.UTC)), Q{11, 22}, &Q{33, 44}, Q{}, E{}}}, E{[]byte{0, 1, 2}, []byte{3, 4, 5}}, P{"P/D", "Q-B"}, }, }, // Maps {rndTrip, prepopulate(SXs{}), canonical, SXs{"B": true, "life": 42, "F": 6.6, "C": complex(42, 6.6), "R": '\u221E', // Re is omitted. "S": "Hello, there.", "T": time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC), "U": url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"}, "A": Array{"x", "y", "z"}, "M": Map{"Foo": 7, "Bar": 8, "Qux": 9}, // Y is ignored. // Ye is ignored. "Zs": Slice{{Z(time.Date(2006, 12, 1, 0, 0, 0, 0, time.UTC)), Q{11, 22}, &Q{33, 44}, Q{}, E{}}}, "E": E{[]byte{0, 1, 2}, []byte{3, 4, 5}}, "P.D\\Q.B": P{"P/D", "Q-B"}, }, }, {decOnly, prepopulate(SXs{}), variation, SXs{"B": true, "life": 42, "F": 6.6, "C": complex(42, 6.6), "R": '\u221E', // Re is omitted. "S": "Hello, there.", "T": time.Date(2013, 10, 1, 7, 5, 34, 88, time.UTC), "U": url.URL{Scheme: "http", Host: "example.org", Path: "/foo", Fragment: "bar"}, "A": Array{"x", "y", "z"}, "M": Map{"Foo": 7, "Bar": 8, "Qux": 9}, // Y is ignored. // Ye is ignored. "Zs": Slice{{Z(time.Date(2006, 12, 1, 0, 0, 0, 0, time.UTC)), Q{11, 22}, &Q{33, 44}, Q{}, E{}}}, "E": E{[]byte{0, 1, 2}, nil}, "Bytes2": string([]byte{3, 4, 5}), "P.D\\Q.B": P{"P/D", "Q-B"}, }, }, {rndTrip, SXs{}, canonical, SXs{"B": "true", "life": "42", "F": "6.6", "C": "42+6.6i", "R": "8734", // Re is omitted. "S": "Hello, there.", "T": "2013-10-01T07:05:34.000000088Z", "U": "http://example.org/foo#bar", "A": map[string]interface{}{"0": "x", "1": "y", "2": "z"}, "M": map[string]interface{}{"Foo": "7", "Bar": "8", "Qux": "9"}, // Y is ignored. // Ye is ignored. "Zs": map[string]interface{}{ "0": map[string]interface{}{ "Z": "2006-12-01", "Q": "11_22", "Qp": "33_44", }, }, "E": map[string]interface{}{"Bytes1": string([]byte{0, 1, 2}), "Bytes2": string([]byte{3, 4, 5})}, "P.D\\Q.B": map[string]interface{}{"A": "P/D", "B": "Q-B"}, }, }, {decOnly, SXs{}, variation, SXs{"B": "true", "life": "42", "F": "6.6", "C": "42+6.6i", "R": "8734", // Re is omitted. "S": "Hello, there.", "T": "2013-10-01T07:05:34.000000088Z", "U": "http://example.org/foo#bar", "A": map[string]interface{}{"0": "x", "1": "y", "2": "z"}, "M": map[string]interface{}{"Foo": "7", "Bar": "8", "Qux": "9"}, // Y is ignored. // Ye is ignored. "Zs": map[string]interface{}{ "0": map[string]interface{}{ "Z": "2006-12-01", "Q": "11_22", "Qp": "33_44", }, }, "E": map[string]interface{}{"Bytes1": string([]byte{0, 1, 2})}, "Bytes2": string([]byte{3, 4, 5}), "P.D\\Q.B": map[string]interface{}{"A": "P/D", "B": "Q-B"}, }, }, } { if c.d&dir != 0 { cs = append(cs, c) } } return cs } type testCase struct { d direction a interface{} s string b interface{} } func b(b bool) *bool { return &b } func i(i int) *int { return &i } func f(f float64) *float64 { return &f } func c(c complex128) *complex128 { return &c } func r(r rune) *rune { return &r } func s(s string) *string { return &s } func t(t time.Time) *time.Time { return &t } func u(u url.URL) *url.URL { return &u } func mustParseQuery(s string) url.Values { vs, err := url.ParseQuery(s) if err != nil { panic(err) } return vs } form-1.5.1/node.go000066400000000000000000000066131344402724600137510ustar00rootroot00000000000000// 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 ( "net/url" "strconv" "strings" ) type node map[string]interface{} func (n node) values(d, e rune) url.Values { vs := url.Values{} n.merge(d, e, "", &vs) return vs } func (n node) merge(d, e rune, p string, vs *url.Values) { for k, x := range n { switch y := x.(type) { case string: vs.Add(p+escape(d, e, k), y) case node: y.merge(d, e, p+escape(d, e, k)+string(d), vs) default: panic("value is neither string nor node") } } } // TODO: Add tests for implicit indexing. func parseValues(d, e rune, vs url.Values, canIndexFirstLevelOrdinally bool) node { // NOTE: Because of the flattening of potentially multiple strings to one key, implicit indexing works: // i. At the first level; e.g. Foo.Bar=A&Foo.Bar=B becomes 0.Foo.Bar=A&1.Foo.Bar=B // ii. At the last level; e.g. Foo.Bar._=A&Foo.Bar._=B becomes Foo.Bar.0=A&Foo.Bar.1=B // TODO: At in-between levels; e.g. Foo._.Bar=A&Foo._.Bar=B becomes Foo.0.Bar=A&Foo.1.Bar=B // (This last one requires that there only be one placeholder in order for it to be unambiguous.) m := map[string]string{} for k, ss := range vs { indexLastLevelOrdinally := strings.HasSuffix(k, string(d)+implicitKey) for i, s := range ss { if canIndexFirstLevelOrdinally { k = strconv.Itoa(i) + string(d) + k } else if indexLastLevelOrdinally { k = strings.TrimSuffix(k, implicitKey) + strconv.Itoa(i) } m[k] = s } } n := node{} for k, s := range m { n = n.split(d, e, k, s) } return n } func splitPath(d, e rune, path string) (k, rest string) { esc := false for i, r := range path { switch { case !esc && r == e: esc = true case !esc && r == d: return unescape(d, e, path[:i]), path[i+1:] default: esc = false } } return unescape(d, e, path), "" } func (n node) split(d, e rune, path, s string) node { k, rest := splitPath(d, e, path) if rest == "" { return add(n, k, s) } if _, ok := n[k]; !ok { n[k] = node{} } c := getNode(n[k]) n[k] = c.split(d, e, rest, s) return n } func add(n node, k, s string) node { if n == nil { return node{k: s} } if _, ok := n[k]; ok { panic("key " + k + " already set") } n[k] = s return n } func isEmpty(x interface{}) bool { switch y := x.(type) { case string: return y == "" case node: if s, ok := y[""].(string); ok { return s == "" } return false } panic("value is neither string nor node") } func getNode(x interface{}) node { switch y := x.(type) { case string: return node{"": y} case node: return y } panic("value is neither string nor node") } func getString(x interface{}) string { switch y := x.(type) { case string: return y case node: if s, ok := y[""].(string); ok { return s } return "" } panic("value is neither string nor node") } func escape(d, e rune, s string) string { s = strings.Replace(s, string(e), string(e)+string(e), -1) // Escape the escape (\ => \\) s = strings.Replace(s, string(d), string(e)+string(d), -1) // Escape the delimiter (. => \.) return s } func unescape(d, e rune, s string) string { s = strings.Replace(s, string(e)+string(d), string(d), -1) // Unescape the delimiter (\. => .) s = strings.Replace(s, string(e)+string(e), string(e), -1) // Unescape the escape (\\ => \) return s } form-1.5.1/node_test.go000066400000000000000000000042241344402724600150040ustar00rootroot00000000000000// 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 ( "reflect" "testing" ) type foo int type bar interface { void() } type qux struct{} type zee []bar func TestCanIndexOrdinally(t *testing.T) { for _, c := range []struct { x interface{} b bool }{ {int(0), false}, {foo(0), false}, {qux{}, false}, {(*int)(nil), false}, {(*foo)(nil), false}, {(*bar)(nil), false}, {(*qux)(nil), false}, {[]qux{}, true}, {[5]qux{}, true}, {&[]foo{}, true}, {&[5]foo{}, true}, {zee{}, true}, {&zee{}, true}, {map[int]foo{}, false}, {map[string]interface{}{}, false}, {map[interface{}]bar{}, false}, {(chan<- int)(nil), false}, {(chan bar)(nil), false}, {(<-chan foo)(nil), false}, } { v := reflect.ValueOf(c.x) if b := canIndexOrdinally(v); b != c.b { t.Errorf("canIndexOrdinally(%#v)\n want (%#v)\n have (%#v)", v, c.b, b) } } } var escapingTestCases = []struct { a, b string d, e rune }{ {"Foo", "Foo", defaultDelimiter, defaultEscape}, {"Foo", "Foo", '/', '^'}, {"Foo.Bar.Qux", "Foo\\.Bar\\.Qux", defaultDelimiter, defaultEscape}, {"Foo.Bar.Qux", "Foo.Bar.Qux", '/', '^'}, {"Foo/Bar/Qux", "Foo/Bar/Qux", defaultDelimiter, defaultEscape}, {"Foo/Bar/Qux", "Foo^/Bar^/Qux", '/', '^'}, {"0", "0", defaultDelimiter, defaultEscape}, {"0", "0", '/', '^'}, {"0.1.2", "0\\.1\\.2", defaultDelimiter, defaultEscape}, {"0.1.2", "0.1.2", '/', '^'}, {"0/1/2", "0/1/2", defaultDelimiter, defaultEscape}, {"0/1/2", "0^/1^/2", '/', '^'}, {"A\\B", "A\\\\B", defaultDelimiter, defaultEscape}, {"A\\B", "A\\B", '/', '^'}, {"A^B", "A^B", defaultDelimiter, defaultEscape}, {"A^B", "A^^B", '/', '^'}, } func TestEscape(t *testing.T) { for _, c := range escapingTestCases { if b := escape(c.d, c.e, c.a); b != c.b { t.Errorf("escape(%q, %q, %q)\n want (%#v)\n have (%#v)", c.d, c.e, c.a, c.b, b) } } } func TestUnescape(t *testing.T) { for _, c := range escapingTestCases { if a := unescape(c.d, c.e, c.b); a != c.a { t.Errorf("unescape(%q, %q, %q)\n want (%#v)\n have (%#v)", c.d, c.e, c.b, c.a, a) } } } form-1.5.1/pre-commit.sh000077500000000000000000000010131344402724600150750ustar00rootroot00000000000000#!/bin/bash -eu # TODO: Only colorize messages given a suitable terminal. # FIXME: Handle case in which no stash entry is created due to no changes. printf "\e[30m=== PRE-COMMIT STARTING ===\e[m\n" git stash save --quiet --keep-index --include-untracked if go build -v ./... && go test -v -cover ./... && go vet ./... && golint . && travis-lint; then result=$? printf "\e[32m=== PRE-COMMIT SUCCEEDED ===\e[m\n" else result=$? printf "\e[31m=== PRE-COMMIT FAILED ===\e[m\n" fi git stash pop --quiet exit $result