pax_global_header00006660000000000000000000000064145313341230014511gustar00rootroot0000000000000052 comment=48ddde5701366ade1d3aba346e09bb58430d37c6 cast-1.6.0/000077500000000000000000000000001453133412300124475ustar00rootroot00000000000000cast-1.6.0/.github/000077500000000000000000000000001453133412300140075ustar00rootroot00000000000000cast-1.6.0/.github/.editorconfig000066400000000000000000000000411453133412300164570ustar00rootroot00000000000000[{*.yml,*.yaml}] indent_size = 2 cast-1.6.0/.github/dependabot.yaml000066400000000000000000000003031453133412300167740ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: gomod directory: / schedule: interval: daily - package-ecosystem: github-actions directory: / schedule: interval: daily cast-1.6.0/.github/workflows/000077500000000000000000000000001453133412300160445ustar00rootroot00000000000000cast-1.6.0/.github/workflows/test.yml000066400000000000000000000020541453133412300175470ustar00rootroot00000000000000on: push: branches: [main] pull_request: name: Test permissions: contents: read jobs: test: strategy: matrix: go-version: [1.19.x, 1.20.x, 1.21.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - name: Install Go uses: actions/setup-go@v4 with: go-version: ${{ matrix.go-version }} - name: Install staticcheck if: matrix.go-version == '1.21.x' run: go install honnef.co/go/tools/cmd/staticcheck@latest shell: bash - name: Update PATH run: echo "$(go env GOPATH)/bin" >> $GITHUB_PATH shell: bash - name: Checkout code uses: actions/checkout@v3 - name: Fmt if: matrix.platform != 'windows-latest' # :( run: "diff <(gofmt -d .) <(printf '')" shell: bash - name: Vet run: go vet ./... - name: Staticcheck if: matrix.go-version == '1.21.x' run: staticcheck ./... - name: Test run: go test -race ./... cast-1.6.0/.gitignore000066400000000000000000000004141453133412300144360ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.bench cast-1.6.0/LICENSE000066400000000000000000000020671453133412300134610ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2014 Steve Francia Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.cast-1.6.0/Makefile000066400000000000000000000021361453133412300141110ustar00rootroot00000000000000GOVERSION := $(shell go version | cut -d ' ' -f 3 | cut -d '.' -f 2) .PHONY: check fmt lint test test-race vet test-cover-html help .DEFAULT_GOAL := help check: test-race fmt vet lint ## Run tests and linters test: ## Run tests go test ./... test-race: ## Run tests with race detector go test -race ./... fmt: ## Run gofmt linter ifeq "$(GOVERSION)" "12" @for d in `go list` ; do \ if [ "`gofmt -l -s $$GOPATH/src/$$d | tee /dev/stderr`" ]; then \ echo "^ improperly formatted go files" && echo && exit 1; \ fi \ done endif lint: ## Run golint linter @for d in `go list` ; do \ if [ "`golint $$d | tee /dev/stderr`" ]; then \ echo "^ golint errors!" && echo && exit 1; \ fi \ done vet: ## Run go vet linter @if [ "`go vet | tee /dev/stderr`" ]; then \ echo "^ go vet errors!" && echo && exit 1; \ fi test-cover-html: ## Generate test coverage report go test -coverprofile=coverage.out -covermode=count go tool cover -func=coverage.out help: @grep -E '^[a-zA-Z0-9_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' cast-1.6.0/README.md000066400000000000000000000060571453133412300137360ustar00rootroot00000000000000# cast [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/cast/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/cast/actions/workflows/ci.yaml) [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/cast)](https://pkg.go.dev/mod/github.com/spf13/cast) ![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cast?style=flat-square)](https://goreportcard.com/report/github.com/spf13/cast) Easy and safe casting from one type to another in Go Don’t Panic! ... Cast ## What is Cast? Cast is a library to convert between different go types in a consistent and easy way. Cast provides simple functions to easily convert a number to a string, an interface into a bool, etc. Cast does this intelligently when an obvious conversion is possible. It doesn’t make any attempts to guess what you meant, for example you can only convert a string to an int when it is a string representation of an int such as “8”. Cast was developed for use in [Hugo](https://gohugo.io), a website engine which uses YAML, TOML or JSON for meta data. ## Why use Cast? When working with dynamic data in Go you often need to cast or convert the data from one type into another. Cast goes beyond just using type assertion (though it uses that when possible) to provide a very straightforward and convenient library. If you are working with interfaces to handle things like dynamic content you’ll need an easy way to convert an interface into a given type. This is the library for you. If you are taking in data from YAML, TOML or JSON or other formats which lack full types, then Cast is the library for you. ## Usage Cast provides a handful of To_____ methods. These methods will always return the desired type. **If input is provided that will not convert to that type, the 0 or nil value for that type will be returned**. Cast also provides identical methods To_____E. These return the same result as the To_____ methods, plus an additional error which tells you if it successfully converted. Using these methods you can tell the difference between when the input matched the zero value or when the conversion failed and the zero value was returned. The following examples are merely a sample of what is available. Please review the code for a complete set. ### Example ‘ToString’: cast.ToString("mayonegg") // "mayonegg" cast.ToString(8) // "8" cast.ToString(8.31) // "8.31" cast.ToString([]byte("one time")) // "one time" cast.ToString(nil) // "" var foo interface{} = "one more time" cast.ToString(foo) // "one more time" ### Example ‘ToInt’: cast.ToInt(8) // 8 cast.ToInt(8.31) // 8 cast.ToInt("8") // 8 cast.ToInt(true) // 1 cast.ToInt(false) // 0 var eight interface{} = 8 cast.ToInt(eight) // 8 cast.ToInt(nil) // 0 cast-1.6.0/cast.go000066400000000000000000000076451453133412300137440ustar00rootroot00000000000000// Copyright © 2014 Steve Francia . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. // Package cast provides easy and safe casting in Go. package cast import "time" // ToBool casts an interface to a bool type. func ToBool(i interface{}) bool { v, _ := ToBoolE(i) return v } // ToTime casts an interface to a time.Time type. func ToTime(i interface{}) time.Time { v, _ := ToTimeE(i) return v } func ToTimeInDefaultLocation(i interface{}, location *time.Location) time.Time { v, _ := ToTimeInDefaultLocationE(i, location) return v } // ToDuration casts an interface to a time.Duration type. func ToDuration(i interface{}) time.Duration { v, _ := ToDurationE(i) return v } // ToFloat64 casts an interface to a float64 type. func ToFloat64(i interface{}) float64 { v, _ := ToFloat64E(i) return v } // ToFloat32 casts an interface to a float32 type. func ToFloat32(i interface{}) float32 { v, _ := ToFloat32E(i) return v } // ToInt64 casts an interface to an int64 type. func ToInt64(i interface{}) int64 { v, _ := ToInt64E(i) return v } // ToInt32 casts an interface to an int32 type. func ToInt32(i interface{}) int32 { v, _ := ToInt32E(i) return v } // ToInt16 casts an interface to an int16 type. func ToInt16(i interface{}) int16 { v, _ := ToInt16E(i) return v } // ToInt8 casts an interface to an int8 type. func ToInt8(i interface{}) int8 { v, _ := ToInt8E(i) return v } // ToInt casts an interface to an int type. func ToInt(i interface{}) int { v, _ := ToIntE(i) return v } // ToUint casts an interface to a uint type. func ToUint(i interface{}) uint { v, _ := ToUintE(i) return v } // ToUint64 casts an interface to a uint64 type. func ToUint64(i interface{}) uint64 { v, _ := ToUint64E(i) return v } // ToUint32 casts an interface to a uint32 type. func ToUint32(i interface{}) uint32 { v, _ := ToUint32E(i) return v } // ToUint16 casts an interface to a uint16 type. func ToUint16(i interface{}) uint16 { v, _ := ToUint16E(i) return v } // ToUint8 casts an interface to a uint8 type. func ToUint8(i interface{}) uint8 { v, _ := ToUint8E(i) return v } // ToString casts an interface to a string type. func ToString(i interface{}) string { v, _ := ToStringE(i) return v } // ToStringMapString casts an interface to a map[string]string type. func ToStringMapString(i interface{}) map[string]string { v, _ := ToStringMapStringE(i) return v } // ToStringMapStringSlice casts an interface to a map[string][]string type. func ToStringMapStringSlice(i interface{}) map[string][]string { v, _ := ToStringMapStringSliceE(i) return v } // ToStringMapBool casts an interface to a map[string]bool type. func ToStringMapBool(i interface{}) map[string]bool { v, _ := ToStringMapBoolE(i) return v } // ToStringMapInt casts an interface to a map[string]int type. func ToStringMapInt(i interface{}) map[string]int { v, _ := ToStringMapIntE(i) return v } // ToStringMapInt64 casts an interface to a map[string]int64 type. func ToStringMapInt64(i interface{}) map[string]int64 { v, _ := ToStringMapInt64E(i) return v } // ToStringMap casts an interface to a map[string]interface{} type. func ToStringMap(i interface{}) map[string]interface{} { v, _ := ToStringMapE(i) return v } // ToSlice casts an interface to a []interface{} type. func ToSlice(i interface{}) []interface{} { v, _ := ToSliceE(i) return v } // ToBoolSlice casts an interface to a []bool type. func ToBoolSlice(i interface{}) []bool { v, _ := ToBoolSliceE(i) return v } // ToStringSlice casts an interface to a []string type. func ToStringSlice(i interface{}) []string { v, _ := ToStringSliceE(i) return v } // ToIntSlice casts an interface to a []int type. func ToIntSlice(i interface{}) []int { v, _ := ToIntSliceE(i) return v } // ToDurationSlice casts an interface to a []time.Duration type. func ToDurationSlice(i interface{}) []time.Duration { v, _ := ToDurationSliceE(i) return v } cast-1.6.0/cast_test.go000066400000000000000000001103041453133412300147660ustar00rootroot00000000000000// Copyright © 2014 Steve Francia . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package cast import ( "encoding/json" "errors" "fmt" "html/template" "path" "reflect" "testing" "time" qt "github.com/frankban/quicktest" ) type testStep struct { input interface{} expect interface{} iserr bool } func createNumberTestSteps(zero, one, eight, eightnegative, eightpoint31, eightpoint31negative interface{}) []testStep { var jeight, jminuseight, jfloateight json.Number _ = json.Unmarshal([]byte("8"), &jeight) _ = json.Unmarshal([]byte("-8"), &jminuseight) _ = json.Unmarshal([]byte("8.0"), &jfloateight) kind := reflect.TypeOf(zero).Kind() isUint := kind == reflect.Uint || kind == reflect.Uint8 || kind == reflect.Uint16 || kind == reflect.Uint32 || kind == reflect.Uint64 // Some precision is lost when converting from float64 to float32. eightpoint31_32 := eightpoint31 eightpoint31negative_32 := eightpoint31negative if kind == reflect.Float64 { eightpoint31_32 = float64(float32(eightpoint31.(float64))) eightpoint31negative_32 = float64(float32(eightpoint31negative.(float64))) } return []testStep{ {int(8), eight, false}, {int8(8), eight, false}, {int16(8), eight, false}, {int32(8), eight, false}, {int64(8), eight, false}, {time.Weekday(8), eight, false}, {time.Month(8), eight, false}, {uint(8), eight, false}, {uint8(8), eight, false}, {uint16(8), eight, false}, {uint32(8), eight, false}, {uint64(8), eight, false}, {float32(8.31), eightpoint31_32, false}, {float64(8.31), eightpoint31, false}, {true, one, false}, {false, zero, false}, {"8", eight, false}, {nil, zero, false}, {int(-8), eightnegative, isUint}, {int8(-8), eightnegative, isUint}, {int16(-8), eightnegative, isUint}, {int32(-8), eightnegative, isUint}, {int64(-8), eightnegative, isUint}, {float32(-8.31), eightpoint31negative_32, isUint}, {float64(-8.31), eightpoint31negative, isUint}, {"-8", eightnegative, isUint}, {jeight, eight, false}, {jminuseight, eightnegative, isUint}, {jfloateight, eight, false}, {"test", zero, true}, {testing.T{}, zero, true}, } } // Maybe Go 1.18 generics will make this less ugly? func runNumberTest(c *qt.C, tests []testStep, tove func(interface{}) (interface{}, error), tov func(interface{}) interface{}) { c.Helper() for i, test := range tests { errmsg := qt.Commentf("i = %d", i) v, err := tove(test.input) if test.iserr { c.Assert(err, qt.IsNotNil, errmsg) continue } c.Assert(err, qt.IsNil, errmsg) c.Assert(v, qt.Equals, test.expect, errmsg) // Non-E test: v = tov(test.input) c.Assert(v, qt.Equals, test.expect, errmsg) } } func TestToUintE(t *testing.T) { tests := createNumberTestSteps(uint(0), uint(1), uint(8), uint(0), uint(8), uint(8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToUintE(v) }, func(v interface{}) interface{} { return ToUint(v) }, ) } func TestToUint64E(t *testing.T) { tests := createNumberTestSteps(uint64(0), uint64(1), uint64(8), uint64(0), uint64(8), uint64(8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToUint64E(v) }, func(v interface{}) interface{} { return ToUint64(v) }, ) } func TestToUint32E(t *testing.T) { tests := createNumberTestSteps(uint32(0), uint32(1), uint32(8), uint32(0), uint32(8), uint32(8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToUint32E(v) }, func(v interface{}) interface{} { return ToUint32(v) }, ) } func TestToUint16E(t *testing.T) { tests := createNumberTestSteps(uint16(0), uint16(1), uint16(8), uint16(0), uint16(8), uint16(8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToUint16E(v) }, func(v interface{}) interface{} { return ToUint16(v) }, ) } func TestToUint8E(t *testing.T) { tests := createNumberTestSteps(uint8(0), uint8(1), uint8(8), uint8(0), uint8(8), uint8(8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToUint8E(v) }, func(v interface{}) interface{} { return ToUint8(v) }, ) } func TestToIntE(t *testing.T) { tests := createNumberTestSteps(int(0), int(1), int(8), int(-8), int(8), int(-8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToIntE(v) }, func(v interface{}) interface{} { return ToInt(v) }, ) } func TestToInt64E(t *testing.T) { tests := createNumberTestSteps(int64(0), int64(1), int64(8), int64(-8), int64(8), int64(-8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToInt64E(v) }, func(v interface{}) interface{} { return ToInt64(v) }, ) } func TestToInt32E(t *testing.T) { tests := createNumberTestSteps(int32(0), int32(1), int32(8), int32(-8), int32(8), int32(-8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToInt32E(v) }, func(v interface{}) interface{} { return ToInt32(v) }, ) } func TestToInt16E(t *testing.T) { tests := createNumberTestSteps(int16(0), int16(1), int16(8), int16(-8), int16(8), int16(-8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToInt16E(v) }, func(v interface{}) interface{} { return ToInt16(v) }, ) } func TestToInt8E(t *testing.T) { tests := createNumberTestSteps(int8(0), int8(1), int8(8), int8(-8), int8(8), int8(-8)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToInt8E(v) }, func(v interface{}) interface{} { return ToInt8(v) }, ) } func TestToFloat64E(t *testing.T) { tests := createNumberTestSteps(float64(0), float64(1), float64(8), float64(-8), float64(8.31), float64(-8.31)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToFloat64E(v) }, func(v interface{}) interface{} { return ToFloat64(v) }, ) } func TestToFloat32E(t *testing.T) { tests := createNumberTestSteps(float32(0), float32(1), float32(8), float32(-8), float32(8.31), float32(-8.31)) runNumberTest( qt.New(t), tests, func(v interface{}) (interface{}, error) { return ToFloat32E(v) }, func(v interface{}) interface{} { return ToFloat32(v) }, ) } func TestToStringE(t *testing.T) { c := qt.New(t) var jn json.Number _ = json.Unmarshal([]byte("8"), &jn) type Key struct { k string } key := &Key{"foo"} tests := []struct { input interface{} expect string iserr bool }{ {int(8), "8", false}, {int8(8), "8", false}, {int16(8), "8", false}, {int32(8), "8", false}, {int64(8), "8", false}, {uint(8), "8", false}, {uint8(8), "8", false}, {uint16(8), "8", false}, {uint32(8), "8", false}, {uint64(8), "8", false}, {float32(8.31), "8.31", false}, {float64(8.31), "8.31", false}, {jn, "8", false}, {true, "true", false}, {false, "false", false}, {nil, "", false}, {[]byte("one time"), "one time", false}, {"one more time", "one more time", false}, {template.HTML("one time"), "one time", false}, {template.URL("http://somehost.foo"), "http://somehost.foo", false}, {template.JS("(1+2)"), "(1+2)", false}, {template.CSS("a"), "a", false}, {template.HTMLAttr("a"), "a", false}, // errors {testing.T{}, "", true}, {key, "", true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToStringE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil, errmsg) continue } c.Assert(err, qt.IsNil, errmsg) c.Assert(v, qt.Equals, test.expect, errmsg) // Non-E test v = ToString(test.input) c.Assert(v, qt.Equals, test.expect, errmsg) } } type foo struct { val string } func (x foo) String() string { return x.val } func TestStringerToString(t *testing.T) { c := qt.New(t) var x foo x.val = "bar" c.Assert(ToString(x), qt.Equals, "bar") } type fu struct { val string } func (x fu) Error() string { return x.val } func TestErrorToString(t *testing.T) { c := qt.New(t) var x fu x.val = "bar" c.Assert(ToString(x), qt.Equals, "bar") } func TestStringMapStringSliceE(t *testing.T) { c := qt.New(t) // ToStringMapString inputs/outputs var stringMapString = map[string]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var stringMapInterface = map[string]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var interfaceMapString = map[interface{}]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var interfaceMapInterface = map[interface{}]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} // ToStringMapStringSlice inputs/outputs var stringMapStringSlice = map[string][]string{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} var stringMapInterfaceSlice = map[string][]interface{}{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} var stringMapInterfaceInterfaceSlice = map[string]interface{}{"key 1": []interface{}{"value 1", "value 2", "value 3"}, "key 2": []interface{}{"value 1", "value 2", "value 3"}, "key 3": []interface{}{"value 1", "value 2", "value 3"}} var stringMapStringSingleSliceFieldsResult = map[string][]string{"key 1": {"value", "1"}, "key 2": {"value", "2"}, "key 3": {"value", "3"}} var interfaceMapStringSlice = map[interface{}][]string{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} var interfaceMapInterfaceSlice = map[interface{}][]interface{}{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} var stringMapStringSliceMultiple = map[string][]string{"key 1": {"value 1", "value 2", "value 3"}, "key 2": {"value 1", "value 2", "value 3"}, "key 3": {"value 1", "value 2", "value 3"}} var stringMapStringSliceSingle = map[string][]string{"key 1": {"value 1"}, "key 2": {"value 2"}, "key 3": {"value 3"}} var stringMapInterface1 = map[string]interface{}{"key 1": []string{"value 1"}, "key 2": []string{"value 2"}} var stringMapInterfaceResult1 = map[string][]string{"key 1": {"value 1"}, "key 2": {"value 2"}} var jsonStringMapString = `{"key 1": "value 1", "key 2": "value 2"}` var jsonStringMapStringArray = `{"key 1": ["value 1"], "key 2": ["value 2", "value 3"]}` var jsonStringMapStringArrayResult = map[string][]string{"key 1": {"value 1"}, "key 2": {"value 2", "value 3"}} type Key struct { k string } tests := []struct { input interface{} expect map[string][]string iserr bool }{ {stringMapStringSlice, stringMapStringSlice, false}, {stringMapInterfaceSlice, stringMapStringSlice, false}, {stringMapInterfaceInterfaceSlice, stringMapStringSlice, false}, {stringMapStringSliceMultiple, stringMapStringSlice, false}, {stringMapStringSliceMultiple, stringMapStringSlice, false}, {stringMapString, stringMapStringSliceSingle, false}, {stringMapInterface, stringMapStringSliceSingle, false}, {stringMapInterface1, stringMapInterfaceResult1, false}, {interfaceMapStringSlice, stringMapStringSlice, false}, {interfaceMapInterfaceSlice, stringMapStringSlice, false}, {interfaceMapString, stringMapStringSingleSliceFieldsResult, false}, {interfaceMapInterface, stringMapStringSingleSliceFieldsResult, false}, {jsonStringMapStringArray, jsonStringMapStringArrayResult, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, {map[interface{}]interface{}{"foo": testing.T{}}, nil, true}, {map[interface{}]interface{}{Key{"foo"}: "bar"}, nil, true}, // ToStringE(Key{"foo"}) should fail {jsonStringMapString, nil, true}, {"", nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToStringMapStringSliceE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil, errmsg) continue } c.Assert(err, qt.IsNil, errmsg) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToStringMapStringSlice(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToStringMapE(t *testing.T) { c := qt.New(t) tests := []struct { input interface{} expect map[string]interface{} iserr bool }{ {map[interface{}]interface{}{"tag": "tags", "group": "groups"}, map[string]interface{}{"tag": "tags", "group": "groups"}, false}, {map[string]interface{}{"tag": "tags", "group": "groups"}, map[string]interface{}{"tag": "tags", "group": "groups"}, false}, {`{"tag": "tags", "group": "groups"}`, map[string]interface{}{"tag": "tags", "group": "groups"}, false}, {`{"tag": "tags", "group": true}`, map[string]interface{}{"tag": "tags", "group": true}, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, {"", nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToStringMapE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil, errmsg) continue } c.Assert(err, qt.IsNil, errmsg) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToStringMap(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToStringMapBoolE(t *testing.T) { c := qt.New(t) tests := []struct { input interface{} expect map[string]bool iserr bool }{ {map[interface{}]interface{}{"v1": true, "v2": false}, map[string]bool{"v1": true, "v2": false}, false}, {map[string]interface{}{"v1": true, "v2": false}, map[string]bool{"v1": true, "v2": false}, false}, {map[string]bool{"v1": true, "v2": false}, map[string]bool{"v1": true, "v2": false}, false}, {`{"v1": true, "v2": false}`, map[string]bool{"v1": true, "v2": false}, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, {"", nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToStringMapBoolE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil, errmsg) continue } c.Assert(err, qt.IsNil, errmsg) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToStringMapBool(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToStringMapIntE(t *testing.T) { c := qt.New(t) tests := []struct { input interface{} expect map[string]int iserr bool }{ {map[interface{}]interface{}{"v1": 1, "v2": 222}, map[string]int{"v1": 1, "v2": 222}, false}, {map[string]interface{}{"v1": 342, "v2": 5141}, map[string]int{"v1": 342, "v2": 5141}, false}, {map[string]int{"v1": 33, "v2": 88}, map[string]int{"v1": 33, "v2": 88}, false}, {map[string]int32{"v1": int32(33), "v2": int32(88)}, map[string]int{"v1": 33, "v2": 88}, false}, {map[string]uint16{"v1": uint16(33), "v2": uint16(88)}, map[string]int{"v1": 33, "v2": 88}, false}, {map[string]float64{"v1": float64(8.22), "v2": float64(43.32)}, map[string]int{"v1": 8, "v2": 43}, false}, {`{"v1": 67, "v2": 56}`, map[string]int{"v1": 67, "v2": 56}, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, {"", nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToStringMapIntE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil, errmsg) continue } c.Assert(err, qt.IsNil, errmsg) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToStringMapInt(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToStringMapInt64E(t *testing.T) { c := qt.New(t) tests := []struct { input interface{} expect map[string]int64 iserr bool }{ {map[interface{}]interface{}{"v1": int32(8), "v2": int32(888)}, map[string]int64{"v1": int64(8), "v2": int64(888)}, false}, {map[string]interface{}{"v1": int64(45), "v2": int64(67)}, map[string]int64{"v1": 45, "v2": 67}, false}, {map[string]int64{"v1": 33, "v2": 88}, map[string]int64{"v1": 33, "v2": 88}, false}, {map[string]int{"v1": 33, "v2": 88}, map[string]int64{"v1": 33, "v2": 88}, false}, {map[string]int32{"v1": int32(33), "v2": int32(88)}, map[string]int64{"v1": 33, "v2": 88}, false}, {map[string]uint16{"v1": uint16(33), "v2": uint16(88)}, map[string]int64{"v1": 33, "v2": 88}, false}, {map[string]float64{"v1": float64(8.22), "v2": float64(43.32)}, map[string]int64{"v1": 8, "v2": 43}, false}, {`{"v1": 67, "v2": 56}`, map[string]int64{"v1": 67, "v2": 56}, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, {"", nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToStringMapInt64E(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToStringMapInt64(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToStringMapStringE(t *testing.T) { c := qt.New(t) var stringMapString = map[string]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var stringMapInterface = map[string]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var interfaceMapString = map[interface{}]string{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var interfaceMapInterface = map[interface{}]interface{}{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"} var jsonString = `{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"}` var invalidJsonString = `{"key 1": "value 1", "key 2": "value 2", "key 3": "value 3"` var emptyString = "" tests := []struct { input interface{} expect map[string]string iserr bool }{ {stringMapString, stringMapString, false}, {stringMapInterface, stringMapString, false}, {interfaceMapString, stringMapString, false}, {interfaceMapInterface, stringMapString, false}, {jsonString, stringMapString, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, {invalidJsonString, nil, true}, {emptyString, nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToStringMapStringE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToStringMapString(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToBoolSliceE(t *testing.T) { c := qt.New(t) tests := []struct { input interface{} expect []bool iserr bool }{ {[]bool{true, false, true}, []bool{true, false, true}, false}, {[]interface{}{true, false, true}, []bool{true, false, true}, false}, {[]int{1, 0, 1}, []bool{true, false, true}, false}, {[]string{"true", "false", "true"}, []bool{true, false, true}, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, {[]string{"foo", "bar"}, nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToBoolSliceE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToBoolSlice(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToIntSliceE(t *testing.T) { c := qt.New(t) tests := []struct { input interface{} expect []int iserr bool }{ {[]int{1, 3}, []int{1, 3}, false}, {[]interface{}{1.2, 3.2}, []int{1, 3}, false}, {[]string{"2", "3"}, []int{2, 3}, false}, {[2]string{"2", "3"}, []int{2, 3}, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, {[]string{"foo", "bar"}, nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToIntSliceE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToIntSlice(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToSliceE(t *testing.T) { c := qt.New(t) tests := []struct { input interface{} expect []interface{} iserr bool }{ {[]interface{}{1, 3}, []interface{}{1, 3}, false}, {[]map[string]interface{}{{"k1": 1}, {"k2": 2}}, []interface{}{map[string]interface{}{"k1": 1}, map[string]interface{}{"k2": 2}}, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToSliceE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToSlice(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToStringSliceE(t *testing.T) { c := qt.New(t) tests := []struct { input interface{} expect []string iserr bool }{ {[]int{1, 2}, []string{"1", "2"}, false}, {[]int8{int8(1), int8(2)}, []string{"1", "2"}, false}, {[]int32{int32(1), int32(2)}, []string{"1", "2"}, false}, {[]int64{int64(1), int64(2)}, []string{"1", "2"}, false}, {[]float32{float32(1.01), float32(2.01)}, []string{"1.01", "2.01"}, false}, {[]float64{float64(1.01), float64(2.01)}, []string{"1.01", "2.01"}, false}, {[]string{"a", "b"}, []string{"a", "b"}, false}, {[]interface{}{1, 3}, []string{"1", "3"}, false}, {interface{}(1), []string{"1"}, false}, {[]error{errors.New("a"), errors.New("b")}, []string{"a", "b"}, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToStringSliceE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToStringSlice(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToDurationSliceE(t *testing.T) { c := qt.New(t) tests := []struct { input interface{} expect []time.Duration iserr bool }{ {[]string{"1s", "1m"}, []time.Duration{time.Second, time.Minute}, false}, {[]int{1, 2}, []time.Duration{1, 2}, false}, {[]interface{}{1, 3}, []time.Duration{1, 3}, false}, {[]time.Duration{1, 3}, []time.Duration{1, 3}, false}, // errors {nil, nil, true}, {testing.T{}, nil, true}, {[]string{"invalid"}, nil, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToDurationSliceE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v, qt.DeepEquals, test.expect, errmsg) // Non-E test v = ToDurationSlice(test.input) c.Assert(v, qt.DeepEquals, test.expect, errmsg) } } func TestToBoolE(t *testing.T) { c := qt.New(t) var jf, jt, je json.Number _ = json.Unmarshal([]byte("0"), &jf) _ = json.Unmarshal([]byte("1"), &jt) _ = json.Unmarshal([]byte("1.0"), &je) tests := []struct { input interface{} expect bool iserr bool }{ {0, false, false}, {int64(0), false, false}, {int32(0), false, false}, {int16(0), false, false}, {int8(0), false, false}, {uint(0), false, false}, {uint64(0), false, false}, {uint32(0), false, false}, {uint16(0), false, false}, {uint8(0), false, false}, {float64(0), false, false}, {float32(0), false, false}, {time.Duration(0), false, false}, {jf, false, false}, {nil, false, false}, {"false", false, false}, {"FALSE", false, false}, {"False", false, false}, {"f", false, false}, {"F", false, false}, {false, false, false}, {"true", true, false}, {"TRUE", true, false}, {"True", true, false}, {"t", true, false}, {"T", true, false}, {1, true, false}, {int64(1), true, false}, {int32(1), true, false}, {int16(1), true, false}, {int8(1), true, false}, {uint(1), true, false}, {uint64(1), true, false}, {uint32(1), true, false}, {uint16(1), true, false}, {uint8(1), true, false}, {float64(1), true, false}, {float32(1), true, false}, {time.Duration(1), true, false}, {jt, true, false}, {je, true, false}, {true, true, false}, {-1, true, false}, {int64(-1), true, false}, {int32(-1), true, false}, {int16(-1), true, false}, {int8(-1), true, false}, // errors {"test", false, true}, {testing.T{}, false, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToBoolE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v, qt.Equals, test.expect, errmsg) // Non-E test v = ToBool(test.input) c.Assert(v, qt.Equals, test.expect, errmsg) } } func BenchmarkTooBool(b *testing.B) { for i := 0; i < b.N; i++ { if !ToBool(true) { b.Fatal("ToBool returned false") } } } func BenchmarkTooInt(b *testing.B) { convert := func(num52 interface{}) { if v := ToInt(num52); v != 52 { b.Fatalf("ToInt returned wrong value, got %d, want %d", v, 32) } } for i := 0; i < b.N; i++ { convert("52") convert(52.0) convert(uint64(52)) } } func BenchmarkTrimZeroDecimal(b *testing.B) { for i := 0; i < b.N; i++ { trimZeroDecimal("") trimZeroDecimal("123") trimZeroDecimal("120") trimZeroDecimal("120.00") } } func BenchmarkCommonTimeLayouts(b *testing.B) { for i := 0; i < b.N; i++ { for _, commonLayout := range []string{"2019-04-29", "2017-05-30T00:00:00Z"} { _, err := StringToDateInDefaultLocation(commonLayout, time.UTC) if err != nil { b.Fatal(err) } } } } func TestIndirectPointers(t *testing.T) { c := qt.New(t) x := 13 y := &x z := &y c.Assert(ToInt(y), qt.Equals, 13) c.Assert(ToInt(z), qt.Equals, 13) } func TestToTime(t *testing.T) { c := qt.New(t) var jntime, jnetime json.Number _ = json.Unmarshal([]byte("1234567890"), &jntime) _ = json.Unmarshal([]byte("123.4567890"), &jnetime) tests := []struct { input interface{} expect time.Time iserr bool }{ {"2009-11-10 23:00:00 +0000 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // Time.String() {"Tue Nov 10 23:00:00 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // ANSIC {"Tue Nov 10 23:00:00 UTC 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // UnixDate {"Tue Nov 10 23:00:00 +0000 2009", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RubyDate {"10 Nov 09 23:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC822 {"10 Nov 09 23:00 +0000", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC822Z {"Tuesday, 10-Nov-09 23:00:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC850 {"Tue, 10 Nov 2009 23:00:00 UTC", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC1123 {"Tue, 10 Nov 2009 23:00:00 +0000", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC1123Z {"2009-11-10T23:00:00Z", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC3339 {"2018-10-21T23:21:29+0200", time.Date(2018, 10, 21, 21, 21, 29, 0, time.UTC), false}, // RFC3339 without timezone hh:mm colon {"2009-11-10T23:00:00Z", time.Date(2009, 11, 10, 23, 0, 0, 0, time.UTC), false}, // RFC3339Nano {"11:00PM", time.Date(0, 1, 1, 23, 0, 0, 0, time.UTC), false}, // Kitchen {"Nov 10 23:00:00", time.Date(0, 11, 10, 23, 0, 0, 0, time.UTC), false}, // Stamp {"Nov 10 23:00:00.000", time.Date(0, 11, 10, 23, 0, 0, 0, time.UTC), false}, // StampMilli {"Nov 10 23:00:00.000000", time.Date(0, 11, 10, 23, 0, 0, 0, time.UTC), false}, // StampMicro {"Nov 10 23:00:00.000000000", time.Date(0, 11, 10, 23, 0, 0, 0, time.UTC), false}, // StampNano {"2016-03-06 15:28:01-00:00", time.Date(2016, 3, 6, 15, 28, 1, 0, time.UTC), false}, // RFC3339 without T {"2016-03-06 15:28:01-0000", time.Date(2016, 3, 6, 15, 28, 1, 0, time.UTC), false}, // RFC3339 without T or timezone hh:mm colon {"2016-03-06 15:28:01", time.Date(2016, 3, 6, 15, 28, 1, 0, time.UTC), false}, {"2016-03-06 15:28:01 -0000", time.Date(2016, 3, 6, 15, 28, 1, 0, time.UTC), false}, {"2016-03-06 15:28:01 -00:00", time.Date(2016, 3, 6, 15, 28, 1, 0, time.UTC), false}, {"2016-03-06 15:28:01 +0900", time.Date(2016, 3, 6, 6, 28, 1, 0, time.UTC), false}, {"2016-03-06 15:28:01 +09:00", time.Date(2016, 3, 6, 6, 28, 1, 0, time.UTC), false}, {"2006-01-02", time.Date(2006, 1, 2, 0, 0, 0, 0, time.UTC), false}, {"02 Jan 2006", time.Date(2006, 1, 2, 0, 0, 0, 0, time.UTC), false}, {1472574600, time.Date(2016, 8, 30, 16, 30, 0, 0, time.UTC), false}, {int(1482597504), time.Date(2016, 12, 24, 16, 38, 24, 0, time.UTC), false}, {int64(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, {int32(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, {uint(1482597504), time.Date(2016, 12, 24, 16, 38, 24, 0, time.UTC), false}, {uint64(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, {uint32(1234567890), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, {jntime, time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, {time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), time.Date(2009, 2, 13, 23, 31, 30, 0, time.UTC), false}, // errors {"2006", time.Time{}, true}, {jnetime, time.Time{}, true}, {testing.T{}, time.Time{}, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToTimeE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v.UTC(), qt.Equals, test.expect, errmsg) // Non-E test v = ToTime(test.input) c.Assert(v.UTC(), qt.Equals, test.expect, errmsg) } } func TestToDurationE(t *testing.T) { c := qt.New(t) var td time.Duration = 5 var jn json.Number _ = json.Unmarshal([]byte("5"), &jn) tests := []struct { input interface{} expect time.Duration iserr bool }{ {time.Duration(5), td, false}, {int(5), td, false}, {int64(5), td, false}, {int32(5), td, false}, {int16(5), td, false}, {int8(5), td, false}, {uint(5), td, false}, {uint64(5), td, false}, {uint32(5), td, false}, {uint16(5), td, false}, {uint8(5), td, false}, {float64(5), td, false}, {float32(5), td, false}, {jn, td, false}, {string("5"), td, false}, {string("5ns"), td, false}, {string("5us"), time.Microsecond * td, false}, {string("5µs"), time.Microsecond * td, false}, {string("5ms"), time.Millisecond * td, false}, {string("5s"), time.Second * td, false}, {string("5m"), time.Minute * td, false}, {string("5h"), time.Hour * td, false}, // errors {"test", 0, true}, {testing.T{}, 0, true}, } for i, test := range tests { errmsg := qt.Commentf("i = %d", i) // assert helper message v, err := ToDurationE(test.input) if test.iserr { c.Assert(err, qt.IsNotNil) continue } c.Assert(err, qt.IsNil) c.Assert(v, qt.Equals, test.expect, errmsg) // Non-E test v = ToDuration(test.input) c.Assert(v, qt.Equals, test.expect, errmsg) } } func TestToTimeWithTimezones(t *testing.T) { c := qt.New(t) est, err := time.LoadLocation("EST") c.Assert(err, qt.IsNil) irn, err := time.LoadLocation("Iran") c.Assert(err, qt.IsNil) swd, err := time.LoadLocation("Europe/Stockholm") c.Assert(err, qt.IsNil) // Test same local time in different timezones utc2016 := time.Date(2016, time.January, 1, 0, 0, 0, 0, time.UTC) est2016 := time.Date(2016, time.January, 1, 0, 0, 0, 0, est) irn2016 := time.Date(2016, time.January, 1, 0, 0, 0, 0, irn) swd2016 := time.Date(2016, time.January, 1, 0, 0, 0, 0, swd) loc2016 := time.Date(2016, time.January, 1, 0, 0, 0, 0, time.Local) for i, format := range timeFormats { format := format if format.typ == timeFormatTimeOnly { continue } nameBase := fmt.Sprintf("%d;timeFormatType=%d;%s", i, format.typ, format.format) t.Run(path.Join(nameBase), func(t *testing.T) { est2016str := est2016.Format(format.format) swd2016str := swd2016.Format(format.format) t.Run("without default location", func(t *testing.T) { c := qt.New(t) converted, err := ToTimeE(est2016str) c.Assert(err, qt.IsNil) if format.hasTimezone() { // Converting inputs with a timezone should preserve it assertTimeEqual(t, est2016, converted) assertLocationEqual(t, est, converted.Location()) } else { // Converting inputs without a timezone should be interpreted // as a local time in UTC. assertTimeEqual(t, utc2016, converted) assertLocationEqual(t, time.UTC, converted.Location()) } }) t.Run("local timezone without a default location", func(t *testing.T) { c := qt.New(t) converted, err := ToTimeE(swd2016str) c.Assert(err, qt.IsNil) if format.hasTimezone() { // Converting inputs with a timezone should preserve it assertTimeEqual(t, swd2016, converted) assertLocationEqual(t, swd, converted.Location()) } else { // Converting inputs without a timezone should be interpreted // as a local time in UTC. assertTimeEqual(t, utc2016, converted) assertLocationEqual(t, time.UTC, converted.Location()) } }) t.Run("nil default location", func(t *testing.T) { c := qt.New(t) converted, err := ToTimeInDefaultLocationE(est2016str, nil) c.Assert(err, qt.IsNil) if format.hasTimezone() { // Converting inputs with a timezone should preserve it assertTimeEqual(t, est2016, converted) assertLocationEqual(t, est, converted.Location()) } else { // Converting inputs without a timezone should be interpreted // as a local time in the local timezone. assertTimeEqual(t, loc2016, converted) assertLocationEqual(t, time.Local, converted.Location()) } }) t.Run("default location not UTC", func(t *testing.T) { c := qt.New(t) converted, err := ToTimeInDefaultLocationE(est2016str, irn) c.Assert(err, qt.IsNil) if format.hasTimezone() { // Converting inputs with a timezone should preserve it assertTimeEqual(t, est2016, converted) assertLocationEqual(t, est, converted.Location()) } else { // Converting inputs without a timezone should be interpreted // as a local time in the given location. assertTimeEqual(t, irn2016, converted) assertLocationEqual(t, irn, converted.Location()) } }) t.Run("time in the local timezone default location not UTC", func(t *testing.T) { c := qt.New(t) converted, err := ToTimeInDefaultLocationE(swd2016str, irn) c.Assert(err, qt.IsNil) if format.hasTimezone() { // Converting inputs with a timezone should preserve it assertTimeEqual(t, swd2016, converted) assertLocationEqual(t, swd, converted.Location()) } else { // Converting inputs without a timezone should be interpreted // as a local time in the given location. assertTimeEqual(t, irn2016, converted) assertLocationEqual(t, irn, converted.Location()) } }) }) } } func TestTrimZeroDecimal(t *testing.T) { c := qt.New(t) c.Assert(trimZeroDecimal("10.0"), qt.Equals, "10") c.Assert(trimZeroDecimal("10.00"), qt.Equals, "10") c.Assert(trimZeroDecimal("10.010"), qt.Equals, "10.010") c.Assert(trimZeroDecimal("0.0000000000"), qt.Equals, "0") c.Assert(trimZeroDecimal("0.00000000001"), qt.Equals, "0.00000000001") } func assertTimeEqual(t *testing.T, expected, actual time.Time) { t.Helper() // Compare the dates using a numeric zone as there are cases where // time.Parse will assign a dummy location. qt.Assert(t, actual.Format(time.RFC1123Z), qt.Equals, expected.Format(time.RFC1123Z)) } func assertLocationEqual(t *testing.T, expected, actual *time.Location) { t.Helper() qt.Assert(t, locationEqual(expected, actual), qt.IsTrue) } func locationEqual(a, b *time.Location) bool { // A note about comparring time.Locations: // - can't only compare pointers // - can't compare loc.String() because locations with the same // name can have different offsets // - can't use reflect.DeepEqual because time.Location has internal // caches if a == b { return true } else if a == nil || b == nil { return false } // Check if they're equal by parsing times with a format that doesn't // include a timezone, which will interpret it as being a local time in // the given zone, and comparing the resulting local times. tA, err := time.ParseInLocation("2006-01-02", "2016-01-01", a) if err != nil { return false } tB, err := time.ParseInLocation("2006-01-02", "2016-01-01", b) if err != nil { return false } return tA.Equal(tB) } cast-1.6.0/caste.go000066400000000000000000001014131453133412300140750ustar00rootroot00000000000000// Copyright © 2014 Steve Francia . // // Use of this source code is governed by an MIT-style // license that can be found in the LICENSE file. package cast import ( "encoding/json" "errors" "fmt" "html/template" "reflect" "strconv" "strings" "time" ) var errNegativeNotAllowed = errors.New("unable to cast negative value") // ToTimeE casts an interface to a time.Time type. func ToTimeE(i interface{}) (tim time.Time, err error) { return ToTimeInDefaultLocationE(i, time.UTC) } // ToTimeInDefaultLocationE casts an empty interface to time.Time, // interpreting inputs without a timezone to be in the given location, // or the local timezone if nil. func ToTimeInDefaultLocationE(i interface{}, location *time.Location) (tim time.Time, err error) { i = indirect(i) switch v := i.(type) { case time.Time: return v, nil case string: return StringToDateInDefaultLocation(v, location) case json.Number: s, err1 := ToInt64E(v) if err1 != nil { return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i) } return time.Unix(s, 0), nil case int: return time.Unix(int64(v), 0), nil case int64: return time.Unix(v, 0), nil case int32: return time.Unix(int64(v), 0), nil case uint: return time.Unix(int64(v), 0), nil case uint64: return time.Unix(int64(v), 0), nil case uint32: return time.Unix(int64(v), 0), nil default: return time.Time{}, fmt.Errorf("unable to cast %#v of type %T to Time", i, i) } } // ToDurationE casts an interface to a time.Duration type. func ToDurationE(i interface{}) (d time.Duration, err error) { i = indirect(i) switch s := i.(type) { case time.Duration: return s, nil case int, int64, int32, int16, int8, uint, uint64, uint32, uint16, uint8: d = time.Duration(ToInt64(s)) return case float32, float64: d = time.Duration(ToFloat64(s)) return case string: if strings.ContainsAny(s, "nsuµmh") { d, err = time.ParseDuration(s) } else { d, err = time.ParseDuration(s + "ns") } return case json.Number: var v float64 v, err = s.Float64() d = time.Duration(v) return default: err = fmt.Errorf("unable to cast %#v of type %T to Duration", i, i) return } } // ToBoolE casts an interface to a bool type. func ToBoolE(i interface{}) (bool, error) { i = indirect(i) switch b := i.(type) { case bool: return b, nil case nil: return false, nil case int: return b != 0, nil case int64: return b != 0, nil case int32: return b != 0, nil case int16: return b != 0, nil case int8: return b != 0, nil case uint: return b != 0, nil case uint64: return b != 0, nil case uint32: return b != 0, nil case uint16: return b != 0, nil case uint8: return b != 0, nil case float64: return b != 0, nil case float32: return b != 0, nil case time.Duration: return b != 0, nil case string: return strconv.ParseBool(i.(string)) case json.Number: v, err := ToInt64E(b) if err == nil { return v != 0, nil } return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i) default: return false, fmt.Errorf("unable to cast %#v of type %T to bool", i, i) } } // ToFloat64E casts an interface to a float64 type. func ToFloat64E(i interface{}) (float64, error) { i = indirect(i) intv, ok := toInt(i) if ok { return float64(intv), nil } switch s := i.(type) { case float64: return s, nil case float32: return float64(s), nil case int64: return float64(s), nil case int32: return float64(s), nil case int16: return float64(s), nil case int8: return float64(s), nil case uint: return float64(s), nil case uint64: return float64(s), nil case uint32: return float64(s), nil case uint16: return float64(s), nil case uint8: return float64(s), nil case string: v, err := strconv.ParseFloat(s, 64) if err == nil { return v, nil } return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i) case json.Number: v, err := s.Float64() if err == nil { return v, nil } return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i) case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i) } } // ToFloat32E casts an interface to a float32 type. func ToFloat32E(i interface{}) (float32, error) { i = indirect(i) intv, ok := toInt(i) if ok { return float32(intv), nil } switch s := i.(type) { case float64: return float32(s), nil case float32: return s, nil case int64: return float32(s), nil case int32: return float32(s), nil case int16: return float32(s), nil case int8: return float32(s), nil case uint: return float32(s), nil case uint64: return float32(s), nil case uint32: return float32(s), nil case uint16: return float32(s), nil case uint8: return float32(s), nil case string: v, err := strconv.ParseFloat(s, 32) if err == nil { return float32(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i) case json.Number: v, err := s.Float64() if err == nil { return float32(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i) case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to float32", i, i) } } // ToInt64E casts an interface to an int64 type. func ToInt64E(i interface{}) (int64, error) { i = indirect(i) intv, ok := toInt(i) if ok { return int64(intv), nil } switch s := i.(type) { case int64: return s, nil case int32: return int64(s), nil case int16: return int64(s), nil case int8: return int64(s), nil case uint: return int64(s), nil case uint64: return int64(s), nil case uint32: return int64(s), nil case uint16: return int64(s), nil case uint8: return int64(s), nil case float64: return int64(s), nil case float32: return int64(s), nil case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { return v, nil } return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) case json.Number: return ToInt64E(string(s)) case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) } } // ToInt32E casts an interface to an int32 type. func ToInt32E(i interface{}) (int32, error) { i = indirect(i) intv, ok := toInt(i) if ok { return int32(intv), nil } switch s := i.(type) { case int64: return int32(s), nil case int32: return s, nil case int16: return int32(s), nil case int8: return int32(s), nil case uint: return int32(s), nil case uint64: return int32(s), nil case uint32: return int32(s), nil case uint16: return int32(s), nil case uint8: return int32(s), nil case float64: return int32(s), nil case float32: return int32(s), nil case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { return int32(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i) case json.Number: return ToInt32E(string(s)) case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to int32", i, i) } } // ToInt16E casts an interface to an int16 type. func ToInt16E(i interface{}) (int16, error) { i = indirect(i) intv, ok := toInt(i) if ok { return int16(intv), nil } switch s := i.(type) { case int64: return int16(s), nil case int32: return int16(s), nil case int16: return s, nil case int8: return int16(s), nil case uint: return int16(s), nil case uint64: return int16(s), nil case uint32: return int16(s), nil case uint16: return int16(s), nil case uint8: return int16(s), nil case float64: return int16(s), nil case float32: return int16(s), nil case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { return int16(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i) case json.Number: return ToInt16E(string(s)) case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to int16", i, i) } } // ToInt8E casts an interface to an int8 type. func ToInt8E(i interface{}) (int8, error) { i = indirect(i) intv, ok := toInt(i) if ok { return int8(intv), nil } switch s := i.(type) { case int64: return int8(s), nil case int32: return int8(s), nil case int16: return int8(s), nil case int8: return s, nil case uint: return int8(s), nil case uint64: return int8(s), nil case uint32: return int8(s), nil case uint16: return int8(s), nil case uint8: return int8(s), nil case float64: return int8(s), nil case float32: return int8(s), nil case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { return int8(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i) case json.Number: return ToInt8E(string(s)) case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to int8", i, i) } } // ToIntE casts an interface to an int type. func ToIntE(i interface{}) (int, error) { i = indirect(i) intv, ok := toInt(i) if ok { return intv, nil } switch s := i.(type) { case int64: return int(s), nil case int32: return int(s), nil case int16: return int(s), nil case int8: return int(s), nil case uint: return int(s), nil case uint64: return int(s), nil case uint32: return int(s), nil case uint16: return int(s), nil case uint8: return int(s), nil case float64: return int(s), nil case float32: return int(s), nil case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { return int(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i) case json.Number: return ToIntE(string(s)) case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to int", i, i) } } // ToUintE casts an interface to a uint type. func ToUintE(i interface{}) (uint, error) { i = indirect(i) intv, ok := toInt(i) if ok { if intv < 0 { return 0, errNegativeNotAllowed } return uint(intv), nil } switch s := i.(type) { case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed } return uint(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i) case json.Number: return ToUintE(string(s)) case int64: if s < 0 { return 0, errNegativeNotAllowed } return uint(s), nil case int32: if s < 0 { return 0, errNegativeNotAllowed } return uint(s), nil case int16: if s < 0 { return 0, errNegativeNotAllowed } return uint(s), nil case int8: if s < 0 { return 0, errNegativeNotAllowed } return uint(s), nil case uint: return s, nil case uint64: return uint(s), nil case uint32: return uint(s), nil case uint16: return uint(s), nil case uint8: return uint(s), nil case float64: if s < 0 { return 0, errNegativeNotAllowed } return uint(s), nil case float32: if s < 0 { return 0, errNegativeNotAllowed } return uint(s), nil case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to uint", i, i) } } // ToUint64E casts an interface to a uint64 type. func ToUint64E(i interface{}) (uint64, error) { i = indirect(i) intv, ok := toInt(i) if ok { if intv < 0 { return 0, errNegativeNotAllowed } return uint64(intv), nil } switch s := i.(type) { case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed } return uint64(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i) case json.Number: return ToUint64E(string(s)) case int64: if s < 0 { return 0, errNegativeNotAllowed } return uint64(s), nil case int32: if s < 0 { return 0, errNegativeNotAllowed } return uint64(s), nil case int16: if s < 0 { return 0, errNegativeNotAllowed } return uint64(s), nil case int8: if s < 0 { return 0, errNegativeNotAllowed } return uint64(s), nil case uint: return uint64(s), nil case uint64: return s, nil case uint32: return uint64(s), nil case uint16: return uint64(s), nil case uint8: return uint64(s), nil case float32: if s < 0 { return 0, errNegativeNotAllowed } return uint64(s), nil case float64: if s < 0 { return 0, errNegativeNotAllowed } return uint64(s), nil case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i) } } // ToUint32E casts an interface to a uint32 type. func ToUint32E(i interface{}) (uint32, error) { i = indirect(i) intv, ok := toInt(i) if ok { if intv < 0 { return 0, errNegativeNotAllowed } return uint32(intv), nil } switch s := i.(type) { case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed } return uint32(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i) case json.Number: return ToUint32E(string(s)) case int64: if s < 0 { return 0, errNegativeNotAllowed } return uint32(s), nil case int32: if s < 0 { return 0, errNegativeNotAllowed } return uint32(s), nil case int16: if s < 0 { return 0, errNegativeNotAllowed } return uint32(s), nil case int8: if s < 0 { return 0, errNegativeNotAllowed } return uint32(s), nil case uint: return uint32(s), nil case uint64: return uint32(s), nil case uint32: return s, nil case uint16: return uint32(s), nil case uint8: return uint32(s), nil case float64: if s < 0 { return 0, errNegativeNotAllowed } return uint32(s), nil case float32: if s < 0 { return 0, errNegativeNotAllowed } return uint32(s), nil case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to uint32", i, i) } } // ToUint16E casts an interface to a uint16 type. func ToUint16E(i interface{}) (uint16, error) { i = indirect(i) intv, ok := toInt(i) if ok { if intv < 0 { return 0, errNegativeNotAllowed } return uint16(intv), nil } switch s := i.(type) { case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed } return uint16(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i) case json.Number: return ToUint16E(string(s)) case int64: if s < 0 { return 0, errNegativeNotAllowed } return uint16(s), nil case int32: if s < 0 { return 0, errNegativeNotAllowed } return uint16(s), nil case int16: if s < 0 { return 0, errNegativeNotAllowed } return uint16(s), nil case int8: if s < 0 { return 0, errNegativeNotAllowed } return uint16(s), nil case uint: return uint16(s), nil case uint64: return uint16(s), nil case uint32: return uint16(s), nil case uint16: return s, nil case uint8: return uint16(s), nil case float64: if s < 0 { return 0, errNegativeNotAllowed } return uint16(s), nil case float32: if s < 0 { return 0, errNegativeNotAllowed } return uint16(s), nil case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to uint16", i, i) } } // ToUint8E casts an interface to a uint type. func ToUint8E(i interface{}) (uint8, error) { i = indirect(i) intv, ok := toInt(i) if ok { if intv < 0 { return 0, errNegativeNotAllowed } return uint8(intv), nil } switch s := i.(type) { case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed } return uint8(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i) case json.Number: return ToUint8E(string(s)) case int64: if s < 0 { return 0, errNegativeNotAllowed } return uint8(s), nil case int32: if s < 0 { return 0, errNegativeNotAllowed } return uint8(s), nil case int16: if s < 0 { return 0, errNegativeNotAllowed } return uint8(s), nil case int8: if s < 0 { return 0, errNegativeNotAllowed } return uint8(s), nil case uint: return uint8(s), nil case uint64: return uint8(s), nil case uint32: return uint8(s), nil case uint16: return uint8(s), nil case uint8: return s, nil case float64: if s < 0 { return 0, errNegativeNotAllowed } return uint8(s), nil case float32: if s < 0 { return 0, errNegativeNotAllowed } return uint8(s), nil case bool: if s { return 1, nil } return 0, nil case nil: return 0, nil default: return 0, fmt.Errorf("unable to cast %#v of type %T to uint8", i, i) } } // From html/template/content.go // Copyright 2011 The Go Authors. All rights reserved. // indirect returns the value, after dereferencing as many times // as necessary to reach the base type (or nil). func indirect(a interface{}) interface{} { if a == nil { return nil } if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr { // Avoid creating a reflect.Value if it's not a pointer. return a } v := reflect.ValueOf(a) for v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() } return v.Interface() } // From html/template/content.go // Copyright 2011 The Go Authors. All rights reserved. // indirectToStringerOrError returns the value, after dereferencing as many times // as necessary to reach the base type (or nil) or an implementation of fmt.Stringer // or error, func indirectToStringerOrError(a interface{}) interface{} { if a == nil { return nil } var errorType = reflect.TypeOf((*error)(nil)).Elem() var fmtStringerType = reflect.TypeOf((*fmt.Stringer)(nil)).Elem() v := reflect.ValueOf(a) for !v.Type().Implements(fmtStringerType) && !v.Type().Implements(errorType) && v.Kind() == reflect.Ptr && !v.IsNil() { v = v.Elem() } return v.Interface() } // ToStringE casts an interface to a string type. func ToStringE(i interface{}) (string, error) { i = indirectToStringerOrError(i) switch s := i.(type) { case string: return s, nil case bool: return strconv.FormatBool(s), nil case float64: return strconv.FormatFloat(s, 'f', -1, 64), nil case float32: return strconv.FormatFloat(float64(s), 'f', -1, 32), nil case int: return strconv.Itoa(s), nil case int64: return strconv.FormatInt(s, 10), nil case int32: return strconv.Itoa(int(s)), nil case int16: return strconv.FormatInt(int64(s), 10), nil case int8: return strconv.FormatInt(int64(s), 10), nil case uint: return strconv.FormatUint(uint64(s), 10), nil case uint64: return strconv.FormatUint(uint64(s), 10), nil case uint32: return strconv.FormatUint(uint64(s), 10), nil case uint16: return strconv.FormatUint(uint64(s), 10), nil case uint8: return strconv.FormatUint(uint64(s), 10), nil case json.Number: return s.String(), nil case []byte: return string(s), nil case template.HTML: return string(s), nil case template.URL: return string(s), nil case template.JS: return string(s), nil case template.CSS: return string(s), nil case template.HTMLAttr: return string(s), nil case nil: return "", nil case fmt.Stringer: return s.String(), nil case error: return s.Error(), nil default: return "", fmt.Errorf("unable to cast %#v of type %T to string", i, i) } } // ToStringMapStringE casts an interface to a map[string]string type. func ToStringMapStringE(i interface{}) (map[string]string, error) { var m = map[string]string{} switch v := i.(type) { case map[string]string: return v, nil case map[string]interface{}: for k, val := range v { m[ToString(k)] = ToString(val) } return m, nil case map[interface{}]string: for k, val := range v { m[ToString(k)] = ToString(val) } return m, nil case map[interface{}]interface{}: for k, val := range v { m[ToString(k)] = ToString(val) } return m, nil case string: err := jsonStringToObject(v, &m) return m, err default: return m, fmt.Errorf("unable to cast %#v of type %T to map[string]string", i, i) } } // ToStringMapStringSliceE casts an interface to a map[string][]string type. func ToStringMapStringSliceE(i interface{}) (map[string][]string, error) { var m = map[string][]string{} switch v := i.(type) { case map[string][]string: return v, nil case map[string][]interface{}: for k, val := range v { m[ToString(k)] = ToStringSlice(val) } return m, nil case map[string]string: for k, val := range v { m[ToString(k)] = []string{val} } case map[string]interface{}: for k, val := range v { switch vt := val.(type) { case []interface{}: m[ToString(k)] = ToStringSlice(vt) case []string: m[ToString(k)] = vt default: m[ToString(k)] = []string{ToString(val)} } } return m, nil case map[interface{}][]string: for k, val := range v { m[ToString(k)] = ToStringSlice(val) } return m, nil case map[interface{}]string: for k, val := range v { m[ToString(k)] = ToStringSlice(val) } return m, nil case map[interface{}][]interface{}: for k, val := range v { m[ToString(k)] = ToStringSlice(val) } return m, nil case map[interface{}]interface{}: for k, val := range v { key, err := ToStringE(k) if err != nil { return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i) } value, err := ToStringSliceE(val) if err != nil { return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i) } m[key] = value } case string: err := jsonStringToObject(v, &m) return m, err default: return m, fmt.Errorf("unable to cast %#v of type %T to map[string][]string", i, i) } return m, nil } // ToStringMapBoolE casts an interface to a map[string]bool type. func ToStringMapBoolE(i interface{}) (map[string]bool, error) { var m = map[string]bool{} switch v := i.(type) { case map[interface{}]interface{}: for k, val := range v { m[ToString(k)] = ToBool(val) } return m, nil case map[string]interface{}: for k, val := range v { m[ToString(k)] = ToBool(val) } return m, nil case map[string]bool: return v, nil case string: err := jsonStringToObject(v, &m) return m, err default: return m, fmt.Errorf("unable to cast %#v of type %T to map[string]bool", i, i) } } // ToStringMapE casts an interface to a map[string]interface{} type. func ToStringMapE(i interface{}) (map[string]interface{}, error) { var m = map[string]interface{}{} switch v := i.(type) { case map[interface{}]interface{}: for k, val := range v { m[ToString(k)] = val } return m, nil case map[string]interface{}: return v, nil case string: err := jsonStringToObject(v, &m) return m, err default: return m, fmt.Errorf("unable to cast %#v of type %T to map[string]interface{}", i, i) } } // ToStringMapIntE casts an interface to a map[string]int{} type. func ToStringMapIntE(i interface{}) (map[string]int, error) { var m = map[string]int{} if i == nil { return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int", i, i) } switch v := i.(type) { case map[interface{}]interface{}: for k, val := range v { m[ToString(k)] = ToInt(val) } return m, nil case map[string]interface{}: for k, val := range v { m[k] = ToInt(val) } return m, nil case map[string]int: return v, nil case string: err := jsonStringToObject(v, &m) return m, err } if reflect.TypeOf(i).Kind() != reflect.Map { return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int", i, i) } mVal := reflect.ValueOf(m) v := reflect.ValueOf(i) for _, keyVal := range v.MapKeys() { val, err := ToIntE(v.MapIndex(keyVal).Interface()) if err != nil { return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int", i, i) } mVal.SetMapIndex(keyVal, reflect.ValueOf(val)) } return m, nil } // ToStringMapInt64E casts an interface to a map[string]int64{} type. func ToStringMapInt64E(i interface{}) (map[string]int64, error) { var m = map[string]int64{} if i == nil { return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int64", i, i) } switch v := i.(type) { case map[interface{}]interface{}: for k, val := range v { m[ToString(k)] = ToInt64(val) } return m, nil case map[string]interface{}: for k, val := range v { m[k] = ToInt64(val) } return m, nil case map[string]int64: return v, nil case string: err := jsonStringToObject(v, &m) return m, err } if reflect.TypeOf(i).Kind() != reflect.Map { return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int64", i, i) } mVal := reflect.ValueOf(m) v := reflect.ValueOf(i) for _, keyVal := range v.MapKeys() { val, err := ToInt64E(v.MapIndex(keyVal).Interface()) if err != nil { return m, fmt.Errorf("unable to cast %#v of type %T to map[string]int64", i, i) } mVal.SetMapIndex(keyVal, reflect.ValueOf(val)) } return m, nil } // ToSliceE casts an interface to a []interface{} type. func ToSliceE(i interface{}) ([]interface{}, error) { var s []interface{} switch v := i.(type) { case []interface{}: return append(s, v...), nil case []map[string]interface{}: for _, u := range v { s = append(s, u) } return s, nil default: return s, fmt.Errorf("unable to cast %#v of type %T to []interface{}", i, i) } } // ToBoolSliceE casts an interface to a []bool type. func ToBoolSliceE(i interface{}) ([]bool, error) { if i == nil { return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i) } switch v := i.(type) { case []bool: return v, nil } kind := reflect.TypeOf(i).Kind() switch kind { case reflect.Slice, reflect.Array: s := reflect.ValueOf(i) a := make([]bool, s.Len()) for j := 0; j < s.Len(); j++ { val, err := ToBoolE(s.Index(j).Interface()) if err != nil { return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i) } a[j] = val } return a, nil default: return []bool{}, fmt.Errorf("unable to cast %#v of type %T to []bool", i, i) } } // ToStringSliceE casts an interface to a []string type. func ToStringSliceE(i interface{}) ([]string, error) { var a []string switch v := i.(type) { case []interface{}: for _, u := range v { a = append(a, ToString(u)) } return a, nil case []string: return v, nil case []int8: for _, u := range v { a = append(a, ToString(u)) } return a, nil case []int: for _, u := range v { a = append(a, ToString(u)) } return a, nil case []int32: for _, u := range v { a = append(a, ToString(u)) } return a, nil case []int64: for _, u := range v { a = append(a, ToString(u)) } return a, nil case []float32: for _, u := range v { a = append(a, ToString(u)) } return a, nil case []float64: for _, u := range v { a = append(a, ToString(u)) } return a, nil case string: return strings.Fields(v), nil case []error: for _, err := range i.([]error) { a = append(a, err.Error()) } return a, nil case interface{}: str, err := ToStringE(v) if err != nil { return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i) } return []string{str}, nil default: return a, fmt.Errorf("unable to cast %#v of type %T to []string", i, i) } } // ToIntSliceE casts an interface to a []int type. func ToIntSliceE(i interface{}) ([]int, error) { if i == nil { return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i) } switch v := i.(type) { case []int: return v, nil } kind := reflect.TypeOf(i).Kind() switch kind { case reflect.Slice, reflect.Array: s := reflect.ValueOf(i) a := make([]int, s.Len()) for j := 0; j < s.Len(); j++ { val, err := ToIntE(s.Index(j).Interface()) if err != nil { return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i) } a[j] = val } return a, nil default: return []int{}, fmt.Errorf("unable to cast %#v of type %T to []int", i, i) } } // ToDurationSliceE casts an interface to a []time.Duration type. func ToDurationSliceE(i interface{}) ([]time.Duration, error) { if i == nil { return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i) } switch v := i.(type) { case []time.Duration: return v, nil } kind := reflect.TypeOf(i).Kind() switch kind { case reflect.Slice, reflect.Array: s := reflect.ValueOf(i) a := make([]time.Duration, s.Len()) for j := 0; j < s.Len(); j++ { val, err := ToDurationE(s.Index(j).Interface()) if err != nil { return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i) } a[j] = val } return a, nil default: return []time.Duration{}, fmt.Errorf("unable to cast %#v of type %T to []time.Duration", i, i) } } // StringToDate attempts to parse a string into a time.Time type using a // predefined list of formats. If no suitable format is found, an error is // returned. func StringToDate(s string) (time.Time, error) { return parseDateWith(s, time.UTC, timeFormats) } // StringToDateInDefaultLocation casts an empty interface to a time.Time, // interpreting inputs without a timezone to be in the given location, // or the local timezone if nil. func StringToDateInDefaultLocation(s string, location *time.Location) (time.Time, error) { return parseDateWith(s, location, timeFormats) } type timeFormatType int const ( timeFormatNoTimezone timeFormatType = iota timeFormatNamedTimezone timeFormatNumericTimezone timeFormatNumericAndNamedTimezone timeFormatTimeOnly ) type timeFormat struct { format string typ timeFormatType } func (f timeFormat) hasTimezone() bool { // We don't include the formats with only named timezones, see // https://github.com/golang/go/issues/19694#issuecomment-289103522 return f.typ >= timeFormatNumericTimezone && f.typ <= timeFormatNumericAndNamedTimezone } var ( timeFormats = []timeFormat{ // Keep common formats at the top. {"2006-01-02", timeFormatNoTimezone}, {time.RFC3339, timeFormatNumericTimezone}, {"2006-01-02T15:04:05", timeFormatNoTimezone}, // iso8601 without timezone {time.RFC1123Z, timeFormatNumericTimezone}, {time.RFC1123, timeFormatNamedTimezone}, {time.RFC822Z, timeFormatNumericTimezone}, {time.RFC822, timeFormatNamedTimezone}, {time.RFC850, timeFormatNamedTimezone}, {"2006-01-02 15:04:05.999999999 -0700 MST", timeFormatNumericAndNamedTimezone}, // Time.String() {"2006-01-02T15:04:05-0700", timeFormatNumericTimezone}, // RFC3339 without timezone hh:mm colon {"2006-01-02 15:04:05Z0700", timeFormatNumericTimezone}, // RFC3339 without T or timezone hh:mm colon {"2006-01-02 15:04:05", timeFormatNoTimezone}, {time.ANSIC, timeFormatNoTimezone}, {time.UnixDate, timeFormatNamedTimezone}, {time.RubyDate, timeFormatNumericTimezone}, {"2006-01-02 15:04:05Z07:00", timeFormatNumericTimezone}, {"02 Jan 2006", timeFormatNoTimezone}, {"2006-01-02 15:04:05 -07:00", timeFormatNumericTimezone}, {"2006-01-02 15:04:05 -0700", timeFormatNumericTimezone}, {time.Kitchen, timeFormatTimeOnly}, {time.Stamp, timeFormatTimeOnly}, {time.StampMilli, timeFormatTimeOnly}, {time.StampMicro, timeFormatTimeOnly}, {time.StampNano, timeFormatTimeOnly}, } ) func parseDateWith(s string, location *time.Location, formats []timeFormat) (d time.Time, e error) { for _, format := range formats { if d, e = time.Parse(format.format, s); e == nil { // Some time formats have a zone name, but no offset, so it gets // put in that zone name (not the default one passed in to us), but // without that zone's offset. So set the location manually. if format.typ <= timeFormatNamedTimezone { if location == nil { location = time.Local } year, month, day := d.Date() hour, min, sec := d.Clock() d = time.Date(year, month, day, hour, min, sec, d.Nanosecond(), location) } return } } return d, fmt.Errorf("unable to parse date: %s", s) } // jsonStringToObject attempts to unmarshall a string as JSON into // the object passed as pointer. func jsonStringToObject(s string, v interface{}) error { data := []byte(s) return json.Unmarshal(data, v) } // toInt returns the int value of v if v or v's underlying type // is an int. // Note that this will return false for int64 etc. types. func toInt(v interface{}) (int, bool) { switch v := v.(type) { case int: return v, true case time.Weekday: return int(v), true case time.Month: return int(v), true default: return 0, false } } func trimZeroDecimal(s string) string { var foundZero bool for i := len(s); i > 0; i-- { switch s[i-1] { case '.': if foundZero { return s[:i-1] } case '0': foundZero = true default: return s } } return s } cast-1.6.0/go.mod000066400000000000000000000004231453133412300135540ustar00rootroot00000000000000module github.com/spf13/cast go 1.19 require github.com/frankban/quicktest v1.14.6 require ( github.com/google/go-cmp v0.5.9 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect ) cast-1.6.0/go.sum000066400000000000000000000020131453133412300135760ustar00rootroot00000000000000github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= cast-1.6.0/timeformattype_string.go000066400000000000000000000016461453133412300174440ustar00rootroot00000000000000// Code generated by "stringer -type timeFormatType"; DO NOT EDIT. package cast import "strconv" func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[timeFormatNoTimezone-0] _ = x[timeFormatNamedTimezone-1] _ = x[timeFormatNumericTimezone-2] _ = x[timeFormatNumericAndNamedTimezone-3] _ = x[timeFormatTimeOnly-4] } const _timeFormatType_name = "timeFormatNoTimezonetimeFormatNamedTimezonetimeFormatNumericTimezonetimeFormatNumericAndNamedTimezonetimeFormatTimeOnly" var _timeFormatType_index = [...]uint8{0, 20, 43, 68, 101, 119} func (i timeFormatType) String() string { if i < 0 || i >= timeFormatType(len(_timeFormatType_index)-1) { return "timeFormatType(" + strconv.FormatInt(int64(i), 10) + ")" } return _timeFormatType_name[_timeFormatType_index[i]:_timeFormatType_index[i+1]] }