forked from mirror/cast
Compare commits
16 Commits
date-with-
...
master
Author | SHA1 | Date |
---|---|---|
re | 52f77be4c4 | |
Bjørn Erik Pedersen | 2b0eb0f724 | |
Bjørn Erik Pedersen | 36a7d5b5b8 | |
yveshield | 408043c8fa | |
Bjørn Erik Pedersen | b481d74679 | |
Kento Tsuji | 88075729b0 | |
Bjørn Erik Pedersen | 3f42935ca9 | |
Bjørn Erik Pedersen | 02aebd9419 | |
Bjørn Erik Pedersen | a9f3cbf098 | |
Bjørn Erik Pedersen | 57c98fb578 | |
Bjørn Erik Pedersen | 22b2b540ce | |
Heewa Barfchin | e4dda5f5f1 | |
Bjørn Erik Pedersen | c3e59ced67 | |
Paco Preciado Mendoza | 8d17101741 | |
zheng | 580a25aa7d | |
K4rian | 1ffadf5510 |
|
@ -0,0 +1,28 @@
|
|||
name: Go
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.16.x, 1.17.x, 1.18.x]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Build
|
||||
run: go build -v ./...
|
||||
|
||||
- name: Test
|
||||
run: go test -race -v ./...
|
21
.travis.yml
21
.travis.yml
|
@ -1,21 +0,0 @@
|
|||
language: go
|
||||
env:
|
||||
- GO111MODULE=on
|
||||
sudo: required
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- tip
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- windows
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
exclude:
|
||||
- os: windows
|
||||
go: tip
|
||||
fast_finish: true
|
||||
script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "windows" ]]; then go test -v -race ./...; else make check; fi
|
12
Makefile
12
Makefile
|
@ -1,15 +1,15 @@
|
|||
GOVERSION := $(shell go version | cut -d ' ' -f 3 | cut -d '.' -f 2)
|
||||
|
||||
.PHONY: check fmt test test-race vet test-cover-html help
|
||||
.PHONY: check fmt lint test test-race vet test-cover-html help
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
check: test-race fmt vet ## Run tests and linters
|
||||
check: test-race fmt vet lint ## Run tests and linters
|
||||
|
||||
test: ## Run tests
|
||||
go test ./...
|
||||
|
||||
test-race: ## Run tests with race detector
|
||||
go test -v -race ./...
|
||||
go test -race ./...
|
||||
|
||||
fmt: ## Run gofmt linter
|
||||
ifeq "$(GOVERSION)" "12"
|
||||
|
@ -20,6 +20,12 @@ ifeq "$(GOVERSION)" "12"
|
|||
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 \
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
cast
|
||||
====
|
||||
[![GoDoc](https://godoc.org/github.com/spf13/cast?status.svg)](https://godoc.org/github.com/spf13/cast)
|
||||
[![Build Status](https://api.travis-ci.org/spf13/cast.svg?branch=master)](https://travis-ci.org/spf13/cast)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cast)](https://goreportcard.com/report/github.com/spf13/cast)
|
||||
[![GoDoc](https://godoc.org/git.internal/re/cast?status.svg)](https://godoc.org/git.internal/re/cast)
|
||||
[![Build Status](https://git.internal/re/cast/actions/workflows/go.yml/badge.svg)](https://git.internal/re/cast/actions/workflows/go.yml)
|
||||
[![Go Report Card](https://goreportcard.com/badge/git.internal/re/cast)](https://goreportcard.com/report/git.internal/re/cast)
|
||||
|
||||
Easy and safe casting from one type to another in Go
|
||||
|
||||
|
|
3
cast.go
3
cast.go
|
@ -20,9 +20,6 @@ func ToTime(i interface{}) time.Time {
|
|||
return v
|
||||
}
|
||||
|
||||
// ToTimeInDefaultLocationE casts an empty interface to time.Time,
|
||||
// interpreting inputs without a timezone to be in the given location.
|
||||
// To fall back to the local timezone, use time.Local as the last argument.
|
||||
func ToTimeInDefaultLocation(i interface{}, location *time.Location) time.Time {
|
||||
v, _ := ToTimeInDefaultLocationE(i, location)
|
||||
return v
|
||||
|
|
1038
cast_test.go
1038
cast_test.go
File diff suppressed because it is too large
Load Diff
422
caste.go
422
caste.go
|
@ -20,12 +20,12 @@ 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, nil)
|
||||
return ToTimeInDefaultLocationE(i, time.UTC)
|
||||
}
|
||||
|
||||
// ToTimeInDefaultLocationE casts an empty interface to time.Time,
|
||||
// interpreting inputs without a timezone to be in the given location.
|
||||
// To fall back to the local timezone, use time.Local as the last argument.
|
||||
// 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)
|
||||
|
||||
|
@ -34,6 +34,12 @@ func ToTimeInDefaultLocationE(i interface{}, location *time.Location) (tim 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:
|
||||
|
@ -71,6 +77,11 @@ func ToDurationE(i interface{}) (d time.Duration, err error) {
|
|||
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
|
||||
|
@ -93,6 +104,12 @@ func ToBoolE(i interface{}) (bool, error) {
|
|||
return false, 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)
|
||||
}
|
||||
|
@ -102,13 +119,16 @@ func ToBoolE(i interface{}) (bool, error) {
|
|||
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 int:
|
||||
return float64(s), nil
|
||||
case int64:
|
||||
return float64(s), nil
|
||||
case int32:
|
||||
|
@ -133,11 +153,19 @@ func ToFloat64E(i interface{}) (float64, error) {
|
|||
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)
|
||||
}
|
||||
|
@ -147,13 +175,16 @@ func ToFloat64E(i interface{}) (float64, error) {
|
|||
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 int:
|
||||
return float32(s), nil
|
||||
case int64:
|
||||
return float32(s), nil
|
||||
case int32:
|
||||
|
@ -178,11 +209,19 @@ func ToFloat32E(i interface{}) (float32, error) {
|
|||
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)
|
||||
}
|
||||
|
@ -192,9 +231,12 @@ func ToFloat32E(i interface{}) (float32, error) {
|
|||
func ToInt64E(i interface{}) (int64, error) {
|
||||
i = indirect(i)
|
||||
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
return int64(intv), nil
|
||||
}
|
||||
|
||||
switch s := i.(type) {
|
||||
case int:
|
||||
return int64(s), nil
|
||||
case int64:
|
||||
return s, nil
|
||||
case int32:
|
||||
|
@ -218,11 +260,13 @@ func ToInt64E(i interface{}) (int64, error) {
|
|||
case float32:
|
||||
return int64(s), nil
|
||||
case string:
|
||||
v, err := strconv.ParseInt(s, 0, 0)
|
||||
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
|
||||
|
@ -239,9 +283,12 @@ func ToInt64E(i interface{}) (int64, error) {
|
|||
func ToInt32E(i interface{}) (int32, error) {
|
||||
i = indirect(i)
|
||||
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
return int32(intv), nil
|
||||
}
|
||||
|
||||
switch s := i.(type) {
|
||||
case int:
|
||||
return int32(s), nil
|
||||
case int64:
|
||||
return int32(s), nil
|
||||
case int32:
|
||||
|
@ -265,11 +312,13 @@ func ToInt32E(i interface{}) (int32, error) {
|
|||
case float32:
|
||||
return int32(s), nil
|
||||
case string:
|
||||
v, err := strconv.ParseInt(s, 0, 0)
|
||||
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
|
||||
|
@ -286,9 +335,12 @@ func ToInt32E(i interface{}) (int32, error) {
|
|||
func ToInt16E(i interface{}) (int16, error) {
|
||||
i = indirect(i)
|
||||
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
return int16(intv), nil
|
||||
}
|
||||
|
||||
switch s := i.(type) {
|
||||
case int:
|
||||
return int16(s), nil
|
||||
case int64:
|
||||
return int16(s), nil
|
||||
case int32:
|
||||
|
@ -312,11 +364,13 @@ func ToInt16E(i interface{}) (int16, error) {
|
|||
case float32:
|
||||
return int16(s), nil
|
||||
case string:
|
||||
v, err := strconv.ParseInt(s, 0, 0)
|
||||
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
|
||||
|
@ -333,9 +387,12 @@ func ToInt16E(i interface{}) (int16, error) {
|
|||
func ToInt8E(i interface{}) (int8, error) {
|
||||
i = indirect(i)
|
||||
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
return int8(intv), nil
|
||||
}
|
||||
|
||||
switch s := i.(type) {
|
||||
case int:
|
||||
return int8(s), nil
|
||||
case int64:
|
||||
return int8(s), nil
|
||||
case int32:
|
||||
|
@ -359,11 +416,13 @@ func ToInt8E(i interface{}) (int8, error) {
|
|||
case float32:
|
||||
return int8(s), nil
|
||||
case string:
|
||||
v, err := strconv.ParseInt(s, 0, 0)
|
||||
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
|
||||
|
@ -380,9 +439,12 @@ func ToInt8E(i interface{}) (int8, error) {
|
|||
func ToIntE(i interface{}) (int, error) {
|
||||
i = indirect(i)
|
||||
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
return intv, nil
|
||||
}
|
||||
|
||||
switch s := i.(type) {
|
||||
case int:
|
||||
return s, nil
|
||||
case int64:
|
||||
return int(s), nil
|
||||
case int32:
|
||||
|
@ -406,11 +468,13 @@ func ToIntE(i interface{}) (int, error) {
|
|||
case float32:
|
||||
return int(s), nil
|
||||
case string:
|
||||
v, err := strconv.ParseInt(s, 0, 0)
|
||||
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 int", i, i)
|
||||
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
|
||||
|
@ -427,18 +491,26 @@ func ToIntE(i interface{}) (int, error) {
|
|||
func ToUintE(i interface{}) (uint, error) {
|
||||
i = indirect(i)
|
||||
|
||||
switch s := i.(type) {
|
||||
case string:
|
||||
v, err := strconv.ParseUint(s, 0, 0)
|
||||
if err == nil {
|
||||
return uint(v), nil
|
||||
}
|
||||
return 0, fmt.Errorf("unable to cast %#v to uint: %s", i, err)
|
||||
case int:
|
||||
if s < 0 {
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
if intv < 0 {
|
||||
return 0, errNegativeNotAllowed
|
||||
}
|
||||
return uint(s), nil
|
||||
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
|
||||
|
@ -495,18 +567,26 @@ func ToUintE(i interface{}) (uint, error) {
|
|||
func ToUint64E(i interface{}) (uint64, error) {
|
||||
i = indirect(i)
|
||||
|
||||
switch s := i.(type) {
|
||||
case string:
|
||||
v, err := strconv.ParseUint(s, 0, 64)
|
||||
if err == nil {
|
||||
return v, nil
|
||||
}
|
||||
return 0, fmt.Errorf("unable to cast %#v to uint64: %s", i, err)
|
||||
case int:
|
||||
if s < 0 {
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
if intv < 0 {
|
||||
return 0, errNegativeNotAllowed
|
||||
}
|
||||
return uint64(s), nil
|
||||
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
|
||||
|
@ -563,18 +643,26 @@ func ToUint64E(i interface{}) (uint64, error) {
|
|||
func ToUint32E(i interface{}) (uint32, error) {
|
||||
i = indirect(i)
|
||||
|
||||
switch s := i.(type) {
|
||||
case string:
|
||||
v, err := strconv.ParseUint(s, 0, 32)
|
||||
if err == nil {
|
||||
return uint32(v), nil
|
||||
}
|
||||
return 0, fmt.Errorf("unable to cast %#v to uint32: %s", i, err)
|
||||
case int:
|
||||
if s < 0 {
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
if intv < 0 {
|
||||
return 0, errNegativeNotAllowed
|
||||
}
|
||||
return uint32(s), nil
|
||||
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
|
||||
|
@ -631,18 +719,26 @@ func ToUint32E(i interface{}) (uint32, error) {
|
|||
func ToUint16E(i interface{}) (uint16, error) {
|
||||
i = indirect(i)
|
||||
|
||||
switch s := i.(type) {
|
||||
case string:
|
||||
v, err := strconv.ParseUint(s, 0, 16)
|
||||
if err == nil {
|
||||
return uint16(v), nil
|
||||
}
|
||||
return 0, fmt.Errorf("unable to cast %#v to uint16: %s", i, err)
|
||||
case int:
|
||||
if s < 0 {
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
if intv < 0 {
|
||||
return 0, errNegativeNotAllowed
|
||||
}
|
||||
return uint16(s), nil
|
||||
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
|
||||
|
@ -699,18 +795,26 @@ func ToUint16E(i interface{}) (uint16, error) {
|
|||
func ToUint8E(i interface{}) (uint8, error) {
|
||||
i = indirect(i)
|
||||
|
||||
switch s := i.(type) {
|
||||
case string:
|
||||
v, err := strconv.ParseUint(s, 0, 8)
|
||||
if err == nil {
|
||||
return uint8(v), nil
|
||||
}
|
||||
return 0, fmt.Errorf("unable to cast %#v to uint8: %s", i, err)
|
||||
case int:
|
||||
if s < 0 {
|
||||
intv, ok := toInt(i)
|
||||
if ok {
|
||||
if intv < 0 {
|
||||
return 0, errNegativeNotAllowed
|
||||
}
|
||||
return uint8(s), nil
|
||||
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
|
||||
|
@ -826,15 +930,17 @@ func ToStringE(i interface{}) (string, error) {
|
|||
case int8:
|
||||
return strconv.FormatInt(int64(s), 10), nil
|
||||
case uint:
|
||||
return strconv.FormatInt(int64(s), 10), nil
|
||||
return strconv.FormatUint(uint64(s), 10), nil
|
||||
case uint64:
|
||||
return strconv.FormatInt(int64(s), 10), nil
|
||||
return strconv.FormatUint(uint64(s), 10), nil
|
||||
case uint32:
|
||||
return strconv.FormatInt(int64(s), 10), nil
|
||||
return strconv.FormatUint(uint64(s), 10), nil
|
||||
case uint16:
|
||||
return strconv.FormatInt(int64(s), 10), nil
|
||||
return strconv.FormatUint(uint64(s), 10), nil
|
||||
case uint8:
|
||||
return strconv.FormatInt(int64(s), 10), nil
|
||||
return strconv.FormatUint(uint64(s), 10), nil
|
||||
case json.Number:
|
||||
return s.String(), nil
|
||||
case []byte:
|
||||
return string(s), nil
|
||||
case template.HTML:
|
||||
|
@ -1136,8 +1242,43 @@ func ToStringSliceE(i interface{}) ([]string, error) {
|
|||
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 {
|
||||
|
@ -1211,51 +1352,24 @@ func ToDurationSliceE(i interface{}) ([]time.Duration, error) {
|
|||
// predefined list of formats. If no suitable format is found, an error is
|
||||
// returned.
|
||||
func StringToDate(s string) (time.Time, error) {
|
||||
return parseDateWith(s, nil, timeFormats)
|
||||
return parseDateWith(s, time.UTC, timeFormats)
|
||||
}
|
||||
|
||||
// StringToDateInDefaultLocation to parse a string into a time.Time type using a
|
||||
// predefined list of formats, interpreting inputs without a timezone to be in
|
||||
// the given location.
|
||||
// To fall back to the local timezone, use time.Local as the last argument.
|
||||
// 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)
|
||||
}
|
||||
|
||||
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.
|
||||
// Note that we only do this when we get a location in the new *InDefaultLocation
|
||||
// variants to avoid breaking existing behaviour in ToTime, however
|
||||
// weird that existing behaviour may be.
|
||||
if location != nil && !format.hasNumericTimezone() {
|
||||
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)
|
||||
}
|
||||
|
||||
type timeFormatType int
|
||||
|
||||
const (
|
||||
timeFormatShort timeFormatType = iota // time or date only, no timezone
|
||||
timeFormatNoTimezone
|
||||
|
||||
// All below have some kind of timezone information, a name and/or offset.
|
||||
timeFormatNoTimezone timeFormatType = iota
|
||||
timeFormatNamedTimezone
|
||||
|
||||
// All below have what we consider to be solid timezone information.
|
||||
timeFormatNumericAndNamedTimezone
|
||||
timeFormatNumericTimezone
|
||||
timeFormatNumericAndNamedTimezone
|
||||
timeFormatTimeOnly
|
||||
)
|
||||
|
||||
type timeFormat struct {
|
||||
|
@ -1263,12 +1377,10 @@ type timeFormat struct {
|
|||
typ timeFormatType
|
||||
}
|
||||
|
||||
func (f timeFormat) hasNumericTimezone() bool {
|
||||
return f.typ >= timeFormatNumericAndNamedTimezone
|
||||
}
|
||||
|
||||
func (f timeFormat) hasAnyTimezone() bool {
|
||||
return f.typ >= timeFormatNamedTimezone
|
||||
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 (
|
||||
|
@ -1285,26 +1397,80 @@ var (
|
|||
{"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},
|
||||
// Must try RubyDate before UnixDate, see:
|
||||
// https://github.com/golang/go/issues/32358
|
||||
{time.RubyDate, timeFormatNumericTimezone},
|
||||
{time.UnixDate, timeFormatNamedTimezone},
|
||||
{time.RubyDate, timeFormatNumericTimezone},
|
||||
{"2006-01-02 15:04:05Z07:00", timeFormatNumericTimezone},
|
||||
{"2006-01-02", timeFormatShort},
|
||||
{"02 Jan 2006", timeFormatShort},
|
||||
{"2006-01-02", timeFormatNoTimezone},
|
||||
{"02 Jan 2006", timeFormatNoTimezone},
|
||||
{"2006-01-02 15:04:05 -07:00", timeFormatNumericTimezone},
|
||||
{"2006-01-02 15:04:05 -0700", timeFormatNumericTimezone},
|
||||
{time.Kitchen, timeFormatShort},
|
||||
{time.Stamp, timeFormatShort},
|
||||
{time.StampMilli, timeFormatShort},
|
||||
{time.StampMicro, timeFormatShort},
|
||||
{time.StampNano, timeFormatShort},
|
||||
{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
|
||||
}
|
||||
|
|
14
go.mod
14
go.mod
|
@ -1,7 +1,13 @@
|
|||
module github.com/spf13/cast
|
||||
module git.internal/re/cast
|
||||
|
||||
go 1.18
|
||||
|
||||
require github.com/frankban/quicktest v1.14.3
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/testify v1.2.2
|
||||
github.com/google/go-cmp v0.5.7 // indirect
|
||||
github.com/kr/pretty v0.3.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/rogpeppe/go-internal v1.6.1 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||
)
|
||||
|
|
24
go.sum
24
go.sum
|
@ -1,6 +1,18 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o=
|
||||
github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
// 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]]
|
||||
}
|
Loading…
Reference in New Issue