├── go.mod ├── .github ├── release-drafter.yml └── workflows │ ├── release-drafter.yml │ └── preliminary_review.yml ├── go.sum ├── generic_test.go ├── .gitignore ├── convert_asTime_test.go ├── LICENSE ├── generic.go ├── convert_test.go ├── type_string.go ├── type_time.go ├── type_bool.go ├── type_float.go ├── convert_asBool_test.go ├── type_uint.go ├── type_int.go ├── type_timestamp.go ├── convert_asInt_test.go ├── type_timestamp_milliseconds.go ├── type_timestamp_nanoseconds.go ├── json_marshal_benchmark_test.go ├── convert_asFloat_test.go ├── convert_asUint_test.go ├── json_unmarshal_benchmark_test.go ├── README.md ├── type_url.go ├── type_time_test.go ├── type_string_test.go ├── type_bool_test.go ├── type_float_test.go ├── type_uint_test.go ├── convert.go ├── type_timestamp_test.go ├── type_timestamp_milliseconds_test.go ├── type_timestamp_nanoseconds_test.go ├── type_int_test.go ├── type_url_old_version_test.go └── type_url_test.go /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/usk81/generic/v2 2 | 3 | go 1.12 4 | 5 | require github.com/stretchr/testify v1.3.0 6 | -------------------------------------------------------------------------------- /.github/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name-template: 'v$RESOLVED_VERSION' 2 | tag-template: 'v$RESOLVED_VERSION' 3 | categories: 4 | - title: 'Bugfix' 5 | labels: 6 | - 'bugfix' 7 | - 'bug' 8 | - 'fix' 9 | change-template: '- $TITLE (#$NUMBER) @$AUTHOR' 10 | version-resolver: 11 | major: 12 | labels: 13 | - 'major' 14 | minor: 15 | labels: 16 | - 'minor' 17 | patch: 18 | labels: 19 | - 'patch' 20 | default: patch 21 | template: | 22 | ## Changes 23 | 24 | $CHANGES 25 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 4 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 5 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 6 | github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= 7 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 8 | -------------------------------------------------------------------------------- /.github/workflows/release-drafter.yml: -------------------------------------------------------------------------------- 1 | name: Release Drafter 2 | 3 | on: 4 | push: 5 | # branches to consider in the event; optional, defaults to all 6 | branches: 7 | - master 8 | 9 | jobs: 10 | update_release_draft: 11 | runs-on: ubuntu-latest 12 | steps: 13 | # Drafts your next Release notes as Pull Requests are merged into "master" 14 | - uses: release-drafter/release-drafter@v5 15 | with: 16 | # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml 17 | config-name: release-drafter.yml 18 | env: 19 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 20 | -------------------------------------------------------------------------------- /generic_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import "testing" 4 | 5 | func TestErrInvalidGenericValueNull(t *testing.T) { 6 | expected := "invalid value: (nil)" 7 | err := ErrInvalidGenericValue{Value: nil} 8 | if err.Error() != expected { 9 | t.Errorf("actual:%s, expected:%s", err.Error(), expected) 10 | } 11 | } 12 | 13 | func TestErrInvalidGenericValueString(t *testing.T) { 14 | expected := "invalid value: (string)" 15 | testVal := "aaaaaaa" 16 | err := ErrInvalidGenericValue{Value: testVal} 17 | if err.Error() != expected { 18 | t.Errorf("actual:%s, expected:%s", err.Error(), expected) 19 | } 20 | } 21 | 22 | func TestValidFlagReset(t *testing.T) { 23 | var v ValidFlag = true 24 | v.Reset() 25 | if v { 26 | t.Error("actual:true, expected:false") 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | # for MacOSX 27 | *.DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Icon must end with two \r 32 | Icon 33 | 34 | # Thumbnails 35 | ._* 36 | 37 | # Files that might appear in the root of a volume 38 | .DocumentRevisions-V100 39 | .fseventsd 40 | .Spotlight-V100 41 | .TemporaryItems 42 | .Trashes 43 | .VolumeIcon.icns 44 | .com.apple.timemachine.donotpresent 45 | 46 | # Directories potentially created on remote AFP share 47 | .AppleDB 48 | .AppleDesktop 49 | Network Trash Folder 50 | Temporary Items 51 | .apdisk 52 | -------------------------------------------------------------------------------- /convert_asTime_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func TestAsTimeTime(t *testing.T) { 9 | x := time.Date(2020, time.Month(7), 24, 20, 0, 0, 0, time.FixedZone("Asia/Tokyo", 9*60*60)) 10 | r, v, err := asTime(x) 11 | if err != nil { 12 | t.Errorf("Not Expected error. error:%s", err.Error()) 13 | } 14 | if !v { 15 | t.Error("expected: true, actual: false") 16 | } 17 | if s := r.String(); s != "2020-07-24 20:00:00 +0900 Asia/Tokyo" { 18 | t.Errorf("expected: 2020-07-24 20:00:00 +0900 Asia/Tokyo, actual: %s", s) 19 | } 20 | } 21 | 22 | func TestAsTimeZero(t *testing.T) { 23 | x := time.Time{} 24 | r, v, err := asTime(x) 25 | if err != nil { 26 | t.Errorf("Not Expected error. error:%s", err.Error()) 27 | } 28 | if !v { 29 | t.Error("expected: true, actual: false") 30 | } 31 | if !r.IsZero() { 32 | t.Errorf("expected: time.IsZero is true, actual: %s", r.String()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yusuke Komatsu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /generic.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "bytes" 5 | "database/sql/driver" 6 | "reflect" 7 | ) 8 | 9 | // Type is the interface used as the basis for generic types 10 | type Type interface { 11 | Valid() bool 12 | Value() (driver.Value, error) 13 | Scan(interface{}) error 14 | Set(interface{}) error 15 | Reset() 16 | } 17 | 18 | // ErrInvalidGenericValue is used as error in generic types 19 | type ErrInvalidGenericValue struct { 20 | Value interface{} 21 | } 22 | 23 | // ValidFlag is the flag to check that value is valid 24 | type ValidFlag bool 25 | 26 | var nullBytes = []byte("null") 27 | 28 | // Reset resets ValidFlag 29 | func (v *ValidFlag) Reset() { 30 | *v = false 31 | } 32 | 33 | // Valid validates the specified value is nil or not 34 | func (v ValidFlag) Valid() bool { 35 | return bool(v) 36 | } 37 | 38 | // Error returns error message 39 | func (e ErrInvalidGenericValue) Error() string { 40 | buf := bytes.Buffer{} 41 | buf.WriteString("invalid value: ") 42 | t := reflect.TypeOf(e.Value) 43 | switch t { 44 | case nil: 45 | buf.WriteString("(nil)") 46 | default: 47 | buf.WriteByte('(') 48 | buf.WriteString(t.String()) 49 | buf.WriteByte(')') 50 | } 51 | 52 | return buf.String() 53 | } 54 | -------------------------------------------------------------------------------- /convert_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "net/url" 5 | "reflect" 6 | "testing" 7 | ) 8 | 9 | func Test_asURL(t *testing.T) { 10 | u, _ := url.Parse(testURLString) 11 | 12 | tests := []struct { 13 | name string 14 | args interface{} 15 | wantResult *url.URL 16 | wantIsValid ValidFlag 17 | wantErr bool 18 | }{ 19 | { 20 | name: "nil", 21 | args: nil, 22 | wantResult: nil, 23 | wantIsValid: false, 24 | wantErr: false, 25 | }, 26 | { 27 | name: "string", 28 | args: testURLString, 29 | wantResult: u, 30 | wantIsValid: true, 31 | wantErr: false, 32 | }, 33 | { 34 | name: "url.URL", 35 | args: u, 36 | wantResult: u, 37 | wantIsValid: true, 38 | wantErr: false, 39 | }, 40 | { 41 | name: "int", 42 | args: 1000, 43 | wantResult: nil, 44 | wantIsValid: false, 45 | wantErr: true, 46 | }, 47 | { 48 | name: "bool", 49 | args: true, 50 | wantResult: nil, 51 | wantIsValid: false, 52 | wantErr: true, 53 | }, 54 | } 55 | for _, tt := range tests { 56 | t.Run(tt.name, func(t *testing.T) { 57 | gotResult, gotIsValid, err := asURL(tt.args) 58 | if (err != nil) != tt.wantErr { 59 | t.Errorf("asURL() error = %v, wantErr %v", err, tt.wantErr) 60 | return 61 | } 62 | if !reflect.DeepEqual(gotResult, tt.wantResult) { 63 | t.Errorf("asURL() gotResult = %v, want %v", gotResult, tt.wantResult) 64 | } 65 | if gotIsValid != tt.wantIsValid { 66 | t.Errorf("asURL() gotIsValid = %v, want %v", gotIsValid, tt.wantIsValid) 67 | } 68 | }) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /.github/workflows/preliminary_review.yml: -------------------------------------------------------------------------------- 1 | name: preliminary review 2 | on: 3 | pull_request 4 | 5 | env: 6 | GO111MODULE: on 7 | GOPROXY: https://proxy.golang.org 8 | 9 | jobs: 10 | golangci: 11 | name: linter 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: golangci-lint 16 | uses: golangci/golangci-lint-action@v2 17 | with: 18 | # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. 19 | version: v1.30 20 | # Optional: golangci-lint command line arguments. 21 | args: --enable=goimports 22 | test: 23 | name: test 24 | strategy: 25 | matrix: 26 | go-version: [1.12.x, 1.13.x, 1.14.x, 1.15.x] 27 | os: [ubuntu-latest] 28 | runs-on: ${{ matrix.os }} 29 | steps: 30 | - name: Install Go 31 | uses: actions/setup-go@v2 32 | with: 33 | go-version: ${{ matrix.go-version }} 34 | - name: Set GOPATH and PATH 35 | run: | 36 | echo "::set-env name=GOPATH::$(dirname $GITHUB_WORKSPACE)" 37 | echo "::add-path::$(dirname $GITHUB_WORKSPACE)/bin" 38 | shell: bash 39 | - name: Checkout code 40 | uses: actions/checkout@v2 41 | - uses: actions/cache@v2 42 | with: 43 | path: ~/go/pkg/mod 44 | key: ${{ matrix.os }}-go-${{ hashFiles('**/go.sum') }} 45 | restore-keys: | 46 | ${{ matrix.os }}-go- 47 | - name: Test 48 | run: go test --coverprofile=coverage.coverprofile --covermode=atomic ./... 49 | - name: Upload coverage to Codecov 50 | if: success() && matrix.go-version == '1.14.x' && matrix.os == 'ubuntu-latest' 51 | uses: codecov/codecov-action@v1 52 | with: 53 | fail_ci_if_error: false -------------------------------------------------------------------------------- /type_string.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | ) 7 | 8 | // String is generic string type structure 9 | type String struct { 10 | ValidFlag 11 | string string 12 | } 13 | 14 | // MarshalString return generic.String converting of request data 15 | func MarshalString(x interface{}) (String, error) { 16 | v := String{} 17 | err := v.Scan(x) 18 | return v, err 19 | } 20 | 21 | // MustString return generic.String converting of request data 22 | func MustString(x interface{}) String { 23 | v, err := MarshalString(x) 24 | if err != nil { 25 | panic(err) 26 | } 27 | return v 28 | } 29 | 30 | // Value implements the driver Valuer interface. 31 | func (v String) Value() (driver.Value, error) { 32 | if !v.Valid() { 33 | return nil, nil 34 | } 35 | return v.string, nil 36 | } 37 | 38 | // Scan implements the sql.Scanner interface. 39 | func (v *String) Scan(x interface{}) (err error) { 40 | v.string, v.ValidFlag, err = asString(x) 41 | if err != nil { 42 | v.ValidFlag = false 43 | return err 44 | } 45 | return 46 | } 47 | 48 | // Weak returns string, but if String.ValidFlag is false, returns nil. 49 | func (v String) Weak() interface{} { 50 | i, _ := v.Value() 51 | return i 52 | } 53 | 54 | // Set sets a specified value. 55 | func (v *String) Set(x interface{}) (err error) { 56 | return v.Scan(x) 57 | } 58 | 59 | // String implements the Stringer interface. 60 | func (v String) String() string { 61 | if !v.Valid() { 62 | return "" 63 | } 64 | return v.string 65 | } 66 | 67 | // MarshalJSON implements the json.Marshaler interface. 68 | func (v String) MarshalJSON() ([]byte, error) { 69 | if !v.Valid() { 70 | return nullBytes, nil 71 | } 72 | s := `"` + v.string + `"` 73 | bs := make([]byte, 0, len(s)) 74 | return append(bs, s...), nil 75 | } 76 | 77 | // UnmarshalJSON implements the json.Unmarshaler interface. 78 | func (v *String) UnmarshalJSON(data []byte) error { 79 | if len(data) == 0 { 80 | return nil 81 | } 82 | var in interface{} 83 | if err := json.Unmarshal(data, &in); err != nil { 84 | return err 85 | } 86 | return v.Scan(in) 87 | } 88 | -------------------------------------------------------------------------------- /type_time.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "time" 6 | ) 7 | 8 | // Time is generic time type structure 9 | type Time struct { 10 | ValidFlag 11 | time time.Time 12 | } 13 | 14 | // MarshalTime return generic.Time converting of request data 15 | func MarshalTime(x interface{}) (Time, error) { 16 | v := Time{} 17 | err := v.Scan(x) 18 | return v, err 19 | } 20 | 21 | // MustTime return generic.Time converting of request data 22 | func MustTime(x interface{}) Time { 23 | v, err := MarshalTime(x) 24 | if err != nil { 25 | panic(err) 26 | } 27 | return v 28 | } 29 | 30 | // Value implements the driver Valuer interface. 31 | func (v Time) Value() (driver.Value, error) { 32 | if !v.Valid() { 33 | return nil, nil 34 | } 35 | return v.time, nil 36 | } 37 | 38 | // Scan implements the sql.Scanner interface. 39 | func (v *Time) Scan(x interface{}) (err error) { 40 | v.time, v.ValidFlag, err = asTime(x) 41 | if err != nil { 42 | v.ValidFlag = false 43 | return err 44 | } 45 | return 46 | } 47 | 48 | // Weak returns Time.Time, but if Time.ValidFlag is false, returns nil. 49 | func (v Time) Weak() interface{} { 50 | i, _ := v.Value() 51 | return i 52 | } 53 | 54 | // Set sets a specified value. 55 | func (v *Time) Set(x interface{}) (err error) { 56 | return v.Scan(x) 57 | } 58 | 59 | // String implements the Stringer interface. 60 | func (v Time) String() string { 61 | if !v.Valid() { 62 | return "" 63 | } 64 | return v.time.String() 65 | } 66 | 67 | // Time returns value as time.Time 68 | func (v Time) Time() time.Time { 69 | if !v.Valid() { 70 | return time.Unix(0, 0) 71 | } 72 | return v.time 73 | } 74 | 75 | // MarshalJSON implements the json.Marshaler interface. 76 | func (v Time) MarshalJSON() ([]byte, error) { 77 | if !v.Valid() { 78 | return nullBytes, nil 79 | } 80 | return v.time.MarshalJSON() 81 | } 82 | 83 | // UnmarshalJSON implements the json.Unmarshaler interface. 84 | func (v *Time) UnmarshalJSON(data []byte) error { 85 | if len(data) == 0 { 86 | return nil 87 | } 88 | if err := v.time.UnmarshalJSON(data); err != nil { 89 | return err 90 | } 91 | v.ValidFlag = true 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /type_bool.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | ) 7 | 8 | // Bool is generic boolean type structure 9 | type Bool struct { 10 | ValidFlag 11 | bool bool 12 | } 13 | 14 | // MarshalBool return generic.Bool converting of request data 15 | func MarshalBool(x interface{}) (Bool, error) { 16 | v := Bool{} 17 | err := v.Scan(x) 18 | return v, err 19 | } 20 | 21 | // MustBool return generic.Bool converting of request data 22 | func MustBool(x interface{}) Bool { 23 | v, err := MarshalBool(x) 24 | if err != nil { 25 | panic(err) 26 | } 27 | return v 28 | } 29 | 30 | // Value implements the driver Valuer interface. 31 | func (v Bool) Value() (driver.Value, error) { 32 | if !v.Valid() { 33 | return nil, nil 34 | } 35 | return v.bool, nil 36 | } 37 | 38 | // Scan implements the sql.Scanner interface. 39 | func (v *Bool) Scan(x interface{}) (err error) { 40 | v.bool, v.ValidFlag, err = asBool(x) 41 | if err != nil { 42 | v.ValidFlag = false 43 | return err 44 | } 45 | return 46 | } 47 | 48 | // Weak returns Bool.Bool, but if Bool.ValidFlag is false, returns nil. 49 | func (v Bool) Weak() interface{} { 50 | i, _ := v.Value() 51 | return i 52 | } 53 | 54 | // Set sets a specified value. 55 | func (v *Bool) Set(x interface{}) (err error) { 56 | return v.Scan(x) 57 | } 58 | 59 | // Bool returns bool value 60 | func (v Bool) Bool() bool { 61 | if v.Valid() && v.bool { 62 | return true 63 | } 64 | return false 65 | } 66 | 67 | // String implements the Stringer interface. 68 | func (v Bool) String() string { 69 | if v.Valid() && v.bool { 70 | return "true" 71 | } 72 | return "false" 73 | } 74 | 75 | // MarshalJSON implements the json.Marshaler interface. 76 | func (v Bool) MarshalJSON() ([]byte, error) { 77 | if !v.Valid() { 78 | return nullBytes, nil 79 | } 80 | if v.bool { 81 | return []byte("true"), nil 82 | } 83 | return []byte("false"), nil 84 | } 85 | 86 | // UnmarshalJSON implements the json.Unmarshaler interface. 87 | func (v *Bool) UnmarshalJSON(data []byte) error { 88 | if len(data) == 0 { 89 | return nil 90 | } 91 | var in interface{} 92 | if err := json.Unmarshal(data, &in); err != nil { 93 | return err 94 | } 95 | return v.Scan(in) 96 | } 97 | -------------------------------------------------------------------------------- /type_float.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | "strconv" 7 | ) 8 | 9 | // Float is generic float type structure 10 | type Float struct { 11 | ValidFlag 12 | float float64 13 | } 14 | 15 | // MarshalFloat return generic.Float converting of request data 16 | func MarshalFloat(x interface{}) (Float, error) { 17 | v := Float{} 18 | err := v.Scan(x) 19 | return v, err 20 | } 21 | 22 | // MustFloat return generic.Float converting of request data 23 | func MustFloat(x interface{}) Float { 24 | v, err := MarshalFloat(x) 25 | if err != nil { 26 | panic(err) 27 | } 28 | return v 29 | } 30 | 31 | // Value implements the driver Valuer interface. 32 | func (v Float) Value() (driver.Value, error) { 33 | if !v.Valid() { 34 | return nil, nil 35 | } 36 | return v.float, nil 37 | } 38 | 39 | // Scan implements the sql.Scanner interface. 40 | func (v *Float) Scan(x interface{}) (err error) { 41 | v.float, v.ValidFlag, err = asFloat(x) 42 | if err != nil { 43 | v.ValidFlag = false 44 | return err 45 | } 46 | return 47 | } 48 | 49 | // Weak returns Float.float, but if Float.ValidFlag is false, returns nil. 50 | func (v Float) Weak() interface{} { 51 | i, _ := v.Value() 52 | return i 53 | } 54 | 55 | // Set sets a specified value. 56 | func (v *Float) Set(x interface{}) (err error) { 57 | return v.Scan(x) 58 | } 59 | 60 | // Float32 returns float32 value 61 | func (v Float) Float32() float32 { 62 | return float32(v.Float64()) 63 | } 64 | 65 | // Float64 returns float64 value 66 | func (v Float) Float64() float64 { 67 | if !v.Valid() { 68 | return 0 69 | } 70 | return v.float 71 | } 72 | 73 | // String implements the Stringer interface. 74 | func (v Float) String() string { 75 | if !v.Valid() { 76 | return "" 77 | } 78 | return strconv.FormatFloat(v.float, 'f', -1, 64) 79 | } 80 | 81 | // MarshalJSON implements the json.Marshaler interface. 82 | func (v Float) MarshalJSON() ([]byte, error) { 83 | if !v.Valid() { 84 | return nullBytes, nil 85 | } 86 | return []byte(strconv.FormatFloat(v.float, 'f', -1, 64)), nil 87 | } 88 | 89 | // UnmarshalJSON implements the json.Unmarshaler interface. 90 | func (v *Float) UnmarshalJSON(data []byte) error { 91 | if len(data) == 0 { 92 | return nil 93 | } 94 | var in interface{} 95 | if err := json.Unmarshal(data, &in); err != nil { 96 | return err 97 | } 98 | return v.Scan(in) 99 | } 100 | -------------------------------------------------------------------------------- /convert_asBool_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import "testing" 4 | 5 | func TestAsBoolInt(t *testing.T) { 6 | i := 100 7 | asBoolTest(i, t) 8 | } 9 | 10 | func TestAsBoolInt8(t *testing.T) { 11 | var i int8 = 100 12 | asBoolTest(i, t) 13 | } 14 | 15 | func TestAsBoolInt16(t *testing.T) { 16 | var i int16 = 100 17 | asBoolTest(i, t) 18 | } 19 | 20 | func TestAsBoolInt32(t *testing.T) { 21 | var i int32 = 100 22 | asBoolTest(i, t) 23 | } 24 | 25 | func TestAsBoolInt64(t *testing.T) { 26 | var i int64 = 100 27 | asBoolTest(i, t) 28 | } 29 | 30 | func TestAsBoolUint(t *testing.T) { 31 | var u uint = 100 32 | asBoolTest(u, t) 33 | } 34 | 35 | func TestAsBoolUint8(t *testing.T) { 36 | var u uint8 = 100 37 | asBoolTest(u, t) 38 | } 39 | 40 | func TestAsBoolUint16(t *testing.T) { 41 | var u uint16 = 100 42 | asBoolTest(u, t) 43 | } 44 | 45 | func TestAsBoolUint32(t *testing.T) { 46 | var u uint32 = 100 47 | asBoolTest(u, t) 48 | } 49 | 50 | func TestAsBoolUint64(t *testing.T) { 51 | var u uint64 = 100 52 | asBoolTest(u, t) 53 | } 54 | 55 | func TestAsBoolFloat32(t *testing.T) { 56 | var f float32 = 1.0001 57 | asBoolTest(f, t) 58 | } 59 | 60 | func TestAsBoolFloat64(t *testing.T) { 61 | f := 1.0001 62 | asBoolTest(f, t) 63 | } 64 | 65 | func TestAsBoolTrue(t *testing.T) { 66 | b := true 67 | asBoolTest(b, t) 68 | } 69 | 70 | func TestAsBoolFalse(t *testing.T) { 71 | b := false 72 | r, v, err := asBool(b) 73 | if err != nil { 74 | t.Errorf("Not Expected error. error:%s", err.Error()) 75 | } 76 | if !v { 77 | t.Error("expected: true, actual: false") 78 | } 79 | if r { 80 | t.Error("expected: false, actual: true") 81 | } 82 | } 83 | 84 | func TestAsBoolNumericString(t *testing.T) { 85 | s := "1" 86 | asBoolTest(s, t) 87 | } 88 | 89 | func TestAsBoolUnumericString(t *testing.T) { 90 | s := "abd" 91 | _, _, err := asBool(s) 92 | if err == nil { 93 | t.Error("Expected error") 94 | } 95 | } 96 | 97 | func TestAsBoolInvalidType(t *testing.T) { 98 | bs := []byte("true") 99 | _, _, err := asBool(bs) 100 | if err == nil { 101 | t.Error("Expected error") 102 | } 103 | } 104 | 105 | func asBoolTest(x interface{}, t *testing.T) { 106 | r, v, err := asBool(x) 107 | if err != nil { 108 | t.Errorf("Not Expected error. error:%s", err.Error()) 109 | } 110 | if !v { 111 | t.Error("expected: true, actual: false") 112 | } 113 | if !r { 114 | t.Error("expected: true, actual: false") 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /type_uint.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | "strconv" 7 | ) 8 | 9 | // Uint is generic uint type structure 10 | type Uint struct { 11 | ValidFlag 12 | uint uint64 13 | } 14 | 15 | // MarshalUint return generic.Uint converting of request data 16 | func MarshalUint(x interface{}) (Uint, error) { 17 | v := Uint{} 18 | err := v.Scan(x) 19 | return v, err 20 | } 21 | 22 | // MustUint return generic.Uint converting of request data 23 | func MustUint(x interface{}) Uint { 24 | v, err := MarshalUint(x) 25 | if err != nil { 26 | panic(err) 27 | } 28 | return v 29 | } 30 | 31 | // Value implements the driver Valuer interface. 32 | func (v Uint) Value() (driver.Value, error) { 33 | if !v.Valid() { 34 | return nil, nil 35 | } 36 | return v.uint, nil 37 | } 38 | 39 | // Scan implements the sql.Scanner interface. 40 | func (v *Uint) Scan(x interface{}) (err error) { 41 | v.uint, v.ValidFlag, err = asUint(x) 42 | if err != nil { 43 | v.ValidFlag = false 44 | return err 45 | } 46 | return 47 | } 48 | 49 | // Weak returns Uint.Uint, but if Uint.ValidFlag is false, returns nil. 50 | func (v Uint) Weak() interface{} { 51 | i, _ := v.Value() 52 | return i 53 | } 54 | 55 | // Set sets a specified value. 56 | func (v *Uint) Set(x interface{}) (err error) { 57 | return v.Scan(x) 58 | } 59 | 60 | // Uint return uint value 61 | func (v Uint) Uint() uint { 62 | if !v.Valid() { 63 | return 0 64 | } 65 | return uint(v.uint) 66 | } 67 | 68 | // Uint32 return uint32 value 69 | func (v Uint) Uint32() uint32 { 70 | if !v.Valid() { 71 | return 0 72 | } 73 | return uint32(v.uint) 74 | } 75 | 76 | // Uint64 return uint64 value 77 | func (v Uint) Uint64() uint64 { 78 | if !v.Valid() { 79 | return 0 80 | } 81 | return v.uint 82 | } 83 | 84 | // String implements the Stringer interface. 85 | func (v Uint) String() string { 86 | if !v.Valid() { 87 | return "" 88 | } 89 | return strconv.FormatUint(v.uint, 10) 90 | } 91 | 92 | // MarshalJSON implements the json.Marshaler interface. 93 | func (v Uint) MarshalJSON() ([]byte, error) { 94 | if !v.Valid() { 95 | return nullBytes, nil 96 | } 97 | return []byte(strconv.FormatUint(v.uint, 10)), nil 98 | } 99 | 100 | // UnmarshalJSON implements the json.Unmarshaler interface. 101 | func (v *Uint) UnmarshalJSON(data []byte) error { 102 | var in interface{} 103 | if err := json.Unmarshal(data, &in); err != nil { 104 | return err 105 | } 106 | return v.Scan(in) 107 | } 108 | -------------------------------------------------------------------------------- /type_int.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | "strconv" 7 | ) 8 | 9 | // Int is generic integer type structure 10 | type Int struct { 11 | ValidFlag 12 | int int64 13 | } 14 | 15 | // MarshalInt return generic.Int converting of request data 16 | func MarshalInt(x interface{}) (Int, error) { 17 | v := Int{} 18 | err := v.Scan(x) 19 | return v, err 20 | } 21 | 22 | // MustInt return generic.Int converting of request data 23 | func MustInt(x interface{}) Int { 24 | v, err := MarshalInt(x) 25 | if err != nil { 26 | panic(err) 27 | } 28 | return v 29 | } 30 | 31 | // Value implements the driver Valuer interface. 32 | func (v Int) Value() (driver.Value, error) { 33 | if !v.Valid() { 34 | return nil, nil 35 | } 36 | return v.int, nil 37 | } 38 | 39 | // Scan implements the sql.Scanner interface. 40 | func (v *Int) Scan(x interface{}) (err error) { 41 | v.int, v.ValidFlag, err = asInt(x) 42 | if err != nil { 43 | v.ValidFlag = false 44 | return err 45 | } 46 | return 47 | } 48 | 49 | // Weak returns Int.Int, but if Int.ValidFlag is false, returns nil. 50 | func (v Int) Weak() interface{} { 51 | i, _ := v.Value() 52 | return i 53 | } 54 | 55 | // Set sets a specified value. 56 | func (v *Int) Set(x interface{}) (err error) { 57 | return v.Scan(x) 58 | } 59 | 60 | // Int return int value 61 | func (v Int) Int() int { 62 | if !v.Valid() { 63 | return 0 64 | } 65 | return int(v.int) 66 | } 67 | 68 | // Int32 return int32 value 69 | func (v Int) Int32() int32 { 70 | if !v.Valid() { 71 | return 0 72 | } 73 | return int32(v.int) 74 | } 75 | 76 | // Int64 return int64 value 77 | func (v Int) Int64() int64 { 78 | if !v.Valid() { 79 | return 0 80 | } 81 | return v.int 82 | } 83 | 84 | // String implements the Stringer interface. 85 | func (v Int) String() string { 86 | if !v.Valid() { 87 | return "" 88 | } 89 | return strconv.FormatInt(v.int, 10) 90 | } 91 | 92 | // MarshalJSON implements the json.Marshaler interface. 93 | func (v Int) MarshalJSON() ([]byte, error) { 94 | if !v.Valid() { 95 | return nullBytes, nil 96 | } 97 | return []byte(strconv.FormatInt(v.int, 10)), nil 98 | } 99 | 100 | // UnmarshalJSON implements the json.Unmarshaler interface. 101 | func (v *Int) UnmarshalJSON(data []byte) error { 102 | if len(data) == 0 || string(data) == "null" { 103 | return nil 104 | } 105 | var in interface{} 106 | if err := json.Unmarshal(data, &in); err != nil { 107 | return err 108 | } 109 | return v.Scan(in) 110 | } 111 | -------------------------------------------------------------------------------- /type_timestamp.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | // Timestamp is a wrapped time type structure 11 | type Timestamp struct { 12 | ValidFlag 13 | time time.Time 14 | } 15 | 16 | // MarshalTimestamp return generic.Timestamp converting of request data 17 | func MarshalTimestamp(x interface{}) (Timestamp, error) { 18 | v := Timestamp{} 19 | err := v.Scan(x) 20 | return v, err 21 | } 22 | 23 | // MustTimestamp return generic.Timestamp converting of request data 24 | func MustTimestamp(x interface{}) Timestamp { 25 | v, err := MarshalTimestamp(x) 26 | if err != nil { 27 | panic(err) 28 | } 29 | return v 30 | } 31 | 32 | // Value returns Time.Time, but if Time.ValidFlag is false, returns nil. 33 | func (v Timestamp) Value() (driver.Value, error) { 34 | if !v.Valid() { 35 | return nil, nil 36 | } 37 | return v.time, nil 38 | } 39 | 40 | // Scan implements the sql.Scanner interface. 41 | func (v *Timestamp) Scan(x interface{}) (err error) { 42 | v.time, v.ValidFlag, err = asTimestamp(x) 43 | if err != nil { 44 | v.ValidFlag = false 45 | return err 46 | } 47 | return 48 | } 49 | 50 | // Weak returns timestamp, but if Timestamp.ValidFlag is false, returns nil. 51 | func (v Timestamp) Weak() interface{} { 52 | i, _ := v.Value() 53 | return i 54 | } 55 | 56 | // Set sets a specified value. 57 | func (v *Timestamp) Set(x interface{}) (err error) { 58 | return v.Scan(x) 59 | } 60 | 61 | // String implements the Stringer interface. 62 | func (v Timestamp) String() string { 63 | return strconv.FormatInt(v.Int64(), 10) 64 | } 65 | 66 | // Int return int value 67 | func (v Timestamp) Int() int { 68 | return int(v.Int64()) 69 | } 70 | 71 | // Int64 return int64 value 72 | func (v Timestamp) Int64() int64 { 73 | if !v.Valid() || v.time.Unix() == 0 { 74 | return 0 75 | } 76 | return v.time.Unix() 77 | } 78 | 79 | // MarshalJSON implements the json.Marshaler interface. 80 | func (v Timestamp) MarshalJSON() ([]byte, error) { 81 | if !v.Valid() { 82 | return nullBytes, nil 83 | } 84 | return []byte(strconv.FormatInt(v.time.Unix(), 10)), nil 85 | } 86 | 87 | // Time returns value as time.Time 88 | func (v Timestamp) Time() time.Time { 89 | if !v.Valid() { 90 | return time.Unix(0, 0) 91 | } 92 | return v.time 93 | } 94 | 95 | // UnmarshalJSON implements the json.Unmarshaler interface. 96 | func (v *Timestamp) UnmarshalJSON(data []byte) error { 97 | if len(data) == 0 { 98 | return nil 99 | } 100 | var in interface{} 101 | if err := json.Unmarshal(data, &in); err != nil { 102 | return err 103 | } 104 | return v.Scan(in) 105 | } 106 | -------------------------------------------------------------------------------- /convert_asInt_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import "testing" 4 | 5 | func TestAsIntInt(t *testing.T) { 6 | i := int(100) 7 | asIntTest(i, t) 8 | } 9 | 10 | func TestAsIntInt8(t *testing.T) { 11 | i := int8(100) 12 | asIntTest(i, t) 13 | } 14 | 15 | func TestAsIntInt16(t *testing.T) { 16 | i := int16(100) 17 | asIntTest(i, t) 18 | } 19 | 20 | func TestAsIntInt32(t *testing.T) { 21 | i := int32(100) 22 | asIntTest(i, t) 23 | } 24 | 25 | func TestAsIntInt64(t *testing.T) { 26 | i := int64(100) 27 | asIntTest(i, t) 28 | } 29 | 30 | func TestAsIntUint(t *testing.T) { 31 | u := uint(100) 32 | asIntTest(u, t) 33 | } 34 | 35 | func TestAsIntUint8(t *testing.T) { 36 | u := uint8(100) 37 | asIntTest(u, t) 38 | } 39 | 40 | func TestAsIntUint16(t *testing.T) { 41 | u := uint16(100) 42 | asIntTest(u, t) 43 | } 44 | 45 | func TestAsIntUint32(t *testing.T) { 46 | u := uint32(100) 47 | asIntTest(u, t) 48 | } 49 | 50 | func TestAsIntUint64(t *testing.T) { 51 | u := uint64(100) 52 | asIntTest(u, t) 53 | } 54 | 55 | func TestAsIntFloat32(t *testing.T) { 56 | f := float32(100.0001) 57 | asIntTest(f, t) 58 | } 59 | 60 | func TestAsIntFloat64(t *testing.T) { 61 | f := float64(100.0001) 62 | asIntTest(f, t) 63 | } 64 | 65 | func TestAsIntTrue(t *testing.T) { 66 | b := true 67 | r, v, err := asInt(b) 68 | if err != nil { 69 | t.Errorf("Not Expected error. error:%s", err.Error()) 70 | } 71 | if !v { 72 | t.Error("expected: true, actual: false") 73 | } 74 | if r != 1 { 75 | t.Errorf("expected: 1, actual: %d", r) 76 | } 77 | } 78 | 79 | func TestAsIntFalse(t *testing.T) { 80 | b := false 81 | r, v, err := asInt(b) 82 | if err != nil { 83 | t.Errorf("Not Expected error. error:%s", err.Error()) 84 | } 85 | if !v { 86 | t.Error("expected: true, actual: false") 87 | } 88 | if r != 0 { 89 | t.Errorf("expected: 0, actual: %d", r) 90 | } 91 | } 92 | 93 | func TestAsIntNumericString(t *testing.T) { 94 | s := "100" 95 | asIntTest(s, t) 96 | } 97 | 98 | func TestAsIntUnumericString(t *testing.T) { 99 | s := "abd" 100 | _, _, err := asInt(s) 101 | if err == nil { 102 | t.Error("Expected error") 103 | } 104 | } 105 | 106 | func TestAsIntInvalidType(t *testing.T) { 107 | bs := []byte("1") 108 | _, _, err := asInt(bs) 109 | if err == nil { 110 | t.Error("Expected error") 111 | } 112 | } 113 | 114 | func asIntTest(x interface{}, t *testing.T) { 115 | r, v, err := asInt(x) 116 | if err != nil { 117 | t.Errorf("Not Expected error. error:%s", err.Error()) 118 | } 119 | if !v { 120 | t.Error("expected: true, actual: false") 121 | } 122 | if r != 100 { 123 | t.Errorf("expected: 100, actual: %d", r) 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /type_timestamp_milliseconds.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | // TimestampMS is a wrapped time type structure 11 | type TimestampMS struct { 12 | ValidFlag 13 | time time.Time 14 | } 15 | 16 | // MarshalTimestampMS returns generic.TimestampMS converting of request data 17 | func MarshalTimestampMS(x interface{}) (TimestampMS, error) { 18 | v := TimestampMS{} 19 | err := v.Scan(x) 20 | return v, err 21 | } 22 | 23 | // MustTimestampMS returns generic.TimestampMS converting of request data 24 | func MustTimestampMS(x interface{}) TimestampMS { 25 | v, err := MarshalTimestampMS(x) 26 | if err != nil { 27 | panic(err) 28 | } 29 | return v 30 | } 31 | 32 | // Value returns timestamp with milliseconds, but if TimestampMS.ValidFlag is false, returns nil. 33 | func (v TimestampMS) Value() (driver.Value, error) { 34 | if !v.Valid() { 35 | return nil, nil 36 | } 37 | return v.time.UnixNano() / 1000000, nil 38 | } 39 | 40 | // Scan implements the sql.Scanner interface. 41 | func (v *TimestampMS) Scan(x interface{}) (err error) { 42 | v.time, v.ValidFlag, err = asTimestampMilliseconds(x) 43 | if err != nil { 44 | v.ValidFlag = false 45 | return err 46 | } 47 | return 48 | } 49 | 50 | // Weak returns timestamp int value, but if TimestampMS.ValidFlag is false, returns nil. 51 | func (v TimestampMS) Weak() interface{} { 52 | i, _ := v.Value() 53 | return i 54 | } 55 | 56 | // Set sets a specified value. 57 | func (v *TimestampMS) Set(x interface{}) (err error) { 58 | return v.Scan(x) 59 | } 60 | 61 | // String implements the Stringer interface. 62 | func (v TimestampMS) String() string { 63 | return strconv.FormatInt(v.Int64(), 10) 64 | } 65 | 66 | // Int return int value 67 | func (v TimestampMS) Int() int { 68 | return int(v.Int64()) 69 | } 70 | 71 | // Int64 return int64 value 72 | func (v TimestampMS) Int64() int64 { 73 | if !v.Valid() || v.time.UnixNano() == 0 { 74 | return 0 75 | } 76 | return v.time.UnixNano() / 1000000 77 | } 78 | 79 | // Time returns value as time.Time 80 | func (v TimestampMS) Time() time.Time { 81 | if !v.Valid() { 82 | return time.Unix(0, 0) 83 | } 84 | return v.time 85 | } 86 | 87 | // MarshalJSON implements the json.Marshaler interface. 88 | func (v TimestampMS) MarshalJSON() ([]byte, error) { 89 | if !v.Valid() { 90 | return nullBytes, nil 91 | } 92 | return []byte(v.String()), nil 93 | } 94 | 95 | // UnmarshalJSON implements the json.Unmarshaler interface. 96 | func (v *TimestampMS) UnmarshalJSON(data []byte) error { 97 | if len(data) == 0 { 98 | return nil 99 | } 100 | var in interface{} 101 | if err := json.Unmarshal(data, &in); err != nil { 102 | return err 103 | } 104 | return v.Scan(in) 105 | } 106 | -------------------------------------------------------------------------------- /type_timestamp_nanoseconds.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | "strconv" 7 | "time" 8 | ) 9 | 10 | // TimestampNano is a wrapped time type structure 11 | type TimestampNano struct { 12 | ValidFlag 13 | time time.Time 14 | } 15 | 16 | // MarshalTimestampNano returns generic.TimestampNano converting of request data 17 | func MarshalTimestampNano(x interface{}) (TimestampNano, error) { 18 | v := TimestampNano{} 19 | err := v.Scan(x) 20 | return v, err 21 | } 22 | 23 | // MustTimestampNano returns generic.TimestampNano converting of request data 24 | func MustTimestampNano(x interface{}) TimestampNano { 25 | v, err := MarshalTimestampNano(x) 26 | if err != nil { 27 | panic(err) 28 | } 29 | return v 30 | } 31 | 32 | // Value returns timestamp with nanoseconds, but if TimestampNano.ValidFlag is false, returns nil. 33 | func (v TimestampNano) Value() (driver.Value, error) { 34 | if !v.Valid() { 35 | return nil, nil 36 | } 37 | return v.time.UnixNano(), nil 38 | } 39 | 40 | // Scan implements the sql.Scanner interface. 41 | func (v *TimestampNano) Scan(x interface{}) (err error) { 42 | v.time, v.ValidFlag, err = asTimestampNanoseconds(x) 43 | if err != nil { 44 | v.ValidFlag = false 45 | return err 46 | } 47 | return 48 | } 49 | 50 | // Weak returns timestamp with nano seconds, but if TimestampNano.ValidFlag is false, returns nil. 51 | func (v TimestampNano) Weak() interface{} { 52 | i, _ := v.Value() 53 | return i 54 | } 55 | 56 | // Set sets a specified value. 57 | func (v *TimestampNano) Set(x interface{}) (err error) { 58 | return v.Scan(x) 59 | } 60 | 61 | // String implements the Stringer interface. 62 | func (v TimestampNano) String() string { 63 | return strconv.FormatInt(v.Int64(), 10) 64 | } 65 | 66 | // Int return int value 67 | func (v TimestampNano) Int() int { 68 | return int(v.Int64()) 69 | } 70 | 71 | // Int64 return int64 value 72 | func (v TimestampNano) Int64() int64 { 73 | if !v.Valid() || v.time.UnixNano() == 0 { 74 | return 0 75 | } 76 | return v.time.UnixNano() 77 | } 78 | 79 | // Time returns value as time.Time 80 | func (v TimestampNano) Time() time.Time { 81 | if !v.Valid() { 82 | return time.Unix(0, 0) 83 | } 84 | return v.time 85 | } 86 | 87 | // MarshalJSON implements the json.Marshaler interface. 88 | func (v TimestampNano) MarshalJSON() ([]byte, error) { 89 | if !v.Valid() { 90 | return nullBytes, nil 91 | } 92 | return []byte(strconv.FormatInt(v.time.UnixNano(), 10)), nil 93 | } 94 | 95 | // UnmarshalJSON implements the json.Unmarshaler interface. 96 | func (v *TimestampNano) UnmarshalJSON(data []byte) error { 97 | if len(data) == 0 { 98 | return nil 99 | } 100 | var in interface{} 101 | if err := json.Unmarshal(data, &in); err != nil { 102 | return err 103 | } 104 | return v.Scan(in) 105 | } 106 | -------------------------------------------------------------------------------- /json_marshal_benchmark_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "net/url" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func BenchmarkMarshalJSONBool(b *testing.B) { 10 | x := Bool{ 11 | ValidFlag: true, 12 | bool: true, 13 | } 14 | for i := 0; i < b.N; i++ { 15 | x.MarshalJSON() // nolint 16 | } 17 | } 18 | 19 | func BenchmarkMarshalJSONFloat(b *testing.B) { 20 | x := Float{ 21 | ValidFlag: true, 22 | float: 1000.000001, 23 | } 24 | for i := 0; i < b.N; i++ { 25 | x.MarshalJSON() // nolint 26 | } 27 | } 28 | 29 | func BenchmarkMarshalJSONInt(b *testing.B) { 30 | x := Int{ 31 | ValidFlag: true, 32 | int: 10000, 33 | } 34 | for i := 0; i < b.N; i++ { 35 | x.MarshalJSON() // nolint 36 | } 37 | } 38 | 39 | func BenchmarkMarshalJSONString(b *testing.B) { 40 | x := String{ 41 | ValidFlag: true, 42 | string: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", 43 | } 44 | for i := 0; i < b.N; i++ { 45 | x.MarshalJSON() // nolint 46 | } 47 | } 48 | 49 | func BenchmarkMarshalJSONStringLarge(b *testing.B) { 50 | x := String{ 51 | ValidFlag: true, 52 | string: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.", 53 | } 54 | for i := 0; i < b.N; i++ { 55 | x.MarshalJSON() // nolint 56 | } 57 | } 58 | 59 | func BenchmarkMarshalJSONTime(b *testing.B) { 60 | x := Time{ 61 | ValidFlag: true, 62 | time: time.Now(), 63 | } 64 | for i := 0; i < b.N; i++ { 65 | x.MarshalJSON() // nolint 66 | } 67 | } 68 | 69 | func BenchmarkMarshalJSONTimestampMS(b *testing.B) { 70 | x := TimestampMS{ 71 | ValidFlag: true, 72 | time: time.Now(), 73 | } 74 | for i := 0; i < b.N; i++ { 75 | x.MarshalJSON() // nolint 76 | } 77 | } 78 | 79 | func BenchmarkMarshalJSONTimestampNano(b *testing.B) { 80 | x := TimestampNano{ 81 | ValidFlag: true, 82 | time: time.Now(), 83 | } 84 | for i := 0; i < b.N; i++ { 85 | x.MarshalJSON() // nolint 86 | } 87 | } 88 | 89 | func BenchmarkMarshalJSONTimestamp(b *testing.B) { 90 | x := Timestamp{ 91 | ValidFlag: true, 92 | time: time.Now(), 93 | } 94 | for i := 0; i < b.N; i++ { 95 | x.MarshalJSON() // nolint 96 | } 97 | } 98 | 99 | func BenchmarkMarshalJSONUint(b *testing.B) { 100 | x := Uint{ 101 | ValidFlag: true, 102 | uint: 10000, 103 | } 104 | for i := 0; i < b.N; i++ { 105 | x.MarshalJSON() // nolint 106 | } 107 | } 108 | 109 | func BenchmarkMarshalJSONURL(b *testing.B) { 110 | x := URL{ 111 | ValidFlag: true, 112 | url: &url.URL{ 113 | Scheme: "https", 114 | Host: "www.google.com", 115 | }, 116 | } 117 | for i := 0; i < b.N; i++ { 118 | x.MarshalJSON() // nolint 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /convert_asFloat_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import "testing" 4 | 5 | func TestAsFloatInt(t *testing.T) { 6 | i := int(100) 7 | asFloatTest(i, t) 8 | } 9 | 10 | func TestAsFloatInt8(t *testing.T) { 11 | i := int8(100) 12 | asFloatTest(i, t) 13 | } 14 | 15 | func TestAsFloatInt16(t *testing.T) { 16 | i := int16(100) 17 | asFloatTest(i, t) 18 | } 19 | 20 | func TestAsFloatInt32(t *testing.T) { 21 | i := int32(100) 22 | asFloatTest(i, t) 23 | } 24 | 25 | func TestAsFloatInt64(t *testing.T) { 26 | i := int64(100) 27 | asFloatTest(i, t) 28 | } 29 | 30 | func TestAsFloatUint(t *testing.T) { 31 | u := uint(100) 32 | asFloatTest(u, t) 33 | } 34 | 35 | func TestAsFloatUint8(t *testing.T) { 36 | u := uint8(100) 37 | asFloatTest(u, t) 38 | } 39 | 40 | func TestAsFloatUint16(t *testing.T) { 41 | u := uint16(100) 42 | asFloatTest(u, t) 43 | } 44 | 45 | func TestAsFloatUint32(t *testing.T) { 46 | u := uint32(100) 47 | asFloatTest(u, t) 48 | } 49 | 50 | func TestAsFloatUint64(t *testing.T) { 51 | u := uint64(100) 52 | asFloatTest(u, t) 53 | } 54 | 55 | func TestAsFloatFloat32(t *testing.T) { 56 | f := float32(100.0001) 57 | r, v, err := asFloat(f) 58 | if err != nil { 59 | t.Errorf("Not Expected error. error:%s", err.Error()) 60 | } 61 | if !v { 62 | t.Error("expected: true, actual: false") 63 | } 64 | if float32(r) != 100.0001 { 65 | t.Errorf("expected: 100.0001, actual: %v", r) 66 | } 67 | } 68 | 69 | func TestAsFloatFloat64(t *testing.T) { 70 | f := float64(100.0001) 71 | r, v, err := asFloat(f) 72 | if err != nil { 73 | t.Errorf("Not Expected error. error:%s", err.Error()) 74 | } 75 | if !v { 76 | t.Error("expected: true, actual: false") 77 | } 78 | if r != 100.0001 { 79 | t.Errorf("expected: 100, actual: %v", r) 80 | } 81 | } 82 | 83 | func TestAsFloatTrue(t *testing.T) { 84 | b := true 85 | r, v, err := asFloat(b) 86 | if err != nil { 87 | t.Errorf("Not Expected error. error:%s", err.Error()) 88 | } 89 | if !v { 90 | t.Error("expected: true, actual: false") 91 | } 92 | if r != 1 { 93 | t.Errorf("expected: 1, actual: %v", r) 94 | } 95 | } 96 | 97 | func TestAsFloatFalse(t *testing.T) { 98 | b := false 99 | r, v, err := asFloat(b) 100 | if err != nil { 101 | t.Errorf("Not Expected error. error:%s", err.Error()) 102 | } 103 | if !v { 104 | t.Error("expected: true, actual: false") 105 | } 106 | if r != 0 { 107 | t.Errorf("expected: 0, actual: %v", r) 108 | } 109 | } 110 | 111 | func TestAsFloatNumericString(t *testing.T) { 112 | s := "100" 113 | asFloatTest(s, t) 114 | } 115 | 116 | func TestAsFloatUnumericString(t *testing.T) { 117 | s := "abd" 118 | _, _, err := asFloat(s) 119 | if err == nil { 120 | t.Error("Expected error") 121 | } 122 | } 123 | 124 | func TestAsFloatInvalidType(t *testing.T) { 125 | bs := []byte("1") 126 | _, _, err := asFloat(bs) 127 | if err == nil { 128 | t.Error("Expected error") 129 | } 130 | } 131 | 132 | func asFloatTest(x interface{}, t *testing.T) { 133 | r, v, err := asFloat(x) 134 | if err != nil { 135 | t.Errorf("Not Expected error. error:%s", err.Error()) 136 | } 137 | if !v { 138 | t.Error("expected: true, actual: false") 139 | } 140 | if r != 100 { 141 | t.Errorf("expected: 100, actual: %v", r) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /convert_asUint_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import "testing" 4 | 5 | func TestAsUintInt(t *testing.T) { 6 | i := int(100) 7 | asUintTest(i, t) 8 | } 9 | 10 | func TestAsUintIntMinus(t *testing.T) { 11 | x := int(-100) 12 | _, _, err := asUint(x) 13 | if err == nil { 14 | t.Error("Expected error") 15 | } 16 | } 17 | 18 | func TestAsUintInt8(t *testing.T) { 19 | i := int8(100) 20 | asUintTest(i, t) 21 | } 22 | 23 | func TestAsUintInt8Minus(t *testing.T) { 24 | x := int8(-100) 25 | _, _, err := asUint(x) 26 | if err == nil { 27 | t.Error("Expected error") 28 | } 29 | } 30 | 31 | func TestAsUintInt16(t *testing.T) { 32 | i := int16(100) 33 | asUintTest(i, t) 34 | } 35 | 36 | func TestAsUintInt16Minus(t *testing.T) { 37 | x := int16(-100) 38 | _, _, err := asUint(x) 39 | if err == nil { 40 | t.Error("Expected error") 41 | } 42 | } 43 | 44 | func TestAsUintInt32(t *testing.T) { 45 | i := int32(100) 46 | asUintTest(i, t) 47 | } 48 | 49 | func TestAsUintInt32Minus(t *testing.T) { 50 | x := int32(-100) 51 | _, _, err := asUint(x) 52 | if err == nil { 53 | t.Error("Expected error") 54 | } 55 | } 56 | 57 | func TestAsUintInt64(t *testing.T) { 58 | i := int64(100) 59 | asUintTest(i, t) 60 | } 61 | 62 | func TestAsUintInt64Minus(t *testing.T) { 63 | x := int64(-100) 64 | _, _, err := asUint(x) 65 | if err == nil { 66 | t.Error("Expected error") 67 | } 68 | } 69 | 70 | func TestAsUintUint(t *testing.T) { 71 | u := uint(100) 72 | asUintTest(u, t) 73 | } 74 | 75 | func TestAsUintUint8(t *testing.T) { 76 | u := uint8(100) 77 | asUintTest(u, t) 78 | } 79 | 80 | func TestAsUintUint16(t *testing.T) { 81 | u := uint16(100) 82 | asUintTest(u, t) 83 | } 84 | 85 | func TestAsUintUint32(t *testing.T) { 86 | u := uint32(100) 87 | asUintTest(u, t) 88 | } 89 | 90 | func TestAsUintUint64(t *testing.T) { 91 | u := uint64(100) 92 | asUintTest(u, t) 93 | } 94 | 95 | func TestAsUintFloat32(t *testing.T) { 96 | f := float32(100.001) 97 | asUintTest(f, t) 98 | } 99 | 100 | func TestAsUintFloat32Minus(t *testing.T) { 101 | x := float32(-100.001) 102 | _, _, err := asUint(x) 103 | if err == nil { 104 | t.Error("Expected error") 105 | } 106 | } 107 | 108 | func TestAsUintFloat64(t *testing.T) { 109 | f := float64(100.001) 110 | asUintTest(f, t) 111 | } 112 | 113 | func TestAsUintFloat64Minus(t *testing.T) { 114 | x := float64(-100.001) 115 | _, _, err := asUint(x) 116 | if err == nil { 117 | t.Error("Expected error") 118 | } 119 | } 120 | 121 | func TestAsUintTrue(t *testing.T) { 122 | b := true 123 | r, v, err := asUint(b) 124 | if err != nil { 125 | t.Errorf("Not Expected error. error:%s", err.Error()) 126 | } 127 | if !v { 128 | t.Error("expected: true, actual: false") 129 | } 130 | if r != 1 { 131 | t.Errorf("expected: 1, actual: %d", r) 132 | } 133 | } 134 | 135 | func TestAsUintFalse(t *testing.T) { 136 | b := false 137 | r, v, err := asUint(b) 138 | if err != nil { 139 | t.Errorf("Not Expected error. error:%s", err.Error()) 140 | } 141 | if !v { 142 | t.Error("expected: true, actual: false") 143 | } 144 | if r != 0 { 145 | t.Errorf("expected: 0, actual: %d", r) 146 | } 147 | } 148 | 149 | func TestAsUintNumericString(t *testing.T) { 150 | s := "100" 151 | asUintTest(s, t) 152 | } 153 | 154 | func TestAsUintUnumericString(t *testing.T) { 155 | s := "abd.01" 156 | _, _, err := asUint(s) 157 | if err == nil { 158 | t.Error("Expected error") 159 | } 160 | } 161 | 162 | func TestAsUintInvalidType(t *testing.T) { 163 | bs := []byte("100") 164 | _, _, err := asUint(bs) 165 | if err == nil { 166 | t.Error("Expected error") 167 | } 168 | } 169 | 170 | func asUintTest(x interface{}, t *testing.T) { 171 | r, v, err := asUint(x) 172 | if err != nil { 173 | t.Errorf("Not Expected error. error:%s", err.Error()) 174 | } 175 | if !v { 176 | t.Error("expected: true, actual: false") 177 | } 178 | if r != 100 { 179 | t.Errorf("expected: 100, actual: %v", r) 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /json_unmarshal_benchmark_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func BenchmarkUnmarshalJSONBoolFromBool(b *testing.B) { 9 | unmarshalJSONBoolBenchmark(b, []byte(`true`)) 10 | } 11 | 12 | func BenchmarkUnmarshalJSONBoolFromFloat(b *testing.B) { 13 | unmarshalJSONBoolBenchmark(b, []byte(`1.0`)) 14 | } 15 | 16 | func BenchmarkUnmarshalJSONBoolFromInt(b *testing.B) { 17 | unmarshalJSONBoolBenchmark(b, []byte(`1`)) 18 | } 19 | 20 | func BenchmarkUnmarshalJSONBoolFromString(b *testing.B) { 21 | unmarshalJSONBoolBenchmark(b, []byte(`"true"`)) 22 | } 23 | 24 | func BenchmarkUnmarshalJSONIntFromBool(b *testing.B) { 25 | unmarshalJSONIntBenchmark(b, []byte(`true`)) 26 | } 27 | 28 | func BenchmarkUnmarshalJSONIntFromFloat(b *testing.B) { 29 | unmarshalJSONIntBenchmark(b, []byte(`1.0`)) 30 | } 31 | 32 | func BenchmarkUnmarshalJSONIntFromInt(b *testing.B) { 33 | unmarshalJSONIntBenchmark(b, []byte(`1`)) 34 | } 35 | 36 | func BenchmarkUnmarshalJSONIntFromString(b *testing.B) { 37 | unmarshalJSONIntBenchmark(b, []byte(`"1.0"`)) 38 | } 39 | 40 | func BenchmarkUnmarshalJSONUintFromBool(b *testing.B) { 41 | unmarshalJSONUintBenchmark(b, []byte(`true`)) 42 | } 43 | 44 | func BenchmarkUnmarshalJSONUintFromFloat(b *testing.B) { 45 | unmarshalJSONUintBenchmark(b, []byte(`1.0`)) 46 | } 47 | 48 | func BenchmarkUnmarshalJSONUintFromInt(b *testing.B) { 49 | unmarshalJSONUintBenchmark(b, []byte(`1`)) 50 | } 51 | 52 | func BenchmarkUnmarshalJSONUintFromString(b *testing.B) { 53 | unmarshalJSONUintBenchmark(b, []byte(`"1.0"`)) 54 | } 55 | 56 | func BenchmarkUnmarshalJSONFloatFromBool(b *testing.B) { 57 | unmarshalJSONFloatBenchmark(b, []byte(`true`)) 58 | } 59 | 60 | func BenchmarkUnmarshalJSONFloatFromFloat(b *testing.B) { 61 | unmarshalJSONFloatBenchmark(b, []byte(`1.0`)) 62 | } 63 | 64 | func BenchmarkUnmarshalJSONFloatFromInt(b *testing.B) { 65 | unmarshalJSONFloatBenchmark(b, []byte(`1`)) 66 | } 67 | 68 | func BenchmarkUnmarshalJSONFloatFromString(b *testing.B) { 69 | unmarshalJSONFloatBenchmark(b, []byte(`"1.0"`)) 70 | } 71 | 72 | func BenchmarkUnmarshalJSONFloatFromUint(b *testing.B) { 73 | unmarshalJSONFloatBenchmark(b, []byte(`1`)) 74 | } 75 | 76 | func BenchmarkUnmarshalJSONStringFromBool(b *testing.B) { 77 | unmarshalJSONStringBenchmark(b, []byte(`true`)) 78 | } 79 | 80 | func BenchmarkUnmarshalJSONStringFromFloat(b *testing.B) { 81 | unmarshalJSONStringBenchmark(b, []byte(`1.0`)) 82 | } 83 | 84 | func BenchmarkUnmarshalJSONStringFromInt(b *testing.B) { 85 | unmarshalJSONStringBenchmark(b, []byte(`1`)) 86 | } 87 | 88 | func BenchmarkUnmarshalJSONStringFromString(b *testing.B) { 89 | unmarshalJSONStringBenchmark(b, []byte(`"true"`)) 90 | } 91 | 92 | func BenchmarkUnmarshalJSONTimeFromString(b *testing.B) { 93 | now := time.Now() 94 | unmarshalJSONTimeBenchmark(b, []byte(`"`+now.String()+`"`)) 95 | } 96 | 97 | func BenchmarkUnmarshalJSONURLFromString(b *testing.B) { 98 | unmarshalJSONURLBenchmark(b, []byte(`"https://google.com"`)) 99 | } 100 | 101 | func unmarshalJSONBoolBenchmark(b *testing.B, bs []byte) { 102 | x := Bool{} 103 | for i := 0; i < b.N; i++ { 104 | x.UnmarshalJSON(bs) // nolint 105 | } 106 | } 107 | 108 | func unmarshalJSONFloatBenchmark(b *testing.B, bs []byte) { 109 | x := Float{} 110 | for i := 0; i < b.N; i++ { 111 | x.UnmarshalJSON(bs) // nolint 112 | } 113 | } 114 | 115 | func unmarshalJSONIntBenchmark(b *testing.B, bs []byte) { 116 | x := Int{} 117 | for i := 0; i < b.N; i++ { 118 | x.UnmarshalJSON(bs) // nolint 119 | } 120 | } 121 | 122 | func unmarshalJSONStringBenchmark(b *testing.B, bs []byte) { 123 | x := String{} 124 | for i := 0; i < b.N; i++ { 125 | x.UnmarshalJSON(bs) // nolint 126 | } 127 | } 128 | 129 | func unmarshalJSONTimeBenchmark(b *testing.B, bs []byte) { 130 | t := Time{} 131 | for i := 0; i < b.N; i++ { 132 | t.UnmarshalJSON(bs) // nolint 133 | } 134 | } 135 | 136 | func unmarshalJSONUintBenchmark(b *testing.B, bs []byte) { 137 | x := Uint{} 138 | for i := 0; i < b.N; i++ { 139 | x.UnmarshalJSON(bs) // nolint 140 | } 141 | } 142 | 143 | func unmarshalJSONURLBenchmark(b *testing.B, bs []byte) { 144 | x := URL{} 145 | for i := 0; i < b.N; i++ { 146 | x.UnmarshalJSON(bs) // nolint 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Generic 2 | [![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/usk81/generic) 3 | [![License](http://img.shields.io/badge/license-mit-blue.svg?style=flat-square)](https://github.com/usk81/generic/blob/master/LICENSE) 4 | ![](https://github.com/usk81/generic/workflows/preliminary%20review/badge.svg) 5 | [![codecov](https://codecov.io/gh/usk81/generic/branch/master/graph/badge.svg)](https://codecov.io/gh/usk81/generic) 6 | [![Go Report Card](https://goreportcard.com/badge/github.com/usk81/generic)](https://goreportcard.com/report/github.com/usk81/generic) 7 | 8 | flexible data type for Go 9 | 10 | support: Go 1.12+ 11 | 12 | ## Install 13 | 14 | standard `go get`: 15 | 16 | ``` 17 | go get -u github.com/usk81/generic/v2 18 | ``` 19 | 20 | ## Usage 21 | 22 | encode/decode: 23 | 24 | ```go 25 | package main 26 | 27 | import ( 28 | "encoding/json" 29 | "github.com/usk81/generic/v2" 30 | ) 31 | 32 | type User struct { 33 | Name String `json:"name"` 34 | Age generic.Int `json:"age"` 35 | } 36 | 37 | var user1 User 38 | u1 := []byte(`{"name":"Daryl Dixon","age":"40"}`) 39 | json.Unmarshal([]byte(u1), &user1) 40 | b, _ := json.Marshal(user1) 41 | Println(string(b)) 42 | // {"name":"Daryl Dixon","age":40} 43 | 44 | var user2 User 45 | u2 := []byte(`{"name":"Rick Grimes"}`) 46 | json.Unmarshal([]byte(u2), &user2) 47 | b, _ := json.Marshal(user2) 48 | Println(string(b)) 49 | // {"name":"Rick Grimes","age":null} 50 | ``` 51 | 52 | set: 53 | 54 | ```go 55 | package main 56 | 57 | import ( 58 | "fmt" 59 | "github.com/usk81/generic" 60 | ) 61 | 62 | func main() { 63 | v := 1.0 64 | 65 | var tb generic.Bool 66 | tb.Set(v) 67 | vb := tb.Weak() 68 | fmt.Printf("%v, (%T)\n", vb, vb) 69 | // true, (bool) 70 | 71 | var tf generic.Float 72 | tf.Set(v) 73 | vf := tf.Weak() 74 | fmt.Printf("%v, (%T)\n", vf, vf) 75 | // 1, (float64) 76 | 77 | var ti generic.Int 78 | ti.Set(v) 79 | vi := ti.Weak() 80 | fmt.Printf("%v, (%T)\n", vi, vi) 81 | // 1, (int64) 82 | 83 | var ts generic.String 84 | ts.Set(v) 85 | vs := ts.Weak() 86 | fmt.Printf("%v, (%T)\n", vs, vs) 87 | // 1, (string) 88 | 89 | var tt generic.Time 90 | tt.Set(v) 91 | vt := tt.Weak() 92 | fmt.Printf("%v, (%T)\n", vt.UTC(), vt) 93 | // 1970-01-01 09:00:01 +0900 JST, (time.Time) 94 | 95 | var tu generic.Uint 96 | tu.Set(v) 97 | vu := tu.Weak() 98 | fmt.Printf("%v, (%T)\n", vu, vu) 99 | // 1, (uint64) 100 | } 101 | ``` 102 | 103 | ## Benchmarks 104 | 105 | ### Marshal 106 | 107 | #### Bool 108 | 109 | | version | requests | /op | B/op | allocs/op | 110 | |---|---|---|---|---| 111 | | 1.0.0 | 5000000 | 240 ns | 185 | 3 | 112 | | 2.0.0 | 200000000 | 6.69 ns | 0 | 0 | 113 | 114 | #### Float 115 | 116 | | version | requests | /op | B/op | allocs/op | 117 | |---|---|---|---|---| 118 | | 1.0.0 | 3000000 | 425 ns | 192 | 3 | 119 | | 2.0.0 | 5000000 | 260 ns | 64 | 3 | 120 | 121 | #### Int 122 | 123 | | version | requests | /op | B/op | allocs/op | 124 | |---|---|---|---|---| 125 | | 1.0.0 | 5000000 | 265 ns | 192 | 3 | 126 | | 2.0.0 | 20000000 | 70.5 ns | 16 | 2 | 127 | 128 | #### String (small) 129 | 130 | | version | requests | /op | B/op | allocs/op | 131 | |---|---|---|---|---| 132 | | 1.0.0 | 3000000 | 382 ns | 200 | 3 | 133 | | 2.0.0 | 20000000 | 89.0 ns | 128 | 2 | 134 | 135 | #### String (Large) 136 | 137 | | version | requests | /op | B/op | allocs/op | 138 | |---|---|---|---|---| 139 | | 1.0.0 | 1000000 | 1056 ns | 776 | 4 | 140 | | 2.0.0 | 5000000 | 237 ns | 896 | 2 | 141 | 142 | #### Time 143 | 144 | | version | requests | /op | B/op | allocs/op | 145 | |---|---|---|---|---| 146 | | 1.0.0 | 1000000 | 1122 ns | 360 | 5 | 147 | | 2.0.0 | 3000000 | 401 ns | 48 | 1 | 148 | 149 | #### TimestampMS 150 | 151 | | version | requests | /op | B/op | allocs/op | 152 | |---|---|---|---|---| 153 | | 1.0.0 | 20000000 | 97.9 ns | 32 | 2 | 154 | | 2.0.0 | 20000000 | 91.2 ns | 32 | 2 | 155 | 156 | #### TimestampNano 157 | 158 | | version | requests | /op | B/op | allocs/op | 159 | |---|---|---|---|---| 160 | | 1.0.0 | 10000000 | 114 ns | 64 | 2 | 161 | | 2.0.0 | 10000000 | 112 ns | 64 | 2 | 162 | 163 | #### Timestamp 164 | 165 | | version | requests | /op | B/op | allocs/op | 166 | |---|---|---|---|---| 167 | | 1.0.0 | 20000000 | 88.4 ns | 32 | 2 | 168 | | 2.0.0 | 20000000 | 86.7 ns | 32 | 2 | 169 | 170 | #### Uint 171 | 172 | | version | requests | /op | B/op | allocs/op | 173 | |---|---|---|---|---| 174 | | 1.0.0 | 5000000 | 277 ns | 192 | 3 | 175 | | 2.0.0 | 20000000 | 64.2 ns | 16 | 2 | 176 | 177 | ## Licence 178 | 179 | [MIT](https://github.com/usk81/generic/blob/master/LICENSE) 180 | 181 | ## Author 182 | 183 | [Yusuke Komatsu](https://github.com/usk81) 184 | -------------------------------------------------------------------------------- /type_url.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "database/sql/driver" 5 | "encoding/json" 6 | "net/url" 7 | ) 8 | 9 | // URL is generic url.URL type structure 10 | type URL struct { 11 | ValidFlag 12 | url *url.URL 13 | } 14 | 15 | // MarshalURL return generic.URL converting of request data 16 | func MarshalURL(x interface{}) (URL, error) { 17 | v := URL{} 18 | err := v.Scan(x) 19 | return v, err 20 | } 21 | 22 | // MustURL return generic.URL converting of request data 23 | func MustURL(x interface{}) URL { 24 | u, err := MarshalURL(x) 25 | if err != nil { 26 | panic(err.Error()) 27 | } 28 | return u 29 | } 30 | 31 | // Value implements the driver Valuer interface. 32 | func (v URL) Value() (driver.Value, error) { 33 | if !v.Valid() || v.url == nil { 34 | return nil, nil 35 | } 36 | return v.url.String(), nil 37 | } 38 | 39 | // Scan implements the sql.Scanner interface. 40 | func (v *URL) Scan(x interface{}) (err error) { 41 | v.url, v.ValidFlag, err = asURL(x) 42 | return 43 | } 44 | 45 | // Weak returns *url.URL, but if String.ValidFlag is false, returns nil. 46 | func (v URL) Weak() interface{} { 47 | return v.URL() 48 | } 49 | 50 | // Set sets a specified value. 51 | func (v *URL) Set(x interface{}) (err error) { 52 | return v.Scan(x) 53 | } 54 | 55 | // String implements the Stringer interface. 56 | func (v URL) String() string { 57 | if !v.Valid() || v.url == nil { 58 | return "" 59 | } 60 | return v.url.String() 61 | } 62 | 63 | // URL returns *url.URL, but if String.ValidFlag is false, returns nil. 64 | func (v URL) URL() *url.URL { 65 | if !v.Valid() || v.url == nil { 66 | return nil 67 | } 68 | return v.url 69 | } 70 | 71 | // MarshalJSON implements the json.Marshaler interface. 72 | func (v URL) MarshalJSON() ([]byte, error) { 73 | if !v.Valid() || v.url == nil { 74 | return nullBytes, nil 75 | } 76 | return json.Marshal(v.url.String()) 77 | } 78 | 79 | // UnmarshalJSON implements the json.Unmarshaler interface. 80 | func (v *URL) UnmarshalJSON(data []byte) error { 81 | if len(data) == 0 { 82 | return nil 83 | } 84 | var in interface{} 85 | if err := json.Unmarshal(data, &in); err != nil { 86 | return err 87 | } 88 | return v.Scan(in) 89 | } 90 | 91 | // EscapedPath returns the escaped form of v.url.Path. 92 | // In general there are multiple possible escaped forms of any path. 93 | // 94 | // EscapedPath returns v.url.RawPath when it is a valid escaping of v.url.Path. 95 | // Otherwise EscapedPath ignores v.url.RawPath and computes an escaped form on its own. 96 | // The String and RequestURI methods use EscapedPath to construct their results. 97 | // In general, code should call EscapedPath instead of reading v.url.RawPath directly. 98 | func (v URL) EscapedPath() string { 99 | if !v.Valid() || v.url == nil { 100 | return "" 101 | } 102 | return v.url.EscapedPath() 103 | } 104 | 105 | // Hostname returns v.url.Host, without any port number. 106 | // 107 | // If Host is an IPv6 literal with a port number, Hostname returns the IPv6 literal without the square brackets. 108 | // IPv6 literals may include a zone identifier. 109 | func (v URL) Hostname() string { 110 | if !v.Valid() || v.url == nil { 111 | return "" 112 | } 113 | return v.url.Hostname() 114 | } 115 | 116 | // IsAbs reports whether the URL is absolute. Absolute means that it has a non-empty scheme. 117 | func (v URL) IsAbs() bool { 118 | if !v.Valid() || v.url == nil { 119 | return false 120 | } 121 | return v.url.IsAbs() 122 | } 123 | 124 | // Port returns the port part of u.Host, without the leading colon. 125 | // If u.Host doesn't contain a port, Port returns an empty string. 126 | func (v URL) Port() string { 127 | if !v.Valid() || v.url == nil { 128 | return "" 129 | } 130 | return v.url.Port() 131 | } 132 | 133 | // Query parses RawQuery and returns the corresponding values. 134 | // It silently discards malformed value pairs. To check errors use ParseQuery. 135 | func (v URL) Query() url.Values { 136 | if !v.Valid() || v.url == nil { 137 | return url.Values{} 138 | } 139 | return v.url.Query() 140 | } 141 | 142 | // Parse parses a URL in the context of the receiver. 143 | // The provided URL may be relative or absolute. 144 | // Parse returns nil, err on parse failure, otherwise its return value is the same as ResolveReference. 145 | func (v URL) Parse(ref string) (result URL, err error) { 146 | if v.url == nil { 147 | u := url.URL{} 148 | v.url, err = u.Parse(ref) 149 | } else { 150 | v.url, err = v.url.Parse(ref) 151 | } 152 | if err != nil { 153 | v = URL{} 154 | return v, err 155 | } 156 | v.ValidFlag = true 157 | return v, err 158 | } 159 | 160 | // RequestURI returns the encoded path?query or opaque?query string that would be used in an HTTP request for v. 161 | func (v URL) RequestURI() string { 162 | if !v.Valid() || v.url == nil { 163 | return "" 164 | } 165 | return v.url.RequestURI() 166 | } 167 | 168 | // ResolveReference resolves a URI reference to an absolute URI from an absolute base URI, per RFC 3986 Section 5.2. 169 | // The URI reference may be relative or absolute. ResolveReference always returns a new URL instance, even if the returned URL is identical to either the base or reference. 170 | // If ref is an absolute URL, then ResolveReference ignores base and returns a copy of ref. 171 | func (v URL) ResolveReference(ref *url.URL) URL { 172 | if ref == nil { 173 | v.ValidFlag = false 174 | v.url = nil 175 | } else { 176 | v.ValidFlag = true 177 | v.url = v.url.ResolveReference(ref) 178 | } 179 | return v 180 | } 181 | -------------------------------------------------------------------------------- /type_time_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "encoding/json" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestMarshalTime(t *testing.T) { 12 | v := time.Now() 13 | expected := v 14 | ts, err := MarshalTime(v) 15 | if err != nil { 16 | t.Errorf("Not Expected error. error:%s", err.Error()) 17 | } 18 | if ts.Weak() != expected { 19 | t.Errorf("actual:%v, expected:%v", ts.Weak(), expected) 20 | } 21 | } 22 | 23 | func TestMustTime(t *testing.T) { 24 | v := time.Now() 25 | tests := []struct { 26 | name string 27 | args interface{} 28 | want Time 29 | wantPanic bool 30 | }{ 31 | { 32 | name: "valid", 33 | args: v, 34 | want: Time{ 35 | ValidFlag: true, 36 | time: v, 37 | }, 38 | wantPanic: false, 39 | }, 40 | { 41 | name: "panic", 42 | args: "valid paramenter", 43 | want: Time{ 44 | ValidFlag: false, 45 | }, 46 | wantPanic: true, 47 | }, 48 | } 49 | for _, tt := range tests { 50 | t.Run(tt.name, func(t *testing.T) { 51 | if tt.wantPanic { 52 | p := assert.Panics(t, func() { 53 | MustTime(tt.args) 54 | }) 55 | if !p { 56 | t.Errorf("MustTime() panic = %v, want panic %v", p, tt.wantPanic) 57 | } 58 | return 59 | } 60 | if got := MustTime(tt.args); got.Time() != v { 61 | t.Errorf("MustTime() = %v, want %v", got, tt.want) 62 | } 63 | }) 64 | } 65 | } 66 | 67 | func TestTimeJsonMarshal(t *testing.T) { 68 | v := time.Now() 69 | tt := Time{ 70 | ValidFlag: true, 71 | time: v, 72 | } 73 | expected := `"` + v.Format(time.RFC3339Nano) + `"` 74 | actual, err := json.Marshal(tt) 75 | if err != nil { 76 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 77 | } 78 | if string(actual) != expected { 79 | t.Errorf("actual:%s, expected:%s", string(actual), expected) 80 | } 81 | } 82 | 83 | func TestTimeJsonMarshalValidFalse(t *testing.T) { 84 | tt := Time{ 85 | ValidFlag: false, 86 | time: time.Now(), 87 | } 88 | expected := []byte("null") 89 | actual, err := json.Marshal(tt) 90 | if err != nil { 91 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 92 | } 93 | if string(actual) != string(expected) { 94 | t.Errorf("actual:%v, expected:%v", actual, expected) 95 | } 96 | } 97 | 98 | func TestTimeJsonUnmarshal(t *testing.T) { 99 | v := time.Now() 100 | in, _ := v.MarshalJSON() 101 | tt := Time{} 102 | if err := tt.UnmarshalJSON(in); err != nil { 103 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 104 | } 105 | if !tt.Valid() { 106 | t.Error("ValidFlag should be TRUE") 107 | } 108 | if tt.Time().Format(time.RFC3339) != v.Format(time.RFC3339) { 109 | t.Errorf("actual:%v, expected:%v", tt.Time(), v) 110 | } 111 | } 112 | 113 | func TestTimeJsonUnmarshalNil(t *testing.T) { 114 | tt := Time{} 115 | if err := tt.UnmarshalJSON(nil); err != nil { 116 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 117 | } 118 | if tt.Valid() { 119 | t.Error("ValidFlag should be FALSE") 120 | } 121 | if tt.Time() != time.Unix(0, 0) { 122 | t.Errorf("actual:%v, expected:%v", tt.Time(), time.Unix(0, 0)) 123 | } 124 | } 125 | 126 | func TestTimeJsonUnmarshalInvalid(t *testing.T) { 127 | tt := Time{} 128 | if err := tt.UnmarshalJSON([]byte(`"a`)); err == nil { 129 | t.Errorf("Expected error when json.Unmarshal, but not; %#v", tt) 130 | } 131 | } 132 | 133 | func TestTimeSetNil(t *testing.T) { 134 | tt := Time{} 135 | err := tt.Set(nil) 136 | if err != nil { 137 | t.Errorf("Not Expected error. error:%s", err.Error()) 138 | } 139 | if _, err = tt.Value(); err != nil { 140 | t.Errorf("This value should return nil. error:%s", err.Error()) 141 | } 142 | } 143 | 144 | func TestTimeSetTime(t *testing.T) { 145 | v := time.Now() 146 | tt := Time{} 147 | err := tt.Set(v) 148 | if err != nil { 149 | t.Errorf("Not Expected error") 150 | } 151 | if tt.Weak() == nil { 152 | t.Errorf("This value should return nil. error:%#v", tt.Weak()) 153 | } 154 | } 155 | 156 | func TestTimeSetInt64(t *testing.T) { 157 | var v int64 = 1367059792 158 | tt := Time{} 159 | err := tt.Set(v) 160 | if err == nil { 161 | t.Errorf("Not Expected error") 162 | } 163 | if tt.Weak() != nil { 164 | t.Errorf("This value should return nil. error:%#v", tt.Weak()) 165 | } 166 | } 167 | 168 | func TestTimeSetNumericString(t *testing.T) { 169 | v := "1467059792" 170 | tt := Time{} 171 | err := tt.Set(v) 172 | if err == nil { 173 | t.Errorf("Expected error.") 174 | } 175 | if tt.Weak() != nil { 176 | t.Errorf("This value should return nil. error:%#v", tt.Weak()) 177 | } 178 | } 179 | 180 | func TestTimeSetNonNumericString(t *testing.T) { 181 | v := "a" 182 | tt := Time{} 183 | err := tt.Set(v) 184 | if err == nil { 185 | t.Error("Expected error.") 186 | } 187 | if tt.Weak() != nil { 188 | t.Errorf("This value should return nil. error:%#v", tt.Weak()) 189 | } 190 | } 191 | 192 | func TestTimeSetBool(t *testing.T) { 193 | v := true 194 | tt := Time{} 195 | err := tt.Set(v) 196 | if err == nil { 197 | t.Error("Expected error.") 198 | } 199 | if tt.Weak() != nil { 200 | t.Errorf("This value should return nil. error:%#v", tt.Weak()) 201 | } 202 | } 203 | 204 | func TestTimeString(t *testing.T) { 205 | var expected = time.Now() 206 | tt := Time{} 207 | tt.Set(expected) // nolint 208 | if tt.String() != expected.String() { 209 | t.Errorf("actual:%s, expected:%s", tt.String(), expected.String()) 210 | } 211 | } 212 | 213 | func TestTimeStringInvalid(t *testing.T) { 214 | tt := Time{ 215 | ValidFlag: false, 216 | time: time.Now(), 217 | } 218 | if tt.String() != "" { 219 | t.Errorf("expected empty string, actual:%s", tt.String()) 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /type_string_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | type TestStringStruct struct { 12 | Int String `json:"int"` 13 | Float String `json:"float"` 14 | Bool String `json:"bool"` 15 | String String `json:"string"` 16 | HTML String `json:"html"` 17 | NullValue String `json:"null_value"` 18 | } 19 | 20 | func TestMarshalString(t *testing.T) { 21 | expected := String{ 22 | ValidFlag: true, 23 | string: "foobar", 24 | } 25 | 26 | s := "foobar" 27 | actual, err := MarshalString(s) 28 | if err != nil { 29 | t.Errorf("Not Expected error when MarshalString. error:%v", err.Error()) 30 | } 31 | if actual != expected { 32 | t.Errorf("actual:%v, expected:%v", actual, expected) 33 | } 34 | } 35 | 36 | func TestMustString(t *testing.T) { 37 | s := "foobar" 38 | tests := []struct { 39 | name string 40 | args interface{} 41 | want String 42 | wantPanic bool 43 | }{ 44 | { 45 | name: "valid", 46 | args: s, 47 | want: String{ 48 | ValidFlag: true, 49 | string: s, 50 | }, 51 | wantPanic: false, 52 | }, 53 | { 54 | name: "panic", 55 | args: struct{}{}, 56 | want: String{ 57 | ValidFlag: false, 58 | }, 59 | wantPanic: true, 60 | }, 61 | } 62 | for _, tt := range tests { 63 | t.Run(tt.name, func(t *testing.T) { 64 | if tt.wantPanic { 65 | p := assert.Panics(t, func() { 66 | MustString(tt.args) 67 | }) 68 | if !p { 69 | t.Errorf("MustString() panic = %v, want panic %v", p, tt.wantPanic) 70 | } 71 | return 72 | } 73 | if got := MustString(tt.args); !reflect.DeepEqual(got, tt.want) { 74 | t.Errorf("MustString() = %v, want %v", got, tt.want) 75 | } 76 | }) 77 | } 78 | } 79 | 80 | func TestStringJsonUnmarshalAndMarshal(t *testing.T) { 81 | var ts TestStringStruct 82 | jstr := `{"int":10,"float":1.1,"bool":false,"string":"qwertyuiopkjhgv876","html":"https://golang.org/src/encoding/json/encode.go?h=float64Encoder&foo=bar#L409","null_value":null}` 83 | expected := `{"int":"10","float":"1.1","bool":"false","string":"qwertyuiopkjhgv876","html":"https://golang.org/src/encoding/json/encode.go?h=float64Encoder\u0026foo=bar#L409","null_value":null}` 84 | err := json.Unmarshal([]byte(jstr), &ts) 85 | if err != nil { 86 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 87 | } 88 | b, err := json.Marshal(ts) 89 | if err != nil { 90 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 91 | } 92 | actual := string(b) 93 | if actual != expected { 94 | t.Errorf("actual:%s, expected:%s", actual, expected) 95 | } 96 | } 97 | 98 | func TestStringUnmarshalNil(t *testing.T) { 99 | var actual String 100 | expected := String{} 101 | err := actual.UnmarshalJSON(nil) 102 | if err != nil { 103 | t.Errorf("Not Expected error when json.Unmarshal. error:%s", err.Error()) 104 | } 105 | if actual != expected { 106 | t.Errorf("actual:%#v, expected:%#v", actual, expected) 107 | } 108 | } 109 | 110 | func TestStringUnmarshalNull(t *testing.T) { 111 | var actual String 112 | expected := String{} 113 | err := actual.UnmarshalJSON([]byte("null")) 114 | if err != nil { 115 | t.Errorf("Not Expected error when json.Unmarshal. error:%s", err.Error()) 116 | } 117 | if actual != expected { 118 | t.Errorf("actual:%#v, expected:%#v", actual, expected) 119 | } 120 | } 121 | 122 | func TestStringJsonUnmarshalInvalid(t *testing.T) { 123 | s := String{} 124 | if err := s.UnmarshalJSON([]byte(`"a`)); err == nil { 125 | t.Errorf("Expected error when json.Unmarshal, but not; %#v", s) 126 | } 127 | } 128 | 129 | func TestStringSetNil(t *testing.T) { 130 | ts := String{} 131 | err := ts.Set(nil) 132 | if err != nil { 133 | t.Errorf("Not Expected error. error:%v", err.Error()) 134 | } 135 | if ts.Weak() != nil { 136 | t.Errorf("This value should return nil. error:%#v", ts.Weak()) 137 | } 138 | } 139 | 140 | func TestStringSetInt64(t *testing.T) { 141 | var v int64 = 100 142 | expected := "100" 143 | ts := String{} 144 | err := ts.Set(v) 145 | if err != nil { 146 | t.Errorf("Not Expected error. error:%v", err.Error()) 147 | } 148 | if ts.Weak() != expected { 149 | t.Errorf("actual:%v, expected:%v", ts.Weak(), expected) 150 | } 151 | } 152 | 153 | func TestStringSetString(t *testing.T) { 154 | v := "vcrtyhjki876tfdews" 155 | expected := "vcrtyhjki876tfdews" 156 | ts := String{} 157 | err := ts.Set(v) 158 | if err != nil { 159 | t.Errorf("Not Expected error. error:%v", err.Error()) 160 | } 161 | if ts.Weak() != expected { 162 | t.Errorf("actual:%v, expected:%v", ts.Weak(), expected) 163 | } 164 | } 165 | 166 | func TestStringInvalidType(t *testing.T) { 167 | v := struct{}{} 168 | ts := String{} 169 | err := ts.Set(v) 170 | if err == nil { 171 | t.Errorf("Expected error. actual: %v", ts) 172 | } 173 | } 174 | 175 | func TestStringSetBool(t *testing.T) { 176 | v := true 177 | expected := "true" 178 | ts := String{} 179 | err := ts.Set(v) 180 | if err != nil { 181 | t.Errorf("Not Expected error. error:%v", err.Error()) 182 | } 183 | if ts.Weak() != expected { 184 | t.Errorf("actual:%v, expected:%v", ts.Weak(), expected) 185 | } 186 | } 187 | 188 | func TestString(t *testing.T) { 189 | expected := "vcrtyhjki876tfdews" 190 | ts := String{ 191 | ValidFlag: true, 192 | string: expected, 193 | } 194 | if ts.String() != expected { 195 | t.Errorf("actual:%s, expected:%s", ts.String(), expected) 196 | } 197 | } 198 | 199 | func TestStringInvalid(t *testing.T) { 200 | ts := String{ 201 | ValidFlag: false, 202 | string: "vcrtyhjki876tfdews", 203 | } 204 | if ts.String() != "" { 205 | t.Errorf("actual:%s, expected: (empty)", ts.String()) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /type_bool_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | type TestBoolStruct struct { 12 | Int Bool `json:"int"` 13 | Float Bool `json:"float"` 14 | Bool Bool `json:"bool"` 15 | String Bool `json:"string"` 16 | NullValue Bool `json:"null_value"` 17 | } 18 | 19 | func TestMarshalBool(t *testing.T) { 20 | expected := Bool{ 21 | ValidFlag: true, 22 | bool: true, 23 | } 24 | 25 | v := true 26 | actual, err := MarshalBool(v) 27 | if err != nil { 28 | t.Errorf("Not Expected error when MarshalBool. error:%s", err.Error()) 29 | } 30 | if actual != expected { 31 | t.Errorf("actual:%v, expected:%v", actual, expected) 32 | } 33 | } 34 | 35 | func TestMustBool(t *testing.T) { 36 | tests := []struct { 37 | name string 38 | args interface{} 39 | want Bool 40 | wantPanic bool 41 | }{ 42 | { 43 | name: "valid", 44 | args: true, 45 | want: Bool{ 46 | ValidFlag: true, 47 | bool: true, 48 | }, 49 | wantPanic: false, 50 | }, 51 | { 52 | name: "panic", 53 | args: "valid paramenter", 54 | want: Bool{ 55 | ValidFlag: false, 56 | }, 57 | wantPanic: true, 58 | }, 59 | } 60 | for _, tt := range tests { 61 | t.Run(tt.name, func(t *testing.T) { 62 | if tt.wantPanic { 63 | p := assert.Panics(t, func() { 64 | MustBool(tt.args) 65 | }) 66 | if !p { 67 | t.Errorf("MustBool() panic = %v, want panic %v", p, tt.wantPanic) 68 | } 69 | return 70 | } 71 | if got := MustBool(tt.args); !reflect.DeepEqual(got, tt.want) { 72 | t.Errorf("MustBool() = %v, want %v", got, tt.want) 73 | } 74 | }) 75 | } 76 | } 77 | 78 | func TestBoolJsonUnmarshalAndMarshal(t *testing.T) { 79 | var ts TestBoolStruct 80 | jstr := `{"int":10,"float":1.1,"bool":false,"string":"1","null_value":null}` 81 | expected := `{"int":true,"float":true,"bool":false,"string":true,"null_value":null}` 82 | err := json.Unmarshal([]byte(jstr), &ts) 83 | if err != nil { 84 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 85 | } 86 | b, err := json.Marshal(ts) 87 | if err != nil { 88 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 89 | } 90 | actual := string(b) 91 | if actual != expected { 92 | t.Errorf("actual:%s, expected:%s", actual, expected) 93 | } 94 | } 95 | 96 | func TestBoolJsonError(t *testing.T) { 97 | var ts TestBoolStruct 98 | jstr := `{"int":10,"float":1.0,"bool":true,"string":"あ","null_value":null}` 99 | expected := `{"int":true,"float":true,"bool":true,"string":null,"null_value":null}` 100 | err := json.Unmarshal([]byte(jstr), &ts) 101 | if err == nil { 102 | t.Error("Expected error when json.Unmarshal.") 103 | } 104 | b, err := json.Marshal(ts) 105 | if err != nil { 106 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 107 | } 108 | actual := string(b) 109 | if actual != expected { 110 | t.Errorf("actual:%s, expected:%s", actual, expected) 111 | } 112 | } 113 | 114 | func TestBoolUnmarshalNil(t *testing.T) { 115 | var actual Bool 116 | expected := Bool{} 117 | err := actual.UnmarshalJSON(nil) 118 | if err != nil { 119 | t.Errorf("Not Expected error when json.Unmarshal. error:%s", err.Error()) 120 | } 121 | if actual != expected { 122 | t.Errorf("actual:%#v, expected:%#v", actual, expected) 123 | } 124 | } 125 | 126 | func TestBoolJsonUnmarshalInvalid(t *testing.T) { 127 | tb := Bool{} 128 | if err := tb.UnmarshalJSON([]byte(`"true`)); err == nil { 129 | t.Errorf("Expected error when json.Unmarshal, but not; %#v", tb) 130 | } 131 | } 132 | 133 | func TestBoolSetNil(t *testing.T) { 134 | ts := Bool{} 135 | err := ts.Set(nil) 136 | if err != nil { 137 | t.Errorf("Not Expected error. error:%v", err.Error()) 138 | } 139 | if ts.Weak() != nil { 140 | t.Errorf("This value should return nil. error:%#v", ts.Weak()) 141 | } 142 | } 143 | 144 | func TestBoolSetInt64(t *testing.T) { 145 | var v int64 = 100 146 | expected := true 147 | ts := Bool{} 148 | err := ts.Set(v) 149 | if err != nil { 150 | t.Errorf("Not Expected error. error:%v", err.Error()) 151 | } 152 | if ts.Weak() != expected { 153 | t.Errorf("actual:%v, expected:%v", ts.Weak(), expected) 154 | } 155 | } 156 | 157 | func TestBoolSetString(t *testing.T) { 158 | v := "false" 159 | expected := false 160 | ts := Bool{} 161 | err := ts.Set(v) 162 | if err != nil { 163 | t.Errorf("Not Expected error. error:%v", err.Error()) 164 | } 165 | if ts.Weak() != expected { 166 | t.Errorf("actual:%v, expected:%v", ts.Weak(), expected) 167 | } 168 | } 169 | 170 | func TestBoolTrue(t *testing.T) { 171 | ts := Bool{ 172 | ValidFlag: true, 173 | bool: true, 174 | } 175 | if !ts.Bool() { 176 | t.Errorf("actual:%v, expected:true", ts.Bool()) 177 | } 178 | } 179 | 180 | func TestBoolFalse(t *testing.T) { 181 | ts := Bool{ 182 | ValidFlag: true, 183 | bool: false, 184 | } 185 | if ts.Bool() { 186 | t.Errorf("actual:%v, expected:false", ts.Bool()) 187 | } 188 | } 189 | 190 | func TestBoolInvalid(t *testing.T) { 191 | ts := Bool{ 192 | ValidFlag: false, 193 | bool: true, 194 | } 195 | if ts.Bool() { 196 | t.Errorf("actual:%v, expected:false", ts.Bool()) 197 | } 198 | } 199 | 200 | func TestBoolStringTrue(t *testing.T) { 201 | ts := Bool{ 202 | ValidFlag: true, 203 | bool: true, 204 | } 205 | if ts.String() != "true" { 206 | t.Errorf("actual:%s, expected:true", ts.String()) 207 | } 208 | } 209 | 210 | func TestBoolStringFalse(t *testing.T) { 211 | ts := Bool{ 212 | ValidFlag: true, 213 | bool: false, 214 | } 215 | if ts.String() != "false" { 216 | t.Errorf("actual:%s, expected:false", ts.String()) 217 | } 218 | } 219 | 220 | func TestBoolStringInvalid(t *testing.T) { 221 | ts := Bool{ 222 | ValidFlag: false, 223 | bool: true, 224 | } 225 | if ts.String() != "false" { 226 | t.Errorf("actual:%s, expected:false", ts.String()) 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /type_float_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | type TestFloatStruct struct { 12 | Int Float `json:"int"` 13 | Float Float `json:"float"` 14 | Bool Float `json:"bool"` 15 | String Float `json:"string"` 16 | NullValue Float `json:"null_value"` 17 | } 18 | 19 | func TestMarshalFloat(t *testing.T) { 20 | expected := Float{ 21 | ValidFlag: true, 22 | float: 1.01, 23 | } 24 | 25 | f := 1.01 26 | actual, err := MarshalFloat(f) 27 | if err != nil { 28 | t.Errorf("Not Expected error when MarshalFloat. error:%s", err.Error()) 29 | } 30 | if actual != expected { 31 | t.Errorf("actual:%v, expected:%v", actual, expected) 32 | } 33 | } 34 | 35 | func TestMustFloat(t *testing.T) { 36 | tests := []struct { 37 | name string 38 | args interface{} 39 | want Float 40 | wantPanic bool 41 | }{ 42 | { 43 | name: "valid", 44 | args: 1.23, 45 | want: Float{ 46 | ValidFlag: true, 47 | float: 1.23, 48 | }, 49 | wantPanic: false, 50 | }, 51 | { 52 | name: "panic", 53 | args: "valid paramenter", 54 | want: Float{ 55 | ValidFlag: false, 56 | }, 57 | wantPanic: true, 58 | }, 59 | } 60 | for _, tt := range tests { 61 | t.Run(tt.name, func(t *testing.T) { 62 | if tt.wantPanic { 63 | p := assert.Panics(t, func() { 64 | MustFloat(tt.args) 65 | }) 66 | if !p { 67 | t.Errorf("MustFloat() panic = %v, want panic %v", p, tt.wantPanic) 68 | } 69 | return 70 | } 71 | if got := MustFloat(tt.args); !reflect.DeepEqual(got, tt.want) { 72 | t.Errorf("MustFloat() = %v, want %v", got, tt.want) 73 | } 74 | }) 75 | } 76 | } 77 | 78 | func TestFloatJsonUnmarshalAndMarshal(t *testing.T) { 79 | var ts TestFloatStruct 80 | jstr := `{"int":10,"float":1.0,"bool":true,"string":"50","null_value":null}` 81 | expected := `{"int":10,"float":1,"bool":1,"string":50,"null_value":null}` 82 | err := json.Unmarshal([]byte(jstr), &ts) 83 | if err != nil { 84 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 85 | } 86 | b, err := json.Marshal(ts) 87 | if err != nil { 88 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 89 | } 90 | actual := string(b) 91 | if actual != expected { 92 | t.Errorf("actual:%s, expected:%s", actual, expected) 93 | } 94 | } 95 | 96 | func TestFloatUnmarshalEmpty(t *testing.T) { 97 | tf := Float{} 98 | err := tf.UnmarshalJSON(nil) 99 | if err != nil { 100 | t.Errorf("Not Expected error when json.Marshal. error:%s", err.Error()) 101 | } 102 | } 103 | 104 | func TestFloatJsonUnmarshalInvalid(t *testing.T) { 105 | tf := Float{} 106 | if err := tf.UnmarshalJSON([]byte(`"0`)); err == nil { 107 | t.Errorf("Expected error when json.Unmarshal, but not; %#v", tf) 108 | } 109 | } 110 | 111 | func TestFloatJsonError(t *testing.T) { 112 | var ts TestFloatStruct 113 | jstr := `{"int":10,"float":1.0,"bool":true,"string":"あ","null_value":null}` 114 | expected := `{"int":10,"float":1,"bool":1,"string":null,"null_value":null}` 115 | err := json.Unmarshal([]byte(jstr), &ts) 116 | if err == nil { 117 | t.Error("Expected error when json.Unmarshal.") 118 | } 119 | b, err := json.Marshal(ts) 120 | if err != nil { 121 | t.Errorf("Not Expected error when json.Marshal. error:%s", err.Error()) 122 | } 123 | actual := string(b) 124 | if actual != expected { 125 | t.Errorf("actual:%s, expected:%s", actual, expected) 126 | } 127 | } 128 | 129 | func TestFloatSetNil(t *testing.T) { 130 | tf := Float{} 131 | err := tf.Set(nil) 132 | if err != nil { 133 | t.Errorf("Not Expected error. error:%s", err.Error()) 134 | } 135 | if tf.Weak() != nil { 136 | t.Errorf("This value should return nil. error:%#v", tf.Weak()) 137 | } 138 | } 139 | 140 | func TestFloatSetInt64(t *testing.T) { 141 | var v int64 = 100 142 | var expected float64 = 100 143 | tf := Float{} 144 | err := tf.Set(v) 145 | if err != nil { 146 | t.Errorf("Not Expected error. error:%v", err.Error()) 147 | } 148 | if tf.Weak() != expected { 149 | t.Errorf("actual:%#v, expected:%#v", tf.Weak(), expected) 150 | } 151 | } 152 | 153 | func TestFloatSetNumericString(t *testing.T) { 154 | v := "56.0001" 155 | expected := 56.0001 156 | tf := Float{} 157 | err := tf.Set(v) 158 | if err != nil { 159 | t.Errorf("Not Expected error. error:%v", err.Error()) 160 | } 161 | if tf.Weak() != expected { 162 | t.Errorf("actual:%v, expected:%v", tf.Weak(), expected) 163 | } 164 | } 165 | 166 | func TestFloatSetNonNumericString(t *testing.T) { 167 | v := "a" 168 | var expected float64 169 | tf := Float{} 170 | err := tf.Set(v) 171 | if err == nil { 172 | t.Error("Expected error.") 173 | } 174 | if tf.Weak() == expected { 175 | t.Errorf("This value should return 0. value:%#v", tf.Weak()) 176 | } 177 | } 178 | 179 | func TestFloatFloat32(t *testing.T) { 180 | var expected float32 = 56.0001 181 | tf := Float{ 182 | ValidFlag: true, 183 | float: float64(expected), 184 | } 185 | if tf.Float32() != expected { 186 | t.Errorf("actual:%v, expected:%v", tf.Float32(), expected) 187 | } 188 | } 189 | 190 | func TestFloatFloat32Invalid(t *testing.T) { 191 | var expected float32 = 56.0001 192 | tf := Float{ 193 | ValidFlag: false, 194 | float: float64(expected), 195 | } 196 | if tf.Float32() != 0 { 197 | t.Errorf("actual:%v, expected:0", tf.Float32()) 198 | } 199 | } 200 | 201 | func TestFloatFloat64(t *testing.T) { 202 | var expected = 56.0001 203 | tf := Float{ 204 | ValidFlag: true, 205 | float: expected, 206 | } 207 | if tf.Float64() != expected { 208 | t.Errorf("actual:%v, expected:%v", tf.Float64(), expected) 209 | } 210 | } 211 | 212 | func TestFloatFloat64Invalid(t *testing.T) { 213 | tf := Float{ 214 | ValidFlag: false, 215 | float: 56.0001, 216 | } 217 | if tf.Float64() != 0 { 218 | t.Errorf("actual:%v, expected:0", tf.Float64()) 219 | } 220 | } 221 | 222 | func TestFloatString(t *testing.T) { 223 | var expected = "56.0001" 224 | tf := Float{} 225 | tf.Set(expected) // nolint 226 | if tf.String() != expected { 227 | t.Errorf("actual:%s, expected:%s", tf.String(), expected) 228 | } 229 | } 230 | 231 | func TestFloatStringInvalid(t *testing.T) { 232 | tf := Float{ 233 | ValidFlag: false, 234 | float: 56.0001, 235 | } 236 | if tf.String() != "" { 237 | t.Errorf("expected empty string, actual:%s", tf.String()) 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /type_uint_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | type TestUintStruct struct { 12 | Int Uint `json:"int"` 13 | Float Uint `json:"float"` 14 | Bool Uint `json:"bool"` 15 | String Uint `json:"string"` 16 | NullValue Uint `json:"null_value"` 17 | } 18 | 19 | func TestMarshalUint(t *testing.T) { 20 | expected := Uint{ 21 | ValidFlag: true, 22 | uint: 100, 23 | } 24 | 25 | i := 100 26 | actual, err := MarshalUint(i) 27 | if err != nil { 28 | t.Errorf("Not Expected error when MarshalUint. error:%v", err.Error()) 29 | } 30 | if actual != expected { 31 | t.Errorf("actual:%v, expected:%v", actual, expected) 32 | } 33 | } 34 | 35 | func TestMustUint(t *testing.T) { 36 | tests := []struct { 37 | name string 38 | args interface{} 39 | want Uint 40 | wantPanic bool 41 | }{ 42 | { 43 | name: "valid", 44 | args: 456, 45 | want: Uint{ 46 | ValidFlag: true, 47 | uint: 456, 48 | }, 49 | wantPanic: false, 50 | }, 51 | { 52 | name: "panic", 53 | args: "valid paramenter", 54 | want: Uint{ 55 | ValidFlag: false, 56 | }, 57 | wantPanic: true, 58 | }, 59 | } 60 | for _, tt := range tests { 61 | t.Run(tt.name, func(t *testing.T) { 62 | if tt.wantPanic { 63 | p := assert.Panics(t, func() { 64 | MustUint(tt.args) 65 | }) 66 | if !p { 67 | t.Errorf("MustUint() panic = %v, want panic %v", p, tt.wantPanic) 68 | } 69 | return 70 | } 71 | if got := MustUint(tt.args); !reflect.DeepEqual(got, tt.want) { 72 | t.Errorf("MustUint() = %v, want %v", got, tt.want) 73 | } 74 | }) 75 | } 76 | } 77 | 78 | func TestUintJsonUnmarshalAndMarshal(t *testing.T) { 79 | var ts TestUintStruct 80 | jstr := `{"int":10,"float":1.0,"bool":true,"string":"50","null_value":null}` 81 | expected := `{"int":10,"float":1,"bool":1,"string":50,"null_value":null}` 82 | err := json.Unmarshal([]byte(jstr), &ts) 83 | if err != nil { 84 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 85 | } 86 | b, err := json.Marshal(ts) 87 | if err != nil { 88 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 89 | } 90 | actual := string(b) 91 | if actual != expected { 92 | t.Errorf("actual:%s, expected:%s", actual, expected) 93 | } 94 | } 95 | 96 | func TestUintJsonUnmarshalInvalid(t *testing.T) { 97 | u := Uint{} 98 | if err := u.UnmarshalJSON([]byte(`"0`)); err == nil { 99 | t.Errorf("Expected error when json.Unmarshal, but not; %#v", u) 100 | } 101 | } 102 | 103 | func TestUintJsonError(t *testing.T) { 104 | var ts TestUintStruct 105 | jstr := `{"int":-10,"float":1.0,"bool":true,"string":"50","null_value":null}` 106 | expected := `{"int":null,"float":null,"bool":null,"string":null,"null_value":null}` 107 | err := json.Unmarshal([]byte(jstr), &ts) 108 | if err == nil { 109 | t.Error("Expected error when json.Unmarshal.") 110 | } 111 | b, err := json.Marshal(ts) 112 | if err != nil { 113 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 114 | } 115 | actual := string(b) 116 | if actual != expected { 117 | t.Errorf("actual:%s, expected:%s", actual, expected) 118 | } 119 | } 120 | 121 | func TestUintSetNil(t *testing.T) { 122 | tu := Uint{} 123 | err := tu.Set(nil) 124 | if err != nil { 125 | t.Errorf("Not Expected error. error:%v", err.Error()) 126 | } 127 | if tu.Weak() != nil { 128 | t.Errorf("This value should return nil. error:%#v", tu.Weak()) 129 | } 130 | } 131 | 132 | func TestUintSetInt64(t *testing.T) { 133 | var v int64 = 100 134 | var expected uint64 = 100 135 | tu := Uint{} 136 | err := tu.Set(v) 137 | if err != nil { 138 | t.Errorf("Not Expected error. error:%v", err.Error()) 139 | } 140 | if tu.Weak() != expected { 141 | t.Errorf("This value should return 100 (uint64). value:%#v", tu.Weak()) 142 | } 143 | } 144 | 145 | func TestUintSetNumericString(t *testing.T) { 146 | v := "56" 147 | var expected uint64 = 56 148 | tu := Uint{} 149 | err := tu.Set(v) 150 | if err != nil { 151 | t.Errorf("Not Expected error. error:%v", err.Error()) 152 | } 153 | if tu.Weak() != expected { 154 | t.Errorf("This value should return nil. error:%#v", tu.Weak()) 155 | } 156 | } 157 | 158 | func TestUintSetNonNumericString(t *testing.T) { 159 | v := "a" 160 | var expected uint64 161 | tu := Uint{} 162 | err := tu.Set(v) 163 | if err == nil { 164 | t.Error("Expected error.") 165 | } 166 | if tu.Weak() == expected { 167 | t.Errorf("This value should return 0. value:%#v", tu.Weak()) 168 | } 169 | } 170 | 171 | func TestUintUint(t *testing.T) { 172 | var expected uint = 123456789 173 | ti := Uint{ 174 | ValidFlag: true, 175 | uint: uint64(expected), 176 | } 177 | if ti.Uint() != expected { 178 | t.Errorf("actual:%d, expected:%d", ti.Uint(), expected) 179 | } 180 | } 181 | 182 | func TestUintUintInvalid(t *testing.T) { 183 | ti := Uint{ 184 | ValidFlag: false, 185 | uint: 123456789, 186 | } 187 | if ti.Uint() != 0 { 188 | t.Errorf("actual:%d, expected:0", ti.Uint()) 189 | } 190 | } 191 | 192 | func TestUintUint32(t *testing.T) { 193 | var expected uint32 = 123456789 194 | ti := Uint{ 195 | ValidFlag: true, 196 | uint: uint64(expected), 197 | } 198 | if ti.Uint32() != expected { 199 | t.Errorf("actual:%d, expected:%d", ti.Uint32(), expected) 200 | } 201 | } 202 | 203 | func TestUintUint32Invalid(t *testing.T) { 204 | ti := Uint{ 205 | ValidFlag: false, 206 | uint: 123456789, 207 | } 208 | if ti.Uint32() != 0 { 209 | t.Errorf("actual:%d, expected:0", ti.Uint32()) 210 | } 211 | } 212 | 213 | func TestUintUint64(t *testing.T) { 214 | var expected uint64 = 123456789 215 | ti := Uint{ 216 | ValidFlag: true, 217 | uint: expected, 218 | } 219 | if ti.Uint64() != expected { 220 | t.Errorf("actual:%d, expected:%d", ti.Uint64(), expected) 221 | } 222 | } 223 | 224 | func TestUintUint64Invalid(t *testing.T) { 225 | ti := Uint{ 226 | ValidFlag: false, 227 | uint: 123456789, 228 | } 229 | if ti.Uint64() != 0 { 230 | t.Errorf("actual:%d, expected:0", ti.Uint64()) 231 | } 232 | } 233 | 234 | func TestUintString(t *testing.T) { 235 | var expected = "123456789" 236 | ti := Uint{} 237 | ti.Set(expected) // nolint 238 | if ti.String() != expected { 239 | t.Errorf("actual:%s, expected:%s", ti.String(), expected) 240 | } 241 | } 242 | 243 | func TestUintStringInvalid(t *testing.T) { 244 | ti := Uint{ 245 | ValidFlag: false, 246 | uint: 123456789, 247 | } 248 | if ti.String() != "" { 249 | t.Errorf("expected empty string, actual:%s", ti.String()) 250 | } 251 | } 252 | -------------------------------------------------------------------------------- /convert.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "net/url" 5 | "reflect" 6 | "strconv" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | // asBool converts a specified value to boolean value. 12 | func asBool(x interface{}) (result bool, isValid ValidFlag, err error) { 13 | switch t := x.(type) { 14 | case nil: 15 | return result, false, nil 16 | case int, int8, int16, int32, int64: 17 | result = reflect.ValueOf(t).Int() != 0 18 | case uint, uint8, uint16, uint32, uint64: 19 | result = reflect.ValueOf(t).Uint() != 0 20 | case float32: 21 | result = x.(float32) != 0 22 | case float64: 23 | result = x.(float64) != 0 24 | case bool: 25 | result = x.(bool) 26 | case string: 27 | b, err := strconv.ParseBool(x.(string)) 28 | if err != nil { 29 | return result, false, ErrInvalidGenericValue{Value: x} 30 | } 31 | result = b 32 | default: 33 | return result, false, ErrInvalidGenericValue{Value: x} 34 | } 35 | return result, true, nil 36 | } 37 | 38 | // asBool converts a specified value to float64 value. 39 | func asFloat(x interface{}) (result float64, isValid ValidFlag, err error) { 40 | switch v := x.(type) { 41 | case nil: 42 | return result, false, nil 43 | case int: 44 | result = float64(v) 45 | case int8: 46 | result = float64(v) 47 | case int16: 48 | result = float64(v) 49 | case int32: 50 | result = float64(v) 51 | case int64: 52 | result = float64(v) 53 | case uint: 54 | result = float64(v) 55 | case uint8: 56 | result = float64(v) 57 | case uint16: 58 | result = float64(v) 59 | case uint32: 60 | result = float64(v) 61 | case uint64: 62 | result = float64(v) 63 | case float32: 64 | result = float64(v) 65 | case float64: 66 | result = v 67 | case bool: 68 | if v { 69 | result = 1 70 | } else { 71 | result = 0 72 | } 73 | case string: 74 | f, err := strconv.ParseFloat(v, 64) 75 | if err != nil { 76 | return result, false, ErrInvalidGenericValue{Value: x} 77 | } 78 | result = f 79 | default: 80 | return result, false, ErrInvalidGenericValue{Value: x} 81 | } 82 | return result, true, nil 83 | } 84 | 85 | // asBool converts a specified value to int64 value. 86 | func asInt(x interface{}) (result int64, isValid ValidFlag, err error) { 87 | switch t := x.(type) { 88 | case nil: 89 | return result, false, nil 90 | case int, int8, int16, int32, int64: 91 | result = reflect.ValueOf(t).Int() 92 | case uint, uint8, uint16, uint32, uint64: 93 | result = int64(reflect.ValueOf(t).Uint()) 94 | case float32: 95 | result = int64(x.(float32)) 96 | case float64: 97 | result = int64(x.(float64)) 98 | case bool: 99 | b := x.(bool) 100 | if b { 101 | result = 1 102 | } else { 103 | result = 0 104 | } 105 | case string: 106 | result, err = strconv.ParseInt(x.(string), 10, 64) 107 | if err != nil { 108 | return 0, false, ErrInvalidGenericValue{Value: x} 109 | } 110 | default: 111 | return result, false, ErrInvalidGenericValue{Value: x} 112 | } 113 | return result, true, nil 114 | } 115 | 116 | // asBool converts a specified value to string value. 117 | func asString(x interface{}) (result string, isValid ValidFlag, err error) { 118 | switch t := x.(type) { 119 | case nil: 120 | return result, false, nil 121 | case int, int8, int16, int32, int64: 122 | result = strconv.FormatInt(reflect.ValueOf(t).Int(), 10) 123 | case uint, uint8, uint16, uint32, uint64: 124 | result = strconv.FormatUint(reflect.ValueOf(t).Uint(), 10) 125 | case float32, float64: 126 | fs := strconv.FormatFloat(reflect.ValueOf(t).Float(), 'f', 10, 64) 127 | result = strings.TrimRight(strings.TrimRight(fs, "0"), ".") 128 | case bool: 129 | result = strconv.FormatBool(x.(bool)) 130 | case string: 131 | result = x.(string) 132 | default: 133 | return result, false, ErrInvalidGenericValue{Value: x} 134 | } 135 | return result, true, nil 136 | } 137 | 138 | // asBool converts a specified value to time.Time value. 139 | func asTime(x interface{}) (result time.Time, isValid ValidFlag, err error) { 140 | switch v := x.(type) { 141 | case nil: 142 | return result, false, nil 143 | case time.Time: 144 | result = v 145 | if result.IsZero() { 146 | return result, true, nil 147 | } 148 | default: 149 | return result, false, ErrInvalidGenericValue{Value: x} 150 | } 151 | return result, true, nil 152 | } 153 | 154 | // asTimestamp converts a specified value to time.Time value. 155 | func asTimestamp(x interface{}) (result time.Time, isValid ValidFlag, err error) { 156 | return asTimestampWithFunc(x, func(i int64) time.Time { 157 | return time.Unix(i, 0) 158 | }) 159 | } 160 | 161 | // asTimestampNanoseconds converts a specified value to time.Time value. 162 | func asTimestampNanoseconds(x interface{}) (result time.Time, isValid ValidFlag, err error) { 163 | return asTimestampWithFunc(x, func(i int64) time.Time { 164 | return time.Unix(0, i) 165 | }) 166 | } 167 | 168 | // asTimestampMilliseconds converts a specified value to time.Time value. 169 | func asTimestampMilliseconds(x interface{}) (result time.Time, isValid ValidFlag, err error) { 170 | return asTimestampWithFunc(x, func(i int64) time.Time { 171 | return time.Unix(0, i*1000000) 172 | }) 173 | } 174 | 175 | // asBool converts a specified value to uint64 value. 176 | func asUint(x interface{}) (result uint64, isValid ValidFlag, err error) { 177 | switch t := x.(type) { 178 | case nil: 179 | return 0, false, nil 180 | case int, int8, int16, int32, int64: 181 | i := reflect.ValueOf(t).Int() 182 | if i < 0 { 183 | return result, false, ErrInvalidGenericValue{Value: x} 184 | } 185 | result = uint64(i) 186 | case uint, uint8, uint16, uint32, uint64: 187 | result = reflect.ValueOf(t).Uint() 188 | case float32: 189 | f32 := x.(float32) 190 | if f32 < 0 { 191 | return result, false, ErrInvalidGenericValue{Value: x} 192 | } 193 | result = uint64(f32) 194 | case float64: 195 | f64 := x.(float64) 196 | if f64 < 0 { 197 | return result, false, ErrInvalidGenericValue{Value: x} 198 | } 199 | result = uint64(f64) 200 | case bool: 201 | if x.(bool) { 202 | result = 1 203 | } else { 204 | result = 0 205 | } 206 | case string: 207 | u64, err := strconv.ParseUint(x.(string), 10, 64) 208 | if err != nil { 209 | return result, false, ErrInvalidGenericValue{Value: x} 210 | } 211 | result = u64 212 | default: 213 | return result, false, ErrInvalidGenericValue{Value: x} 214 | } 215 | return result, true, nil 216 | } 217 | 218 | func asTimestampWithFunc(x interface{}, f func(i int64) time.Time) (result time.Time, isValid ValidFlag, err error) { 219 | var i int64 220 | switch t := x.(type) { 221 | case nil: 222 | return result, false, nil 223 | case time.Time: 224 | result = x.(time.Time) 225 | if result.IsZero() { 226 | return result, true, nil 227 | } 228 | return result, true, nil 229 | case string: 230 | result, err = time.Parse(time.RFC3339Nano, x.(string)) 231 | return result, err == nil, err 232 | case int, int8, int16, int32, int64: 233 | i = reflect.ValueOf(t).Int() 234 | case uint, uint8, uint16, uint32, uint64: 235 | i = int64(reflect.ValueOf(t).Uint()) 236 | case float32: 237 | i = int64(x.(float32)) 238 | case float64: 239 | i = int64(x.(float64)) 240 | default: 241 | return result, false, ErrInvalidGenericValue{Value: x} 242 | } 243 | if i < 0 { 244 | return result, false, ErrInvalidGenericValue{Value: x} 245 | } 246 | return f(i), true, nil 247 | } 248 | 249 | func asURL(x interface{}) (result *url.URL, isValid ValidFlag, err error) { 250 | switch v := x.(type) { 251 | case nil: 252 | return nil, false, nil 253 | case *url.URL: 254 | result = v 255 | case string: 256 | result, err = url.Parse(v) 257 | default: 258 | err = ErrInvalidGenericValue{Value: x} 259 | } 260 | return result, (err == nil), err 261 | } 262 | -------------------------------------------------------------------------------- /type_timestamp_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "strconv" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestMarshalTimestamp(t *testing.T) { 14 | v := time.Now() 15 | expected := v 16 | ts, err := MarshalTimestamp(v) 17 | if err != nil { 18 | t.Errorf("Not Expected error. error:%s", err.Error()) 19 | } 20 | if ts.Weak() != expected { 21 | t.Errorf("actual:%v, expected:%v", ts.Weak(), expected) 22 | } 23 | } 24 | 25 | func TestMustTimestamp(t *testing.T) { 26 | v := time.Now() 27 | tests := []struct { 28 | name string 29 | args interface{} 30 | want Timestamp 31 | wantPanic bool 32 | }{ 33 | { 34 | name: "valid", 35 | args: v, 36 | want: Timestamp{ 37 | ValidFlag: true, 38 | time: v, 39 | }, 40 | wantPanic: false, 41 | }, 42 | { 43 | name: "panic", 44 | args: "valid paramenter", 45 | want: Timestamp{ 46 | ValidFlag: false, 47 | }, 48 | wantPanic: true, 49 | }, 50 | } 51 | for _, tt := range tests { 52 | t.Run(tt.name, func(t *testing.T) { 53 | if tt.wantPanic { 54 | p := assert.Panics(t, func() { 55 | MustTimestamp(tt.args) 56 | }) 57 | if !p { 58 | t.Errorf("MustTimestamp() panic = %v, want panic %v", p, tt.wantPanic) 59 | } 60 | return 61 | } 62 | if got := MustTimestamp(tt.args); got.Weak() != v { 63 | t.Errorf("MustTimestamp() = %v, want %v", got, tt.want) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestTimestampJsonMarshal(t *testing.T) { 70 | v := time.Now() 71 | ts := Timestamp{ 72 | ValidFlag: true, 73 | time: v, 74 | } 75 | expected := strconv.FormatInt(v.Unix(), 10) 76 | actual, err := json.Marshal(ts) 77 | if err != nil { 78 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 79 | } 80 | if string(actual) != expected { 81 | t.Errorf("actual:%s, expected:%s", string(actual), expected) 82 | } 83 | } 84 | 85 | func TestTimestampJsonMarshalValidFalse(t *testing.T) { 86 | ts := Timestamp{ 87 | ValidFlag: false, 88 | time: time.Now(), 89 | } 90 | expected := []byte("null") 91 | actual, err := json.Marshal(ts) 92 | if err != nil { 93 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 94 | } 95 | if string(actual) != string(expected) { 96 | t.Errorf("actual:%v, expected:%v", actual, expected) 97 | } 98 | } 99 | 100 | func TestTimestampJsonUnmarshal(t *testing.T) { 101 | v := time.Now() 102 | in, _ := v.MarshalJSON() 103 | ts := Timestamp{} 104 | if err := ts.UnmarshalJSON(in); err != nil { 105 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 106 | } 107 | if !ts.Valid() { 108 | t.Error("ValidFlag should be TRUE") 109 | } 110 | if ts.Int64() != v.Unix() { 111 | t.Errorf("actual:%v, expected:%v", ts.Int64(), v) 112 | } 113 | } 114 | 115 | func TestTimestampJsonUnmarshalNil(t *testing.T) { 116 | ts := Timestamp{} 117 | if err := ts.UnmarshalJSON(nil); err != nil { 118 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 119 | } 120 | if ts.Valid() { 121 | t.Error("ValidFlag should be FALSE") 122 | } 123 | if ts.Int64() != 0 { 124 | t.Errorf("actual:%v, expected:%v", ts.Int64(), 0) 125 | } 126 | } 127 | 128 | func TestTimestampJsonUnmarshalInvalid(t *testing.T) { 129 | ts := Timestamp{} 130 | if err := ts.UnmarshalJSON([]byte(`"a`)); err == nil { 131 | t.Errorf("Expected error when json.Unmarshal, but not; %#v", ts) 132 | } 133 | } 134 | 135 | func TestTimestampSetNil(t *testing.T) { 136 | ts := Timestamp{} 137 | err := ts.Set(nil) 138 | if err != nil { 139 | t.Errorf("Not Expected error. error:%s", err.Error()) 140 | } 141 | actual, err := ts.Value() 142 | if err != nil { 143 | t.Errorf("This value should return nil. error:%s", err.Error()) 144 | } 145 | if actual != nil { 146 | t.Errorf("actual:%d, expected:nil", actual) 147 | } 148 | } 149 | 150 | func TestTimestampSetTime(t *testing.T) { 151 | v := time.Now() 152 | expected := v 153 | ts := Timestamp{} 154 | err := ts.Set(v) 155 | if err != nil { 156 | t.Errorf("Not Expected error. error:%s", err.Error()) 157 | } 158 | if ts.Weak() != expected { 159 | t.Errorf("actual:%v, expected:%v", ts.Weak(), expected) 160 | } 161 | } 162 | 163 | func TestTimestampSetInt64(t *testing.T) { 164 | var v int64 = 1367059792 165 | expected := time.Unix(v, 0) 166 | ts := Timestamp{} 167 | err := ts.Set(v) 168 | if err != nil { 169 | t.Errorf("Not Expected error. error:%s", err.Error()) 170 | } 171 | if ts.Weak() != expected { 172 | t.Errorf("actual:%v, expected:%v", ts.Weak(), expected) 173 | } 174 | } 175 | 176 | func TestTimestampSetNumericString(t *testing.T) { 177 | v := "1467059792" 178 | ts := Timestamp{} 179 | err := ts.Set(v) 180 | if err == nil { 181 | t.Errorf("Expected error.") 182 | } 183 | if ts.Weak() != nil { 184 | t.Errorf("This value should return nil. actual:%#v", ts.Weak()) 185 | } 186 | } 187 | 188 | func TestTimestampSetNonNumericString(t *testing.T) { 189 | v := "a" 190 | ts := Timestamp{} 191 | err := ts.Set(v) 192 | if err == nil { 193 | t.Error("Expected error.") 194 | } 195 | if ts.Weak() != nil { 196 | t.Errorf("This value should return nil. actual:%#v", ts.Weak()) 197 | } 198 | } 199 | 200 | func TestTimestampSetBool(t *testing.T) { 201 | v := true 202 | ts := Timestamp{} 203 | err := ts.Set(v) 204 | if err == nil { 205 | t.Error("Expected error.") 206 | } 207 | if ts.Weak() != nil { 208 | t.Errorf("This value should return nil. actual:%#v", ts.Weak()) 209 | } 210 | } 211 | 212 | func TestTimestampInt64(t *testing.T) { 213 | v := time.Now() 214 | expected := v.Unix() 215 | ts := Timestamp{} 216 | err := ts.Set(v) 217 | if err != nil { 218 | t.Error("Not expected error.") 219 | } 220 | if ts.Int64() != expected { 221 | t.Errorf("This value should return %d. value:%d", expected, ts.Int()) 222 | } 223 | } 224 | 225 | func TestTimestampInt64Zero(t *testing.T) { 226 | v := time.Unix(0, 0) 227 | var expected int64 228 | ts := Timestamp{} 229 | err := ts.Set(v) 230 | if err != nil { 231 | t.Error("Not expected error.") 232 | } 233 | if ts.Int64() != expected { 234 | t.Errorf("This value should return %d. value:%d", expected, ts.Int64()) 235 | } 236 | } 237 | 238 | func TestTimestampInt(t *testing.T) { 239 | v := time.Now() 240 | expected := int(v.Unix()) 241 | ts := Timestamp{} 242 | err := ts.Set(v) 243 | if err != nil { 244 | t.Error("Not expected error.") 245 | } 246 | if ts.Int() != expected { 247 | t.Errorf("This value should return %d. value:%d", expected, ts.Int()) 248 | } 249 | } 250 | 251 | func TestTimestampString(t *testing.T) { 252 | v := time.Now() 253 | expected := strconv.FormatInt(v.Unix(), 10) 254 | ts := Timestamp{} 255 | err := ts.Set(v) 256 | if err != nil { 257 | t.Error("Not expected error.") 258 | } 259 | if ts.String() != expected { 260 | t.Errorf("This value should return %s. value:%s", expected, ts.String()) 261 | } 262 | } 263 | 264 | func TestTimestamp_Time(t *testing.T) { 265 | now := time.Now() 266 | 267 | type fields struct { 268 | ValidFlag ValidFlag 269 | time time.Time 270 | } 271 | tests := []struct { 272 | name string 273 | fields fields 274 | want time.Time 275 | }{ 276 | { 277 | name: "now", 278 | fields: fields{ 279 | ValidFlag: true, 280 | time: now, 281 | }, 282 | want: now, 283 | }, 284 | { 285 | name: "invalid", 286 | fields: fields{ 287 | ValidFlag: false, 288 | time: now, 289 | }, 290 | want: time.Unix(0, 0), 291 | }, 292 | } 293 | for _, tt := range tests { 294 | t.Run(tt.name, func(t *testing.T) { 295 | v := Timestamp{ 296 | ValidFlag: tt.fields.ValidFlag, 297 | time: tt.fields.time, 298 | } 299 | if got := v.Time(); !reflect.DeepEqual(got, tt.want) { 300 | t.Errorf("Timestamp.Time() = %v, want %v", got, tt.want) 301 | } 302 | }) 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /type_timestamp_milliseconds_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "strconv" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestMarshalTimestampMS(t *testing.T) { 14 | v := time.Now() 15 | expected := v.UnixNano() / 1000000 16 | ts, err := MarshalTimestampMS(v) 17 | if err != nil { 18 | t.Errorf("Not Expected error. error:%s", err.Error()) 19 | } 20 | if ts.Weak() != expected { 21 | t.Errorf("actual:%[1]v(%[1]T), expected:%[2]v(%[2]T)", ts.Weak(), expected) 22 | } 23 | } 24 | 25 | func TestMustTimestampMS(t *testing.T) { 26 | v := time.Now() 27 | expected := v.UnixNano() / 1000000 28 | tests := []struct { 29 | name string 30 | args interface{} 31 | want TimestampMS 32 | wantPanic bool 33 | }{ 34 | { 35 | name: "valid", 36 | args: v, 37 | want: TimestampMS{ 38 | ValidFlag: true, 39 | time: v, 40 | }, 41 | wantPanic: false, 42 | }, 43 | { 44 | name: "panic", 45 | args: "valid paramenter", 46 | want: TimestampMS{ 47 | ValidFlag: false, 48 | }, 49 | wantPanic: true, 50 | }, 51 | } 52 | for _, tt := range tests { 53 | t.Run(tt.name, func(t *testing.T) { 54 | if tt.wantPanic { 55 | p := assert.Panics(t, func() { 56 | MustTimestampMS(tt.args) 57 | }) 58 | if !p { 59 | t.Errorf("MustTimestampMS() panic = %v, want panic %v", p, tt.wantPanic) 60 | } 61 | return 62 | } 63 | if got := MustTimestampMS(tt.args); got.Weak() != expected { 64 | t.Errorf("MustTimestampMS() = %v, want %v", got, tt.want) 65 | } 66 | }) 67 | } 68 | } 69 | 70 | func TestTimestampMSJsonMarshal(t *testing.T) { 71 | v := time.Now() 72 | tm := TimestampMS{ 73 | ValidFlag: true, 74 | time: v, 75 | } 76 | expected := strconv.FormatInt(v.UnixNano()/1000000, 10) 77 | actual, err := json.Marshal(tm) 78 | if err != nil { 79 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 80 | } 81 | if string(actual) != expected { 82 | t.Errorf("actual:%s, expected:%s", string(actual), expected) 83 | } 84 | } 85 | 86 | func TestTimestampMSJsonMarshalValidFalse(t *testing.T) { 87 | tm := TimestampMS{ 88 | ValidFlag: false, 89 | time: time.Now(), 90 | } 91 | expected := []byte("null") 92 | actual, err := json.Marshal(tm) 93 | if err != nil { 94 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 95 | } 96 | if string(actual) != string(expected) { 97 | t.Errorf("actual:%v, expected:%v", actual, expected) 98 | } 99 | } 100 | 101 | func TestTimestampMSJsonUnmarshal(t *testing.T) { 102 | v := time.Now() 103 | in, _ := v.MarshalJSON() 104 | tm := TimestampMS{} 105 | if err := tm.UnmarshalJSON(in); err != nil { 106 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 107 | } 108 | if !tm.Valid() { 109 | t.Error("ValidFlag should be TRUE") 110 | } 111 | if tm.Int64() != v.UnixNano()/1000000 { 112 | t.Errorf("actual:%d, expected:%d", tm.Int64(), v.UnixNano()/1000000) 113 | } 114 | } 115 | 116 | func TestTimestampMSJsonUnmarshalNil(t *testing.T) { 117 | tm := TimestampMS{} 118 | if err := tm.UnmarshalJSON(nil); err != nil { 119 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 120 | } 121 | if tm.Valid() { 122 | t.Error("ValidFlag should be FALSE") 123 | } 124 | if tm.Int64() != 0 { 125 | t.Errorf("actual:%d, expected:%d", tm.Int64(), 0) 126 | } 127 | } 128 | 129 | func TestTimestampMSJsonUnmarshalInvalid(t *testing.T) { 130 | tm := TimestampMS{} 131 | if err := tm.UnmarshalJSON([]byte(`"a`)); err == nil { 132 | t.Errorf("Expected error when json.Unmarshal, but not; %#v", tm) 133 | } 134 | } 135 | 136 | func TestTimestampMSSetNil(t *testing.T) { 137 | tm := TimestampMS{} 138 | err := tm.Set(nil) 139 | if err != nil { 140 | t.Errorf("Not Expected error. error:%s", err.Error()) 141 | } 142 | if _, err = tm.Value(); err != nil { 143 | t.Errorf("This value should return nil. error:%s", err.Error()) 144 | } 145 | } 146 | 147 | func TestTimestampMSSetTime(t *testing.T) { 148 | v := time.Now() 149 | expected := v 150 | tm := TimestampMS{} 151 | err := tm.Set(v) 152 | if err != nil { 153 | t.Errorf("Not Expected error. error:%s", err.Error()) 154 | } 155 | if tm.Weak() != expected.UnixNano()/1000000 { 156 | t.Errorf("actual:%v, expected:%v", tm.Weak(), expected) 157 | } 158 | } 159 | 160 | func TestTimestampMSSetInt64(t *testing.T) { 161 | var v int64 = 1367059792 162 | expected := time.Unix(0, v*1000000) 163 | tm := TimestampMS{} 164 | err := tm.Set(v) 165 | if err != nil { 166 | t.Errorf("Not Expected error. error:%s", err.Error()) 167 | } 168 | if tm.Weak() != expected.UnixNano()/1000000 { 169 | t.Errorf("actual:%v, expected:%v", tm.Weak(), expected) 170 | } 171 | } 172 | 173 | func TestTimestampMSSetNumericString(t *testing.T) { 174 | v := "1467059792" 175 | tm := TimestampMS{} 176 | err := tm.Set(v) 177 | if err == nil { 178 | t.Errorf("Expected error.") 179 | } 180 | if tm.Weak() != nil { 181 | t.Errorf("This value should return nil. value:%#v", tm.Weak()) 182 | } 183 | } 184 | 185 | func TestTimestampMSSetNonNumericString(t *testing.T) { 186 | v := "a" 187 | tm := TimestampMS{} 188 | err := tm.Set(v) 189 | if err == nil { 190 | t.Error("Expected error.") 191 | } 192 | if tm.Weak() != nil { 193 | t.Errorf("This value should return nil. value:%#v", tm.Weak()) 194 | } 195 | } 196 | 197 | func TestTimestampMSSetBool(t *testing.T) { 198 | v := true 199 | tm := TimestampMS{} 200 | err := tm.Set(v) 201 | if err == nil { 202 | t.Errorf("Not Expected error. error:%s", err.Error()) 203 | } 204 | if tm.Weak() != nil { 205 | t.Errorf("This value should return nil. value:%#v", tm.Weak()) 206 | } 207 | } 208 | 209 | func TestTimestampMSInt64(t *testing.T) { 210 | v := time.Now() 211 | expected := v.UnixNano() / 1000000 212 | tm := TimestampMS{} 213 | err := tm.Set(v) 214 | if err != nil { 215 | t.Error("Not expected error.") 216 | } 217 | if tm.Int64() != expected { 218 | t.Errorf("This value should return %d. value:%d", expected, tm.Int()) 219 | } 220 | } 221 | 222 | func TestTimestampMSInt64Zero(t *testing.T) { 223 | v := time.Unix(0, 0) 224 | var expected int64 225 | tm := TimestampMS{} 226 | err := tm.Set(v) 227 | if err != nil { 228 | t.Error("Not expected error.") 229 | } 230 | if tm.Int64() != expected { 231 | t.Errorf("This value should return %d. value:%d", expected, tm.Int()) 232 | } 233 | } 234 | 235 | func TestTimestampMSInt(t *testing.T) { 236 | v := time.Now() 237 | expected := int(v.UnixNano() / 1000000) 238 | tm := TimestampMS{} 239 | err := tm.Set(v) 240 | if err != nil { 241 | t.Error("Not expected error.") 242 | } 243 | if tm.Int() != expected { 244 | t.Errorf("This value should return %d. value:%d", expected, tm.Int()) 245 | } 246 | } 247 | 248 | func TestTimestampMSString(t *testing.T) { 249 | v := time.Now() 250 | expected := strconv.FormatInt(v.UnixNano()/1000000, 10) 251 | tm := TimestampMS{} 252 | err := tm.Set(v) 253 | if err != nil { 254 | t.Error("Not expected error.") 255 | } 256 | if tm.String() != expected { 257 | t.Errorf("This value should return %s. value:%s", expected, tm.String()) 258 | } 259 | } 260 | 261 | func TestTimestampMS_Time(t *testing.T) { 262 | now := time.Now() 263 | 264 | type fields struct { 265 | ValidFlag ValidFlag 266 | time time.Time 267 | } 268 | tests := []struct { 269 | name string 270 | fields fields 271 | want time.Time 272 | }{ 273 | { 274 | name: "now", 275 | fields: fields{ 276 | ValidFlag: true, 277 | time: now, 278 | }, 279 | want: now, 280 | }, 281 | { 282 | name: "invalid", 283 | fields: fields{ 284 | ValidFlag: false, 285 | time: now, 286 | }, 287 | want: time.Unix(0, 0), 288 | }, 289 | } 290 | for _, tt := range tests { 291 | t.Run(tt.name, func(t *testing.T) { 292 | v := TimestampMS{ 293 | ValidFlag: tt.fields.ValidFlag, 294 | time: tt.fields.time, 295 | } 296 | if got := v.Time(); !reflect.DeepEqual(got, tt.want) { 297 | t.Errorf("TimestampMS.Time() = %v, want %v", got, tt.want) 298 | } 299 | }) 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /type_timestamp_nanoseconds_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "strconv" 7 | "testing" 8 | "time" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestMarshalTimestampNano(t *testing.T) { 14 | v := time.Now() 15 | expected := v.UnixNano() 16 | ts, err := MarshalTimestampNano(v) 17 | if err != nil { 18 | t.Errorf("Not Expected error. error:%s", err.Error()) 19 | } 20 | if ts.Weak() != expected { 21 | t.Errorf("actual:%[1]v(%[1]T), expected:%[2]v(%[2]T)", ts.Weak(), expected) 22 | } 23 | } 24 | 25 | func TestMustTimestampNano(t *testing.T) { 26 | v := time.Now() 27 | tests := []struct { 28 | name string 29 | args interface{} 30 | want TimestampNano 31 | wantPanic bool 32 | }{ 33 | { 34 | name: "valid", 35 | args: v, 36 | want: TimestampNano{ 37 | ValidFlag: true, 38 | time: v, 39 | }, 40 | wantPanic: false, 41 | }, 42 | { 43 | name: "panic", 44 | args: "valid paramenter", 45 | want: TimestampNano{ 46 | ValidFlag: false, 47 | }, 48 | wantPanic: true, 49 | }, 50 | } 51 | for _, tt := range tests { 52 | t.Run(tt.name, func(t *testing.T) { 53 | if tt.wantPanic { 54 | p := assert.Panics(t, func() { 55 | MustTimestampNano(tt.args) 56 | }) 57 | if !p { 58 | t.Errorf("MustTimestampNano() panic = %v, want panic %v", p, tt.wantPanic) 59 | } 60 | return 61 | } 62 | if got := MustTimestampNano(tt.args); got.Weak() != v.UnixNano() { 63 | t.Errorf("MustTimestampNano() = %v, want %v", got, tt.want) 64 | } 65 | }) 66 | } 67 | } 68 | 69 | func TestTimestampNanoJsonMarshal(t *testing.T) { 70 | v := time.Now() 71 | tn := TimestampNano{ 72 | ValidFlag: true, 73 | time: v, 74 | } 75 | expected := strconv.FormatInt(v.UnixNano(), 10) 76 | actual, err := json.Marshal(tn) 77 | if err != nil { 78 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 79 | } 80 | if string(actual) != expected { 81 | t.Errorf("actual:%s, expected:%s", string(actual), expected) 82 | } 83 | } 84 | 85 | func TestTimestampNanoJsonMarshalValidFalse(t *testing.T) { 86 | tn := TimestampNano{ 87 | ValidFlag: false, 88 | time: time.Now(), 89 | } 90 | expected := []byte("null") 91 | actual, err := json.Marshal(tn) 92 | if err != nil { 93 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 94 | } 95 | if string(actual) != string(expected) { 96 | t.Errorf("actual:%v, expected:%v", actual, expected) 97 | } 98 | } 99 | 100 | func TestTimestampNanoJsonUnmarshal(t *testing.T) { 101 | v := time.Now() 102 | in, _ := v.MarshalJSON() 103 | tn := TimestampNano{} 104 | if err := tn.UnmarshalJSON(in); err != nil { 105 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 106 | } 107 | if !tn.Valid() { 108 | t.Error("ValidFlag should be TRUE") 109 | } 110 | if tn.Int64() != v.UnixNano() { 111 | t.Errorf("actual:%d, expected:%d", tn.Int64(), v.UnixNano()) 112 | } 113 | } 114 | 115 | func TestTimestampNanoJsonUnmarshalNil(t *testing.T) { 116 | tn := TimestampNano{} 117 | if err := tn.UnmarshalJSON(nil); err != nil { 118 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 119 | } 120 | if tn.Valid() { 121 | t.Error("ValidFlag should be FALSE") 122 | } 123 | if tn.Int64() != 0 { 124 | t.Errorf("actual:%d, expected:%d", tn.Int64(), 0) 125 | } 126 | } 127 | 128 | func TestTimestampNanoJsonUnmarshalInvalid(t *testing.T) { 129 | tn := TimestampNano{} 130 | if err := tn.UnmarshalJSON([]byte(`"a`)); err == nil { 131 | t.Errorf("Expected error when json.Unmarshal, but not; %#v", tn) 132 | } 133 | } 134 | 135 | func TestTimestampNanoSetNil(t *testing.T) { 136 | tn := TimestampNano{} 137 | err := tn.Set(nil) 138 | if err != nil { 139 | t.Errorf("Not Expected error. error:%s", err.Error()) 140 | } 141 | actual, err := tn.Value() 142 | if err != nil { 143 | t.Errorf("This value should return nil. error:%s", err.Error()) 144 | } 145 | if actual != nil { 146 | t.Errorf("actual:%d, expected:nil", actual) 147 | } 148 | } 149 | 150 | func TestTimestampNanoSetTime(t *testing.T) { 151 | v := time.Now() 152 | expected := v 153 | tn := TimestampNano{} 154 | err := tn.Set(v) 155 | if err != nil { 156 | t.Errorf("Not Expected error. error:%s", err.Error()) 157 | } 158 | if tn.Weak() != expected.UnixNano() { 159 | t.Errorf("actual:%v, expected:%v", tn.Weak(), expected) 160 | } 161 | } 162 | 163 | func TestTimestampNanoSetInt64(t *testing.T) { 164 | var v int64 = 1367059792 165 | expected := time.Unix(0, v) 166 | tn := TimestampNano{} 167 | err := tn.Set(v) 168 | if err != nil { 169 | t.Errorf("Not Expected error. error:%s", err.Error()) 170 | } 171 | if actual := tn.Weak(); actual != expected.UnixNano() { 172 | t.Errorf("actual:%v, expected:%v", actual, expected.UnixNano()) 173 | } 174 | } 175 | 176 | func TestTimestampNanoSetNumericString(t *testing.T) { 177 | v := "1467059792" 178 | tn := TimestampNano{} 179 | err := tn.Set(v) 180 | if err == nil { 181 | t.Errorf("Expected error.") 182 | } 183 | if tn.Weak() != nil { 184 | t.Errorf("This value should return nil. value:%#v", tn.Weak()) 185 | } 186 | } 187 | 188 | func TestTimestampNanoSetNonNumericString(t *testing.T) { 189 | v := "a" 190 | tn := TimestampNano{} 191 | err := tn.Set(v) 192 | if err == nil { 193 | t.Error("Expected error.") 194 | } 195 | if tn.Weak() != nil { 196 | t.Errorf("This value should return nil. value:%#v", tn.Weak()) 197 | } 198 | } 199 | 200 | func TestTimestampNanoSetBool(t *testing.T) { 201 | v := true 202 | tn := TimestampNano{} 203 | err := tn.Set(v) 204 | if err == nil { 205 | t.Error("Expected error.") 206 | } 207 | if tn.Weak() != nil { 208 | t.Errorf("This value should return nil. value:%#v", tn.Weak()) 209 | } 210 | } 211 | 212 | func TestTimestampNanoInt64(t *testing.T) { 213 | v := time.Now() 214 | expected := v.UnixNano() 215 | tm := TimestampNano{} 216 | err := tm.Set(v) 217 | if err != nil { 218 | t.Error("Not expected error.") 219 | } 220 | if tm.Int64() != expected { 221 | t.Errorf("This value should return %d. value:%d", expected, tm.Int()) 222 | } 223 | } 224 | 225 | func TestTimestampNanoInt64Zero(t *testing.T) { 226 | v := time.Unix(0, 0) 227 | var expected int64 228 | tm := TimestampNano{} 229 | err := tm.Set(v) 230 | if err != nil { 231 | t.Error("Not expected error.") 232 | } 233 | if tm.Int64() != expected { 234 | t.Errorf("This value should return %d. value:%d", expected, tm.Int()) 235 | } 236 | } 237 | 238 | func TestTimestampNanoInt(t *testing.T) { 239 | v := time.Now() 240 | expected := int(v.UnixNano()) 241 | tm := TimestampNano{} 242 | err := tm.Set(v) 243 | if err != nil { 244 | t.Error("Not expected error.") 245 | } 246 | if tm.Int() != expected { 247 | t.Errorf("This value should return %d. value:%d", expected, tm.Int()) 248 | } 249 | } 250 | 251 | func TestTimestampNanoString(t *testing.T) { 252 | v := time.Now() 253 | expected := strconv.FormatInt(v.UnixNano(), 10) 254 | tm := TimestampNano{} 255 | err := tm.Set(v) 256 | if err != nil { 257 | t.Error("Not expected error.") 258 | } 259 | if tm.String() != expected { 260 | t.Errorf("This value should return %s. value:%s", expected, tm.String()) 261 | } 262 | } 263 | 264 | func TestTimestampNano_Time(t *testing.T) { 265 | now := time.Now() 266 | 267 | type fields struct { 268 | ValidFlag ValidFlag 269 | time time.Time 270 | } 271 | tests := []struct { 272 | name string 273 | fields fields 274 | want time.Time 275 | }{ 276 | { 277 | name: "now", 278 | fields: fields{ 279 | ValidFlag: true, 280 | time: now, 281 | }, 282 | want: now, 283 | }, 284 | { 285 | name: "invalid", 286 | fields: fields{ 287 | ValidFlag: false, 288 | time: now, 289 | }, 290 | want: time.Unix(0, 0), 291 | }, 292 | } 293 | for _, tt := range tests { 294 | t.Run(tt.name, func(t *testing.T) { 295 | v := TimestampNano{ 296 | ValidFlag: tt.fields.ValidFlag, 297 | time: tt.fields.time, 298 | } 299 | if got := v.Time(); !reflect.DeepEqual(got, tt.want) { 300 | t.Errorf("TimestampNano.Time() = %v, want %v", got, tt.want) 301 | } 302 | }) 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /type_int_test.go: -------------------------------------------------------------------------------- 1 | package generic 2 | 3 | import ( 4 | "encoding/json" 5 | "reflect" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | type TestIntStruct struct { 12 | Int Int `json:"int"` 13 | Float Int `json:"float"` 14 | Bool Int `json:"bool"` 15 | String Int `json:"string"` 16 | NullValue Int `json:"null_value"` 17 | Empty Int `json:"empty"` 18 | } 19 | 20 | func TestMarshalInt(t *testing.T) { 21 | expected := Int{ 22 | ValidFlag: true, 23 | int: 100, 24 | } 25 | 26 | i := 100 27 | actual, err := MarshalInt(i) 28 | if err != nil { 29 | t.Errorf("Not Expected error when MarshalInt. error:%v", err.Error()) 30 | } 31 | if actual != expected { 32 | t.Errorf("actual:%v, expected:%v", actual, expected) 33 | } 34 | } 35 | 36 | func TestMustInt(t *testing.T) { 37 | tests := []struct { 38 | name string 39 | args interface{} 40 | want Int 41 | wantPanic bool 42 | }{ 43 | { 44 | name: "valid", 45 | args: 123, 46 | want: Int{ 47 | ValidFlag: true, 48 | int: 123, 49 | }, 50 | wantPanic: false, 51 | }, 52 | { 53 | name: "panic", 54 | args: "valid paramenter", 55 | want: Int{ 56 | ValidFlag: false, 57 | }, 58 | wantPanic: true, 59 | }, 60 | } 61 | for _, tt := range tests { 62 | t.Run(tt.name, func(t *testing.T) { 63 | if tt.wantPanic { 64 | p := assert.Panics(t, func() { 65 | MustInt(tt.args) 66 | }) 67 | if !p { 68 | t.Errorf("MustInt() panic = %v, want panic %v", p, tt.wantPanic) 69 | } 70 | return 71 | } 72 | if got := MustInt(tt.args); !reflect.DeepEqual(got, tt.want) { 73 | t.Errorf("MustInt() = %v, want %v", got, tt.want) 74 | } 75 | }) 76 | } 77 | } 78 | 79 | func TestIntJsonUnmarshalAndMarshal(t *testing.T) { 80 | var ts TestIntStruct 81 | jstr := `{"int":10,"float":1.0,"bool":true,"string":"-50","null_value":null}` 82 | expected := `{"int":10,"float":1,"bool":1,"string":-50,"null_value":null,"empty":null}` 83 | err := json.Unmarshal([]byte(jstr), &ts) 84 | if err != nil { 85 | t.Errorf("Not Expected error when json.Unmarshal. error:%v", err.Error()) 86 | } 87 | b, err := json.Marshal(ts) 88 | if err != nil { 89 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 90 | } 91 | actual := string(b) 92 | if actual != expected { 93 | t.Errorf("actual:%s, expected:%s", actual, expected) 94 | } 95 | } 96 | 97 | func TestIntJsonMarshal(t *testing.T) { 98 | ts := Int{ 99 | ValidFlag: true, 100 | int: 1000, 101 | } 102 | expected := `1000` 103 | actual, err := json.Marshal(ts) 104 | if err != nil { 105 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 106 | } 107 | if string(actual) != expected { 108 | t.Errorf("actual:%s, expected:%s", string(actual), expected) 109 | } 110 | } 111 | 112 | func TestIntJsonMarshalValidFalse(t *testing.T) { 113 | ts := Int{ 114 | ValidFlag: false, 115 | int: 1000, 116 | } 117 | expected := []byte("null") 118 | actual, err := json.Marshal(ts) 119 | if err != nil { 120 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 121 | } 122 | if string(actual) != string(expected) { 123 | t.Errorf("actual:%v, expected:%v", actual, expected) 124 | } 125 | } 126 | 127 | func TestIntJsonMarshalZero(t *testing.T) { 128 | ts := Int{ 129 | ValidFlag: true, 130 | int: 0, 131 | } 132 | expected := `0` 133 | actual, err := json.Marshal(ts) 134 | if err != nil { 135 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 136 | } 137 | if string(actual) != expected { 138 | t.Errorf("actual:%s, expected:%s", string(actual), expected) 139 | } 140 | } 141 | 142 | func TestIntJsonUnmarshal(t *testing.T) { 143 | jstr := `{"int":10,"float":1.0,"bool":true,"string":"-50","null_value":null}` 144 | expected := TestIntStruct{ 145 | Int: Int{ 146 | ValidFlag: true, 147 | int: 10, 148 | }, 149 | Float: Int{ 150 | ValidFlag: true, 151 | int: 1, 152 | }, 153 | Bool: Int{ 154 | ValidFlag: true, 155 | int: 1, 156 | }, 157 | String: Int{ 158 | ValidFlag: true, 159 | int: -50, 160 | }, 161 | NullValue: Int{ 162 | ValidFlag: false, 163 | int: 0, 164 | }, 165 | Empty: Int{ 166 | ValidFlag: false, 167 | int: 0, 168 | }, 169 | } 170 | var actual TestIntStruct 171 | err := json.Unmarshal([]byte(jstr), &actual) 172 | if err != nil { 173 | t.Errorf("Not Expected error when json.Marshal. error:%s", err.Error()) 174 | } 175 | if actual != expected { 176 | t.Errorf("actual:%#v, expected:%#v", actual, expected) 177 | } 178 | } 179 | 180 | func TestUnmarshalNil(t *testing.T) { 181 | var actual Int 182 | expected := Int{} 183 | err := actual.UnmarshalJSON(nil) 184 | if err != nil { 185 | t.Errorf("Not Expected error when json.Unmarshal. error:%s", err.Error()) 186 | } 187 | if actual != expected { 188 | t.Errorf("actual:%#v, expected:%#v", actual, expected) 189 | } 190 | } 191 | 192 | func TestUnmarshalInvalidData(t *testing.T) { 193 | var actual Int 194 | err := actual.UnmarshalJSON([]byte(`"1`)) 195 | if err == nil { 196 | t.Error("Expected error when json.Unmarshal") 197 | } 198 | } 199 | 200 | func TestIntJsonError(t *testing.T) { 201 | var ts TestIntStruct 202 | jstr := `{"int":10,"float":1.0,"bool":true,"string":"あ","null_value":null}` 203 | expected := `{"int":10,"float":1,"bool":1,"string":null,"null_value":null,"empty":null}` 204 | err := json.Unmarshal([]byte(jstr), &ts) 205 | if err == nil { 206 | t.Error("Expected error when json.Unmarshal.") 207 | } 208 | b, err := json.Marshal(ts) 209 | if err != nil { 210 | t.Errorf("Not Expected error when json.Marshal. error:%v", err.Error()) 211 | } 212 | actual := string(b) 213 | if actual != expected { 214 | t.Errorf("actual:%s, expected:%s", actual, expected) 215 | } 216 | } 217 | 218 | func TestIntSetNil(t *testing.T) { 219 | ti := Int{} 220 | err := ti.Set(nil) 221 | if err != nil { 222 | t.Errorf("Not Expected error. error:%v", err.Error()) 223 | } 224 | if ti.Weak() != nil { 225 | t.Errorf("This value should return nil. error:%#v", ti.Weak()) 226 | } 227 | } 228 | 229 | func TestIntSetInt64(t *testing.T) { 230 | var v int64 = 100 231 | var expected int64 = 100 232 | ti := Int{} 233 | err := ti.Set(v) 234 | if err != nil { 235 | t.Errorf("Not Expected error. error:%v", err.Error()) 236 | } 237 | if ti.Weak() != expected { 238 | t.Errorf("actual:%v, expected:%v", ti.Weak(), expected) 239 | } 240 | } 241 | 242 | func TestIntSetNumericString(t *testing.T) { 243 | v := "56" 244 | var expected int64 = 56 245 | ti := Int{} 246 | err := ti.Set(v) 247 | if err != nil { 248 | t.Errorf("Not Expected error. error:%v", err.Error()) 249 | } 250 | if ti.Weak() != expected { 251 | t.Errorf("actual:%v, expected:%v", ti.Weak(), expected) 252 | } 253 | } 254 | 255 | func TestIntSetNonNumericString(t *testing.T) { 256 | v := "a" 257 | var expected int64 258 | ti := Int{} 259 | err := ti.Set(v) 260 | if err == nil { 261 | t.Error("Expected error.") 262 | } 263 | if ti.Weak() == expected { 264 | t.Errorf("This value should return 0. value:%#v", ti.Weak()) 265 | } 266 | } 267 | 268 | func TestIntInt(t *testing.T) { 269 | expected := 123456789 270 | ti := Int{ 271 | ValidFlag: true, 272 | int: int64(expected), 273 | } 274 | if ti.Int() != expected { 275 | t.Errorf("actual:%d, expected:%d", ti.Int(), expected) 276 | } 277 | } 278 | 279 | func TestIntIntInvalid(t *testing.T) { 280 | ti := Int{ 281 | ValidFlag: false, 282 | int: 123456789, 283 | } 284 | if ti.Int() != 0 { 285 | t.Errorf("actual:%d, expected:0", ti.Int()) 286 | } 287 | } 288 | 289 | func TestIntInt32(t *testing.T) { 290 | var expected int32 = 123456789 291 | ti := Int{ 292 | ValidFlag: true, 293 | int: int64(expected), 294 | } 295 | if ti.Int32() != expected { 296 | t.Errorf("actual:%d, expected:%d", ti.Int32(), expected) 297 | } 298 | } 299 | 300 | func TestIntInt32Invalid(t *testing.T) { 301 | ti := Int{ 302 | ValidFlag: false, 303 | int: 123456789, 304 | } 305 | if ti.Int32() != 0 { 306 | t.Errorf("actual:%d, expected:0", ti.Int32()) 307 | } 308 | } 309 | 310 | func TestIntInt64(t *testing.T) { 311 | var expected int64 = 123456789 312 | ti := Int{ 313 | ValidFlag: true, 314 | int: expected, 315 | } 316 | if ti.Int64() != expected { 317 | t.Errorf("actual:%d, expected:%d", ti.Int64(), expected) 318 | } 319 | } 320 | 321 | func TestIntInt64Invalid(t *testing.T) { 322 | ti := Int{ 323 | ValidFlag: false, 324 | int: 123456789, 325 | } 326 | if ti.Int64() != 0 { 327 | t.Errorf("actual:%d, expected:0", ti.Int64()) 328 | } 329 | } 330 | 331 | func TestIntString(t *testing.T) { 332 | var expected = "123456789" 333 | ti := Int{} 334 | ti.Set(expected) // nolint 335 | if ti.String() != expected { 336 | t.Errorf("actual:%s, expected:%s", ti.String(), expected) 337 | } 338 | } 339 | 340 | func TestIntStringInvalid(t *testing.T) { 341 | ti := Int{ 342 | ValidFlag: false, 343 | int: 123456789, 344 | } 345 | if ti.String() != "" { 346 | t.Errorf("expected empty string, actual:%s", ti.String()) 347 | } 348 | } 349 | -------------------------------------------------------------------------------- /type_url_old_version_test.go: -------------------------------------------------------------------------------- 1 | // +build !go1.15 2 | 3 | package generic 4 | 5 | import ( 6 | "net/url" 7 | "reflect" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | type testURLResult struct { 14 | in string 15 | uri URL 16 | hostname string 17 | abs bool 18 | port string 19 | queries url.Values 20 | requestURI string 21 | } 22 | 23 | const ( 24 | testURLString = "https://foobar.test:8080?q=fizzbizz" 25 | rootPath = "/" 26 | ) 27 | 28 | var ( 29 | urltests = []testURLResult{ 30 | // no path 31 | { 32 | in: "http://www.google.com", 33 | uri: URL{ 34 | ValidFlag: true, 35 | url: &url.URL{ 36 | Scheme: "http", 37 | Host: "www.google.com", 38 | }, 39 | }, 40 | hostname: "www.google.com", 41 | abs: true, 42 | port: "", 43 | queries: url.Values{}, 44 | requestURI: rootPath, 45 | }, 46 | // path 47 | { 48 | in: "http://www.google.com/", 49 | uri: URL{ 50 | ValidFlag: true, 51 | url: &url.URL{ 52 | Scheme: "http", 53 | Host: "www.google.com", 54 | Path: rootPath, 55 | }, 56 | }, 57 | hostname: "www.google.com", 58 | abs: true, 59 | port: "", 60 | queries: url.Values{}, 61 | requestURI: rootPath, 62 | }, 63 | // path with hex escaping 64 | { 65 | in: "http://www.google.com/file%20one%26two", 66 | uri: URL{ 67 | ValidFlag: true, 68 | url: &url.URL{ 69 | Scheme: "http", 70 | Host: "www.google.com", 71 | Path: "/file one&two", 72 | RawPath: "/file%20one%26two", 73 | }, 74 | }, 75 | hostname: "www.google.com", 76 | abs: true, 77 | port: "", 78 | queries: url.Values{}, 79 | requestURI: "/file%20one%26two", 80 | }, 81 | // user 82 | { 83 | in: "ftp://webmaster@www.google.com/", 84 | uri: URL{ 85 | ValidFlag: true, 86 | url: &url.URL{ 87 | Scheme: "ftp", 88 | User: url.User("webmaster"), 89 | Host: "www.google.com", 90 | Path: rootPath, 91 | }, 92 | }, 93 | hostname: "www.google.com", 94 | abs: true, 95 | port: "", 96 | queries: url.Values{}, 97 | requestURI: rootPath, 98 | }, 99 | // escape sequence in username 100 | { 101 | in: "ftp://john%20doe@www.google.com/", 102 | uri: URL{ 103 | ValidFlag: true, 104 | url: &url.URL{ 105 | Scheme: "ftp", 106 | User: url.User("john doe"), 107 | Host: "www.google.com", 108 | Path: rootPath, 109 | }, 110 | }, 111 | hostname: "www.google.com", 112 | abs: true, 113 | port: "", 114 | queries: url.Values{}, 115 | requestURI: rootPath, 116 | }, 117 | // empty query 118 | { 119 | in: "http://www.google.com/?", 120 | uri: URL{ 121 | ValidFlag: true, 122 | url: &url.URL{ 123 | Scheme: "http", 124 | Host: "www.google.com", 125 | Path: "/", 126 | ForceQuery: true, 127 | }, 128 | }, 129 | hostname: "www.google.com", 130 | abs: true, 131 | port: "", 132 | queries: url.Values{}, 133 | requestURI: "/?", 134 | }, 135 | // query ending in question mark 136 | // golang.org/issue/14573 137 | { 138 | in: "http://www.google.com/?foo=bar?", 139 | uri: URL{ 140 | ValidFlag: true, 141 | url: &url.URL{ 142 | Scheme: "http", 143 | Host: "www.google.com", 144 | Path: "/", 145 | RawQuery: "foo=bar?", 146 | }, 147 | }, 148 | hostname: "www.google.com", 149 | abs: true, 150 | port: "", 151 | queries: url.Values{ 152 | "foo": []string{"bar?"}, 153 | }, 154 | requestURI: "/?foo=bar?", 155 | }, 156 | // query 157 | { 158 | in: "http://www.google.com/?q=go+language", 159 | uri: URL{ 160 | ValidFlag: true, 161 | url: &url.URL{ 162 | Scheme: "http", 163 | Host: "www.google.com", 164 | Path: "/", 165 | RawQuery: "q=go+language", 166 | }, 167 | }, 168 | hostname: "www.google.com", 169 | abs: true, 170 | port: "", 171 | queries: url.Values{ 172 | "q": []string{"go language"}, 173 | }, 174 | requestURI: "/?q=go+language", 175 | }, 176 | // query with hex escaping: NOT parsed 177 | { 178 | in: "http://www.google.com/?q=go%20language", 179 | uri: URL{ 180 | ValidFlag: true, 181 | url: &url.URL{ 182 | Scheme: "http", 183 | Host: "www.google.com", 184 | Path: "/", 185 | RawQuery: "q=go%20language", 186 | }, 187 | }, 188 | hostname: "www.google.com", 189 | abs: true, 190 | port: "", 191 | queries: url.Values{ 192 | "q": []string{"go language"}, 193 | }, 194 | requestURI: "/?q=go%20language", 195 | }, 196 | // %20 outside query 197 | { 198 | in: "http://www.google.com/a%20b?q=c+d", 199 | uri: URL{ 200 | ValidFlag: true, 201 | url: &url.URL{ 202 | Scheme: "http", 203 | Host: "www.google.com", 204 | Path: "/a b", 205 | RawQuery: "q=c+d", 206 | }, 207 | }, 208 | hostname: "www.google.com", 209 | abs: true, 210 | port: "", 211 | queries: url.Values{ 212 | "q": []string{"c d"}, 213 | }, 214 | requestURI: "/a%20b?q=c+d", 215 | }, 216 | // path without leading /, so no parsing 217 | { 218 | in: "http:www.google.com/?q=go+language", 219 | uri: URL{ 220 | ValidFlag: true, 221 | url: &url.URL{ 222 | Scheme: "http", 223 | Opaque: "www.google.com/", 224 | RawQuery: "q=go+language", 225 | }, 226 | }, 227 | hostname: "", 228 | abs: true, 229 | port: "", 230 | queries: url.Values{ 231 | "q": []string{"go language"}, 232 | }, 233 | requestURI: "www.google.com/?q=go+language", 234 | }, 235 | // path without leading /, so no parsing 236 | { 237 | in: "http:%2f%2fwww.google.com/?q=go+language", 238 | uri: URL{ 239 | ValidFlag: true, 240 | url: &url.URL{ 241 | Scheme: "http", 242 | Opaque: "%2f%2fwww.google.com/", 243 | RawQuery: "q=go+language", 244 | }, 245 | }, 246 | hostname: "", 247 | abs: true, 248 | port: "", 249 | queries: url.Values{ 250 | "q": []string{"go language"}, 251 | }, 252 | requestURI: "%2f%2fwww.google.com/?q=go+language", 253 | }, 254 | // non-authority with path 255 | { 256 | in: "mailto:/webmaster@golang.org", 257 | uri: URL{ 258 | ValidFlag: true, 259 | url: &url.URL{ 260 | Scheme: "mailto", 261 | Path: "/webmaster@golang.org", 262 | }, 263 | }, 264 | hostname: "", 265 | abs: true, 266 | port: "", 267 | queries: url.Values{}, 268 | requestURI: "/webmaster@golang.org", 269 | }, 270 | // non-authority 271 | { 272 | in: "mailto:webmaster@golang.org", 273 | uri: URL{ 274 | ValidFlag: true, 275 | url: &url.URL{ 276 | Scheme: "mailto", 277 | Opaque: "webmaster@golang.org", 278 | }, 279 | }, 280 | hostname: "", 281 | abs: true, 282 | port: "", 283 | queries: url.Values{}, 284 | requestURI: "webmaster@golang.org", 285 | }, 286 | // unescaped :// in query should not create a scheme 287 | { 288 | in: "/foo?query=http://bad", 289 | uri: URL{ 290 | ValidFlag: true, 291 | url: &url.URL{ 292 | Path: "/foo", 293 | RawQuery: "query=http://bad", 294 | }, 295 | }, 296 | hostname: "", 297 | abs: false, 298 | port: "", 299 | queries: url.Values{ 300 | "query": []string{"http://bad"}, 301 | }, 302 | requestURI: "/foo?query=http://bad", 303 | }, 304 | // leading // without scheme should create an authority 305 | { 306 | in: "//foo", 307 | uri: URL{ 308 | ValidFlag: true, 309 | url: &url.URL{ 310 | Host: "foo", 311 | }, 312 | }, 313 | hostname: "foo", 314 | abs: false, 315 | port: "", 316 | queries: url.Values{}, 317 | requestURI: rootPath, 318 | }, 319 | // leading // without scheme, with userinfo, path, and query 320 | { 321 | in: "//user@foo/path?a=b", 322 | uri: URL{ 323 | ValidFlag: true, 324 | url: &url.URL{ 325 | User: url.User("user"), 326 | Host: "foo", 327 | Path: "/path", 328 | RawQuery: "a=b", 329 | }, 330 | }, 331 | hostname: "foo", 332 | abs: false, 333 | port: "", 334 | queries: url.Values{ 335 | "a": []string{"b"}, 336 | }, 337 | requestURI: "/path?a=b", 338 | }, 339 | // Three leading slashes isn't an authority, but doesn't return an error. 340 | // (We can't return an error, as this code is also used via 341 | // ServeHTTP -> ReadRequest -> Parse, which is arguably a 342 | // different URL parsing context, but currently shares the 343 | // same codepath) 344 | { 345 | in: "///threeslashes", 346 | uri: URL{ 347 | ValidFlag: true, 348 | url: &url.URL{ 349 | Path: "///threeslashes", 350 | }, 351 | }, 352 | hostname: "", 353 | abs: false, 354 | port: "", 355 | queries: url.Values{}, 356 | requestURI: "///threeslashes", 357 | }, 358 | { 359 | in: "http://user:password@google.com", 360 | uri: URL{ 361 | ValidFlag: true, 362 | url: &url.URL{ 363 | Scheme: "http", 364 | User: url.UserPassword("user", "password"), 365 | Host: "google.com", 366 | }, 367 | }, 368 | hostname: "google.com", 369 | abs: true, 370 | port: "", 371 | queries: url.Values{}, 372 | requestURI: rootPath, 373 | }, 374 | // unescaped @ in username should not confuse host 375 | { 376 | in: "http://j@ne:password@google.com", 377 | uri: URL{ 378 | ValidFlag: true, 379 | url: &url.URL{ 380 | Scheme: "http", 381 | User: url.UserPassword("j@ne", "password"), 382 | Host: "google.com", 383 | }, 384 | }, 385 | hostname: "google.com", 386 | abs: true, 387 | port: "", 388 | queries: url.Values{}, 389 | requestURI: rootPath, 390 | }, 391 | // unescaped @ in password should not confuse host 392 | { 393 | in: "http://jane:p@ssword@google.com", 394 | uri: URL{ 395 | ValidFlag: true, 396 | url: &url.URL{ 397 | Scheme: "http", 398 | User: url.UserPassword("jane", "p@ssword"), 399 | Host: "google.com", 400 | }, 401 | }, 402 | hostname: "google.com", 403 | abs: true, 404 | port: "", 405 | queries: url.Values{}, 406 | requestURI: rootPath, 407 | }, 408 | { 409 | in: "http://j@ne:password@google.com/p@th?q=@go", 410 | uri: URL{ 411 | ValidFlag: true, 412 | url: &url.URL{ 413 | Scheme: "http", 414 | User: url.UserPassword("j@ne", "password"), 415 | Host: "google.com", 416 | Path: "/p@th", 417 | RawQuery: "q=@go", 418 | }, 419 | }, 420 | hostname: "google.com", 421 | abs: true, 422 | port: "", 423 | queries: url.Values{ 424 | "q": []string{"@go"}, 425 | }, 426 | requestURI: "/p@th?q=@go", 427 | }, 428 | { 429 | in: "http://www.google.com/?q=go+language#foo", 430 | uri: URL{ 431 | ValidFlag: true, 432 | url: &url.URL{ 433 | Scheme: "http", 434 | Host: "www.google.com", 435 | Path: "/", 436 | RawQuery: "q=go+language", 437 | Fragment: "foo", 438 | }, 439 | }, 440 | hostname: "www.google.com", 441 | abs: true, 442 | port: "", 443 | queries: url.Values{ 444 | "q": []string{"go language"}, 445 | }, 446 | requestURI: "/?q=go+language", 447 | }, 448 | { 449 | in: "http://www.google.com/?q=go+language#foo%26bar", 450 | uri: URL{ 451 | ValidFlag: true, 452 | url: &url.URL{ 453 | Scheme: "http", 454 | Host: "www.google.com", 455 | Path: "/", 456 | RawQuery: "q=go+language", 457 | Fragment: "foo&bar", 458 | }, 459 | }, 460 | hostname: "www.google.com", 461 | abs: true, 462 | port: "", 463 | queries: url.Values{ 464 | "q": []string{"go language"}, 465 | }, 466 | requestURI: "/?q=go+language", 467 | }, 468 | { 469 | in: "file:///home/adg/rabbits", 470 | uri: URL{ 471 | ValidFlag: true, 472 | url: &url.URL{ 473 | Scheme: "file", 474 | Host: "", 475 | Path: "/home/adg/rabbits", 476 | }, 477 | }, 478 | hostname: "", 479 | abs: true, 480 | port: "", 481 | queries: url.Values{}, 482 | requestURI: "/home/adg/rabbits", 483 | }, 484 | // "Windows" paths are no exception to the rule. 485 | // See golang.org/issue/6027, especially comment #9. 486 | { 487 | in: "file:///C:/FooBar/Baz.txt", 488 | uri: URL{ 489 | ValidFlag: true, 490 | url: &url.URL{ 491 | Scheme: "file", 492 | Host: "", 493 | Path: "/C:/FooBar/Baz.txt", 494 | }, 495 | }, 496 | hostname: "", 497 | abs: true, 498 | port: "", 499 | queries: url.Values{}, 500 | requestURI: "/C:/FooBar/Baz.txt", 501 | }, 502 | // case-insensitive scheme 503 | { 504 | in: "MaIlTo:webmaster@golang.org", 505 | uri: URL{ 506 | ValidFlag: true, 507 | url: &url.URL{ 508 | Scheme: "mailto", 509 | Opaque: "webmaster@golang.org", 510 | }, 511 | }, 512 | hostname: "", 513 | abs: true, 514 | port: "", 515 | queries: url.Values{}, 516 | requestURI: "webmaster@golang.org", 517 | }, 518 | // Relative path 519 | { 520 | in: "a/b/c", 521 | uri: URL{ 522 | ValidFlag: true, 523 | url: &url.URL{ 524 | Path: "/a/b/c", 525 | }, 526 | }, 527 | hostname: "", 528 | abs: false, 529 | port: "", 530 | queries: url.Values{}, 531 | requestURI: "/a/b/c", 532 | }, 533 | // escaped '?' in username and password 534 | { 535 | in: "http://%3Fam:pa%3Fsword@google.com", 536 | uri: URL{ 537 | ValidFlag: true, 538 | url: &url.URL{ 539 | Scheme: "http", 540 | User: url.UserPassword("?am", "pa?sword"), 541 | Host: "google.com", 542 | }, 543 | }, 544 | hostname: "google.com", 545 | abs: true, 546 | port: "", 547 | queries: url.Values{}, 548 | requestURI: rootPath, 549 | }, 550 | // host subcomponent; IPv4 address in RFC 3986 551 | { 552 | in: "http://192.168.0.1/", 553 | uri: URL{ 554 | ValidFlag: true, 555 | url: &url.URL{ 556 | Scheme: "http", 557 | Host: "192.168.0.1", 558 | Path: "/", 559 | }, 560 | }, 561 | hostname: "192.168.0.1", 562 | abs: true, 563 | port: "", 564 | queries: url.Values{}, 565 | requestURI: rootPath, 566 | }, 567 | // host and port subcomponents; IPv4 address in RFC 3986 568 | { 569 | in: "http://192.168.0.1:8080/", 570 | uri: URL{ 571 | ValidFlag: true, 572 | url: &url.URL{ 573 | Scheme: "http", 574 | Host: "192.168.0.1:8080", 575 | Path: "/", 576 | }, 577 | }, 578 | hostname: "192.168.0.1", 579 | abs: true, 580 | port: "8080", 581 | queries: url.Values{}, 582 | requestURI: rootPath, 583 | }, 584 | // host subcomponent; IPv6 address in RFC 3986 585 | { 586 | in: "http://[fe80::1]/", 587 | uri: URL{ 588 | ValidFlag: true, 589 | url: &url.URL{ 590 | Scheme: "http", 591 | Host: "[fe80::1]", 592 | Path: "/", 593 | }, 594 | }, 595 | hostname: "fe80::1", 596 | abs: true, 597 | port: "", 598 | queries: url.Values{}, 599 | requestURI: rootPath, 600 | }, 601 | // host and port subcomponents; IPv6 address in RFC 3986 602 | { 603 | in: "http://[fe80::1]:8080/", 604 | uri: URL{ 605 | ValidFlag: true, 606 | url: &url.URL{ 607 | Scheme: "http", 608 | Host: "[fe80::1]:8080", 609 | Path: "/", 610 | }, 611 | }, 612 | hostname: "fe80::1", 613 | abs: true, 614 | port: "8080", 615 | queries: url.Values{}, 616 | requestURI: rootPath, 617 | }, 618 | // host subcomponent; IPv6 address with zone identifier in RFC 6874 619 | { 620 | in: "http://[fe80::1%25en0]/", // alphanum zone identifier 621 | uri: URL{ 622 | ValidFlag: true, 623 | url: &url.URL{ 624 | Scheme: "http", 625 | Host: "[fe80::1%en0]", 626 | Path: "/", 627 | }, 628 | }, 629 | hostname: "fe80::1%en0", 630 | abs: true, 631 | port: "", 632 | queries: url.Values{}, 633 | requestURI: rootPath, 634 | }, 635 | // host and port subcomponents; IPv6 address with zone identifier in RFC 6874 636 | { 637 | in: "http://[fe80::1%25en0]:8080/", // alphanum zone identifier 638 | uri: URL{ 639 | ValidFlag: true, 640 | url: &url.URL{ 641 | Scheme: "http", 642 | Host: "[fe80::1%en0]:8080", 643 | Path: "/", 644 | }, 645 | }, 646 | hostname: "fe80::1%en0", 647 | abs: true, 648 | port: "8080", 649 | queries: url.Values{}, 650 | requestURI: rootPath, 651 | }, 652 | // host subcomponent; IPv6 address with zone identifier in RFC 6874 653 | { 654 | in: "http://[fe80::1%25%65%6e%301-._~]/", // percent-encoded+unreserved zone identifier 655 | uri: URL{ 656 | ValidFlag: true, 657 | url: &url.URL{ 658 | Scheme: "http", 659 | Host: "[fe80::1%en01-._~]", 660 | Path: "/", 661 | }, 662 | }, 663 | hostname: "fe80::1%en01-._~", 664 | abs: true, 665 | port: "", 666 | queries: url.Values{}, 667 | requestURI: rootPath, 668 | }, 669 | // host and port subcomponents; IPv6 address with zone identifier in RFC 6874 670 | { 671 | in: "http://[fe80::1%25%65%6e%301-._~]:8080/", // percent-encoded+unreserved zone identifier 672 | uri: URL{ 673 | ValidFlag: true, 674 | url: &url.URL{ 675 | Scheme: "http", 676 | Host: "[fe80::1%en01-._~]:8080", 677 | Path: "/", 678 | }, 679 | }, 680 | hostname: "fe80::1%en01-._~", 681 | abs: true, 682 | port: "8080", 683 | queries: url.Values{}, 684 | requestURI: rootPath, 685 | }, 686 | // alternate escapings of path survive round trip 687 | { 688 | in: "http://rest.rsc.io/foo%2fbar/baz%2Fquux?alt=media", 689 | uri: URL{ 690 | ValidFlag: true, 691 | url: &url.URL{ 692 | Scheme: "http", 693 | Host: "rest.rsc.io", 694 | Path: "/foo/bar/baz/quux", 695 | RawPath: "/foo%2fbar/baz%2Fquux", 696 | RawQuery: "alt=media", 697 | }, 698 | }, 699 | hostname: "rest.rsc.io", 700 | abs: true, 701 | port: "", 702 | queries: url.Values{ 703 | "alt": []string{"media"}, 704 | }, 705 | requestURI: "/foo%2fbar/baz%2Fquux?alt=media", 706 | }, 707 | // issue 12036 708 | { 709 | in: "mysql://a,b,c/bar", 710 | uri: URL{ 711 | ValidFlag: true, 712 | url: &url.URL{ 713 | Scheme: "mysql", 714 | Host: "a,b,c", 715 | Path: "/bar", 716 | }, 717 | }, 718 | hostname: "a,b,c", 719 | abs: true, 720 | port: "", 721 | queries: url.Values{}, 722 | requestURI: "/bar", 723 | }, 724 | // worst case host, still round trips 725 | { 726 | in: "scheme://!$&'()*+,;=hello!:1/path", 727 | uri: URL{ 728 | ValidFlag: true, 729 | url: &url.URL{ 730 | Scheme: "scheme", 731 | Host: "!$&'()*+,;=hello!:1", 732 | Path: "/path", 733 | }, 734 | }, 735 | hostname: "!$&'()*+,;=hello!", 736 | abs: true, 737 | port: "1", 738 | queries: url.Values{}, 739 | requestURI: "/path", 740 | }, 741 | // worst case path, still round trips 742 | { 743 | in: "http://host/!$&'()*+,;=:@[hello]", 744 | uri: URL{ 745 | ValidFlag: true, 746 | url: &url.URL{ 747 | Scheme: "http", 748 | Host: "host", 749 | Path: "/!$&'()*+,;=:@[hello]", 750 | RawPath: "/!$&'()*+,;=:@[hello]", 751 | }, 752 | }, 753 | hostname: "host", 754 | abs: true, 755 | port: "", 756 | queries: url.Values{}, 757 | requestURI: "/!$&'()*+,;=:@[hello]", 758 | }, 759 | // golang.org/issue/5684 760 | { 761 | in: "http://example.com/oid/[order_id]", 762 | uri: URL{ 763 | ValidFlag: true, 764 | url: &url.URL{ 765 | Scheme: "http", 766 | Host: "example.com", 767 | Path: "/oid/[order_id]", 768 | RawPath: "/oid/[order_id]", 769 | }, 770 | }, 771 | hostname: "example.com", 772 | abs: true, 773 | port: "", 774 | queries: url.Values{}, 775 | requestURI: "/oid/[order_id]", 776 | }, 777 | // golang.org/issue/12200 (colon with empty port) 778 | { 779 | in: "http://192.168.0.2:8080/foo", 780 | uri: URL{ 781 | ValidFlag: true, 782 | url: &url.URL{ 783 | Scheme: "http", 784 | Host: "192.168.0.2:8080", 785 | Path: "/foo", 786 | }, 787 | }, 788 | hostname: "192.168.0.2", 789 | abs: true, 790 | port: "8080", 791 | queries: url.Values{}, 792 | requestURI: "/foo", 793 | }, 794 | { 795 | in: "http://192.168.0.2:/foo", 796 | uri: URL{ 797 | ValidFlag: true, 798 | url: &url.URL{ 799 | Scheme: "http", 800 | Host: "192.168.0.2:", 801 | Path: "/foo", 802 | }, 803 | }, 804 | hostname: "192.168.0.2", 805 | abs: true, 806 | port: "", 807 | queries: url.Values{}, 808 | requestURI: "/foo", 809 | }, 810 | // Malformed IPv6 but still accepted. 811 | { 812 | in: "http://2b01:e34:ef40:7730:8e70:5aff:fefe:edac:8080/foo", 813 | uri: URL{ 814 | ValidFlag: true, 815 | url: &url.URL{ 816 | Scheme: "http", 817 | Host: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac:8080", 818 | Path: "/foo", 819 | }, 820 | }, 821 | hostname: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac", 822 | abs: true, 823 | port: "8080", 824 | queries: url.Values{}, 825 | requestURI: "/foo", 826 | }, 827 | // Malformed IPv6 but still accepted. 828 | { 829 | in: "http://2b01:e34:ef40:7730:8e70:5aff:fefe:edac:/foo", 830 | uri: URL{ 831 | ValidFlag: true, 832 | url: &url.URL{ 833 | Scheme: "http", 834 | Host: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac:", 835 | Path: "/foo", 836 | }, 837 | }, 838 | hostname: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac", 839 | abs: true, 840 | port: "", 841 | queries: url.Values{}, 842 | requestURI: "/foo", 843 | }, 844 | { 845 | in: "http://[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:8080/foo", 846 | uri: URL{ 847 | ValidFlag: true, 848 | url: &url.URL{ 849 | Scheme: "http", 850 | Host: "[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:8080", 851 | Path: "/foo", 852 | }, 853 | }, 854 | hostname: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac", 855 | abs: true, 856 | port: "8080", 857 | queries: url.Values{}, 858 | requestURI: "/foo", 859 | }, 860 | { 861 | in: "http://[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:/foo", 862 | uri: URL{ 863 | ValidFlag: true, 864 | url: &url.URL{ 865 | Scheme: "http", 866 | Host: "[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:", 867 | Path: "/foo", 868 | }, 869 | }, 870 | hostname: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac", 871 | abs: true, 872 | port: "", 873 | queries: url.Values{}, 874 | requestURI: "/foo", 875 | }, 876 | // golang.org/issue/7991 and golang.org/issue/12719 (non-ascii %-encoded in host) 877 | { 878 | in: "http://hello.世界.com/foo", 879 | uri: URL{ 880 | ValidFlag: true, 881 | url: &url.URL{ 882 | Scheme: "http", 883 | Host: "hello.世界.com", 884 | Path: "/foo", 885 | }, 886 | }, 887 | hostname: "hello.世界.com", 888 | abs: true, 889 | port: "", 890 | queries: url.Values{}, 891 | requestURI: "/foo", 892 | }, 893 | { 894 | in: "http://hello.%e4%b8%96%e7%95%8c.com/foo", 895 | uri: URL{ 896 | ValidFlag: true, 897 | url: &url.URL{ 898 | Scheme: "http", 899 | Host: "hello.世界.com", 900 | Path: "/foo", 901 | }, 902 | }, 903 | hostname: "hello.世界.com", 904 | abs: true, 905 | port: "", 906 | queries: url.Values{}, 907 | requestURI: "/foo", 908 | }, 909 | { 910 | in: "http://hello.%E4%B8%96%E7%95%8C.com/foo", 911 | uri: URL{ 912 | ValidFlag: true, 913 | url: &url.URL{ 914 | Scheme: "http", 915 | Host: "hello.世界.com", 916 | Path: "/foo", 917 | }, 918 | }, 919 | hostname: "hello.世界.com", 920 | abs: true, 921 | port: "", 922 | queries: url.Values{}, 923 | requestURI: "/foo", 924 | }, 925 | // golang.org/issue/10433 (path beginning with //) 926 | { 927 | in: "http://example.com//foo", 928 | uri: URL{ 929 | ValidFlag: true, 930 | url: &url.URL{ 931 | Scheme: "http", 932 | Host: "example.com", 933 | Path: "//foo", 934 | }, 935 | }, 936 | hostname: "example.com", 937 | abs: true, 938 | port: "", 939 | queries: url.Values{}, 940 | requestURI: "//foo", 941 | }, 942 | // test that we can reparse the host names we accept. 943 | { 944 | in: "myscheme://authority<\"hi\">/foo", 945 | uri: URL{ 946 | ValidFlag: true, 947 | url: &url.URL{ 948 | Scheme: "myscheme", 949 | Host: "authority<\"hi\">", 950 | Path: "/foo", 951 | }, 952 | }, 953 | hostname: "authority<\"hi\">", 954 | abs: true, 955 | port: "", 956 | queries: url.Values{}, 957 | requestURI: "/foo", 958 | }, 959 | // spaces in hosts are disallowed but escaped spaces in IPv6 scope IDs are grudgingly OK. 960 | // This happens on Windows. 961 | // golang.org/issue/14002 962 | { 963 | in: "tcp://[2020::2020:20:2020:2020%25Windows%20Loves%20Spaces]:2020", 964 | uri: URL{ 965 | ValidFlag: true, 966 | url: &url.URL{ 967 | Scheme: "tcp", 968 | Host: "[2020::2020:20:2020:2020%Windows Loves Spaces]:2020", 969 | }, 970 | }, 971 | hostname: "2020::2020:20:2020:2020%Windows Loves Spaces", 972 | abs: true, 973 | port: "2020", 974 | queries: url.Values{}, 975 | requestURI: rootPath, 976 | }, 977 | { 978 | in: "magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn", 979 | uri: URL{ 980 | ValidFlag: true, 981 | url: &url.URL{ 982 | Scheme: "magnet", 983 | Host: "", 984 | Path: "", 985 | RawQuery: "xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn", 986 | }, 987 | }, 988 | hostname: "", 989 | abs: true, 990 | port: "", 991 | queries: url.Values{ 992 | "xt": []string{"urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"}, 993 | "dn": []string{""}, 994 | }, 995 | requestURI: "/?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn", 996 | }, 997 | { 998 | in: "mailto:?subject=hi", 999 | uri: URL{ 1000 | ValidFlag: true, 1001 | url: &url.URL{ 1002 | Scheme: "mailto", 1003 | Host: "", 1004 | Path: "", 1005 | RawQuery: "subject=hi", 1006 | }, 1007 | }, 1008 | hostname: "", 1009 | abs: true, 1010 | port: "", 1011 | queries: url.Values{ 1012 | "subject": []string{"hi"}, 1013 | }, 1014 | requestURI: "/?subject=hi", 1015 | }, 1016 | } 1017 | 1018 | // { 1019 | // in: "validFlag false", 1020 | // uri: URL{ 1021 | // ValidFlag: false, 1022 | // url: &url.URL{ 1023 | // Scheme: "http", 1024 | // Host: "www.google.com", 1025 | // Path: "/", 1026 | // RawQuery: "q=go+language", 1027 | // Fragment: "foo", 1028 | // }, 1029 | // }, 1030 | // hostname: "", 1031 | // abs: false, 1032 | // port: "", 1033 | // queries: url.Values{}, 1034 | // requestURI: "", 1035 | // }, 1036 | // { 1037 | // in: "nil", 1038 | // uri: URL{ 1039 | // ValidFlag: true, 1040 | // url: nil, 1041 | // }, 1042 | // hostname: "", 1043 | // abs: false, 1044 | // port: "", 1045 | // queries: url.Values{}, 1046 | // requestURI: "", 1047 | // }, 1048 | ) 1049 | 1050 | func TestMarshalURL(t *testing.T) { 1051 | v, _ := url.Parse(testURLString) 1052 | 1053 | expected := URL{ 1054 | ValidFlag: true, 1055 | url: v, 1056 | } 1057 | 1058 | tests := []struct { 1059 | name string 1060 | args interface{} 1061 | want URL 1062 | wantErr bool 1063 | }{ 1064 | { 1065 | name: "URL", 1066 | args: v, 1067 | want: expected, 1068 | wantErr: false, 1069 | }, 1070 | { 1071 | name: "string", 1072 | args: testURLString, 1073 | want: expected, 1074 | wantErr: false, 1075 | }, 1076 | { 1077 | name: "invalid", 1078 | args: 10000, 1079 | want: URL{}, 1080 | wantErr: true, 1081 | }, 1082 | } 1083 | for _, tt := range tests { 1084 | t.Run(tt.name, func(t *testing.T) { 1085 | got, err := MarshalURL(tt.args) 1086 | if (err != nil) != tt.wantErr { 1087 | t.Errorf("MarshalURL() error = %v, wantErr %v", err, tt.wantErr) 1088 | return 1089 | } 1090 | if !reflect.DeepEqual(got, tt.want) { 1091 | t.Errorf("MarshalURL() = %v, want %v", got, tt.want) 1092 | } 1093 | }) 1094 | } 1095 | } 1096 | 1097 | func TestMustURL(t *testing.T) { 1098 | v, _ := url.Parse(testURLString) 1099 | 1100 | expected := URL{ 1101 | ValidFlag: true, 1102 | url: v, 1103 | } 1104 | 1105 | tests := []struct { 1106 | name string 1107 | args interface{} 1108 | want URL 1109 | wantPanic bool 1110 | }{ 1111 | { 1112 | name: "valid", 1113 | args: testURLString, 1114 | want: expected, 1115 | wantPanic: false, 1116 | }, 1117 | { 1118 | name: "valid", 1119 | args: struct{}{}, 1120 | want: URL{ 1121 | ValidFlag: false, 1122 | }, 1123 | wantPanic: true, 1124 | }, 1125 | } 1126 | for _, tt := range tests { 1127 | t.Run(tt.name, func(t *testing.T) { 1128 | if tt.wantPanic { 1129 | p := assert.Panics(t, func() { 1130 | MustURL(tt.args) 1131 | }) 1132 | if !p { 1133 | t.Errorf("MustURL() panic = %v, want panic %v", p, tt.wantPanic) 1134 | } 1135 | return 1136 | } 1137 | if got := MustURL(tt.args); !reflect.DeepEqual(got, tt.want) { 1138 | t.Errorf("MustURL() = %v, want %v", got, tt.want) 1139 | } 1140 | }) 1141 | } 1142 | } 1143 | 1144 | func TestURL_Value(t *testing.T) { 1145 | t.Skip() 1146 | } 1147 | 1148 | func TestURL_Scan(t *testing.T) { 1149 | t.Skip() 1150 | } 1151 | 1152 | func TestURL_Weak(t *testing.T) { 1153 | for _, tt := range urltests { 1154 | t.Run(tt.in, func(t *testing.T) { 1155 | if got := tt.uri.Weak(); got != tt.uri.url { 1156 | t.Errorf("URL.URL() = %v, want %v", got, tt.uri.url) 1157 | } 1158 | }) 1159 | } 1160 | } 1161 | 1162 | // func TestURL_Set(t *testing.T) { 1163 | // t.Skip() 1164 | // } 1165 | 1166 | func TestURL_String(t *testing.T) { 1167 | u1, _ := url.Parse(testURLString) 1168 | 1169 | s := "/foobar?fizz=bizz" 1170 | u2, _ := url.Parse(s) 1171 | 1172 | tests := []struct { 1173 | name string 1174 | fields URL 1175 | want string 1176 | }{ 1177 | { 1178 | name: "valid url", 1179 | fields: URL{ 1180 | ValidFlag: true, 1181 | url: u1, 1182 | }, 1183 | want: testURLString, 1184 | }, 1185 | { 1186 | name: "ValidFlag false", 1187 | fields: URL{ 1188 | ValidFlag: false, 1189 | url: u1, 1190 | }, 1191 | want: "", 1192 | }, 1193 | { 1194 | name: "only path", 1195 | fields: URL{ 1196 | ValidFlag: true, 1197 | url: u2, 1198 | }, 1199 | want: s, 1200 | }, 1201 | } 1202 | for _, tt := range tests { 1203 | t.Run(tt.name, func(t *testing.T) { 1204 | if got := tt.fields.String(); got != tt.want { 1205 | t.Errorf("URL.String() = %v, want %v", got, tt.want) 1206 | } 1207 | }) 1208 | } 1209 | } 1210 | 1211 | func TestURL_URL(t *testing.T) { 1212 | for _, tt := range urltests { 1213 | t.Run(tt.in, func(t *testing.T) { 1214 | if got := tt.uri.URL(); got != tt.uri.url { 1215 | t.Errorf("URL.URL() = %v, want %v", got, tt.uri.url) 1216 | } 1217 | }) 1218 | } 1219 | } 1220 | 1221 | func TestURL_MarshalJSON_UnmarshalJSON(t *testing.T) { 1222 | for _, tt := range urltests { 1223 | t.Run(tt.in, func(t *testing.T) { 1224 | bs, err := tt.uri.MarshalJSON() 1225 | if err != nil { 1226 | t.Errorf("URL.MarshalJSON() error = %v, not expected", err) 1227 | return 1228 | } 1229 | v := URL{} 1230 | err = v.UnmarshalJSON(bs) 1231 | if err != nil { 1232 | t.Errorf("URL.UnmarshalJSON() error = %v, not expected", err) 1233 | return 1234 | } 1235 | if v.String() != tt.uri.String() { 1236 | t.Errorf("URL.UnmarshalJSON() = %v, want %v", v, tt.uri) 1237 | } 1238 | }) 1239 | } 1240 | } 1241 | 1242 | func TestURL_UnmarshalJSON(t *testing.T) { 1243 | t.Skip() 1244 | } 1245 | 1246 | func TestURL_EscapedPath(t *testing.T) { 1247 | t.Skip() 1248 | } 1249 | 1250 | func TestURL_Hostname(t *testing.T) { 1251 | for _, tt := range urltests { 1252 | t.Run(tt.in, func(t *testing.T) { 1253 | if got := tt.uri.Hostname(); got != tt.hostname { 1254 | t.Errorf("URL.Hostname() = %v, want %v", got, tt.hostname) 1255 | } 1256 | }) 1257 | } 1258 | } 1259 | 1260 | func TestURL_IsAbs(t *testing.T) { 1261 | for _, tt := range urltests { 1262 | t.Run(tt.in, func(t *testing.T) { 1263 | if got := tt.uri.IsAbs(); got != tt.abs { 1264 | t.Errorf("URL.IsAbs() = %v, want %v", got, tt.abs) 1265 | } 1266 | }) 1267 | } 1268 | } 1269 | 1270 | func TestURL_Port(t *testing.T) { 1271 | for _, tt := range urltests { 1272 | t.Run(tt.in, func(t *testing.T) { 1273 | if got := tt.uri.Port(); got != tt.port { 1274 | t.Errorf("URL.Port() = %v, want %v", got, tt.port) 1275 | } 1276 | }) 1277 | } 1278 | } 1279 | 1280 | func TestURL_Query(t *testing.T) { 1281 | for _, tt := range urltests { 1282 | t.Run(tt.in, func(t *testing.T) { 1283 | if got := tt.uri.Query(); !reflect.DeepEqual(got, tt.queries) { 1284 | t.Errorf("URL.Query() = %#v, want %#v", got, tt.queries) 1285 | } 1286 | }) 1287 | } 1288 | } 1289 | 1290 | func TestURL_Parse(t *testing.T) { 1291 | for _, tt := range urltests { 1292 | t.Run(tt.in, func(t *testing.T) { 1293 | v := URL{} 1294 | gotResult, err := v.Parse(tt.in) 1295 | if err != nil { 1296 | t.Errorf("URL.Parse() error = %v, not expected", err) 1297 | if gotResult != v { 1298 | t.Errorf("URL.Parse() = %v, want %v", gotResult, URL{}) 1299 | } 1300 | return 1301 | } 1302 | if !reflect.DeepEqual(gotResult, tt.uri) { 1303 | t.Errorf("URL.Parse() = %v, want %v", gotResult, tt.uri) 1304 | } 1305 | }) 1306 | } 1307 | } 1308 | 1309 | func TestURL_RequestURI(t *testing.T) { 1310 | for _, tt := range urltests { 1311 | t.Run(tt.in, func(t *testing.T) { 1312 | if got := tt.uri.RequestURI(); got != tt.requestURI { 1313 | t.Errorf("URL.RequestURI() = %v, want %v", got, tt.requestURI) 1314 | } 1315 | }) 1316 | } 1317 | } 1318 | 1319 | func TestURL_ResolveReference(t *testing.T) { 1320 | u, _ := url.Parse(testURLString) 1321 | ud, _ := url.Parse("https://notfound.test?q1=a&q2=b") 1322 | v := URL{ 1323 | ValidFlag: false, 1324 | url: ud, 1325 | } 1326 | tests := []struct { 1327 | name string 1328 | args *url.URL 1329 | want URL 1330 | }{ 1331 | { 1332 | name: "valid URL", 1333 | args: u, 1334 | want: URL{ 1335 | ValidFlag: true, 1336 | url: u, 1337 | }, 1338 | }, 1339 | { 1340 | name: "nil", 1341 | args: nil, 1342 | want: URL{}, 1343 | }, 1344 | } 1345 | for _, tt := range tests { 1346 | t.Run(tt.name, func(t *testing.T) { 1347 | if got := v.ResolveReference(tt.args); !reflect.DeepEqual(got, tt.want) { 1348 | t.Errorf("URL.ResolveReference() = %v, want %v", got, tt.want) 1349 | } 1350 | }) 1351 | } 1352 | } 1353 | -------------------------------------------------------------------------------- /type_url_test.go: -------------------------------------------------------------------------------- 1 | // +build go1.15 2 | 3 | package generic 4 | 5 | import ( 6 | "net/url" 7 | "reflect" 8 | "testing" 9 | 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | type testURLResult struct { 14 | in string 15 | uri URL 16 | hostname string 17 | abs bool 18 | port string 19 | queries url.Values 20 | requestURI string 21 | } 22 | 23 | const ( 24 | testURLString = "https://foobar.test:8080?q=fizzbizz" 25 | rootPath = "/" 26 | ) 27 | 28 | var ( 29 | urltests = []testURLResult{ 30 | // no path 31 | { 32 | in: "http://www.google.com", 33 | uri: URL{ 34 | ValidFlag: true, 35 | url: &url.URL{ 36 | Scheme: "http", 37 | Host: "www.google.com", 38 | }, 39 | }, 40 | hostname: "www.google.com", 41 | abs: true, 42 | port: "", 43 | queries: url.Values{}, 44 | requestURI: rootPath, 45 | }, 46 | // path 47 | { 48 | in: "http://www.google.com/", 49 | uri: URL{ 50 | ValidFlag: true, 51 | url: &url.URL{ 52 | Scheme: "http", 53 | Host: "www.google.com", 54 | Path: rootPath, 55 | }, 56 | }, 57 | hostname: "www.google.com", 58 | abs: true, 59 | port: "", 60 | queries: url.Values{}, 61 | requestURI: rootPath, 62 | }, 63 | // path with hex escaping 64 | { 65 | in: "http://www.google.com/file%20one%26two", 66 | uri: URL{ 67 | ValidFlag: true, 68 | url: &url.URL{ 69 | Scheme: "http", 70 | Host: "www.google.com", 71 | Path: "/file one&two", 72 | RawPath: "/file%20one%26two", 73 | }, 74 | }, 75 | hostname: "www.google.com", 76 | abs: true, 77 | port: "", 78 | queries: url.Values{}, 79 | requestURI: "/file%20one%26two", 80 | }, 81 | // user 82 | { 83 | in: "ftp://webmaster@www.google.com/", 84 | uri: URL{ 85 | ValidFlag: true, 86 | url: &url.URL{ 87 | Scheme: "ftp", 88 | User: url.User("webmaster"), 89 | Host: "www.google.com", 90 | Path: rootPath, 91 | }, 92 | }, 93 | hostname: "www.google.com", 94 | abs: true, 95 | port: "", 96 | queries: url.Values{}, 97 | requestURI: rootPath, 98 | }, 99 | // escape sequence in username 100 | { 101 | in: "ftp://john%20doe@www.google.com/", 102 | uri: URL{ 103 | ValidFlag: true, 104 | url: &url.URL{ 105 | Scheme: "ftp", 106 | User: url.User("john doe"), 107 | Host: "www.google.com", 108 | Path: rootPath, 109 | }, 110 | }, 111 | hostname: "www.google.com", 112 | abs: true, 113 | port: "", 114 | queries: url.Values{}, 115 | requestURI: rootPath, 116 | }, 117 | // empty query 118 | { 119 | in: "http://www.google.com/?", 120 | uri: URL{ 121 | ValidFlag: true, 122 | url: &url.URL{ 123 | Scheme: "http", 124 | Host: "www.google.com", 125 | Path: "/", 126 | ForceQuery: true, 127 | }, 128 | }, 129 | hostname: "www.google.com", 130 | abs: true, 131 | port: "", 132 | queries: url.Values{}, 133 | requestURI: "/?", 134 | }, 135 | // query ending in question mark 136 | // golang.org/issue/14573 137 | { 138 | in: "http://www.google.com/?foo=bar?", 139 | uri: URL{ 140 | ValidFlag: true, 141 | url: &url.URL{ 142 | Scheme: "http", 143 | Host: "www.google.com", 144 | Path: "/", 145 | RawQuery: "foo=bar?", 146 | }, 147 | }, 148 | hostname: "www.google.com", 149 | abs: true, 150 | port: "", 151 | queries: url.Values{ 152 | "foo": []string{"bar?"}, 153 | }, 154 | requestURI: "/?foo=bar?", 155 | }, 156 | // query 157 | { 158 | in: "http://www.google.com/?q=go+language", 159 | uri: URL{ 160 | ValidFlag: true, 161 | url: &url.URL{ 162 | Scheme: "http", 163 | Host: "www.google.com", 164 | Path: "/", 165 | RawQuery: "q=go+language", 166 | }, 167 | }, 168 | hostname: "www.google.com", 169 | abs: true, 170 | port: "", 171 | queries: url.Values{ 172 | "q": []string{"go language"}, 173 | }, 174 | requestURI: "/?q=go+language", 175 | }, 176 | // query with hex escaping: NOT parsed 177 | { 178 | in: "http://www.google.com/?q=go%20language", 179 | uri: URL{ 180 | ValidFlag: true, 181 | url: &url.URL{ 182 | Scheme: "http", 183 | Host: "www.google.com", 184 | Path: "/", 185 | RawQuery: "q=go%20language", 186 | }, 187 | }, 188 | hostname: "www.google.com", 189 | abs: true, 190 | port: "", 191 | queries: url.Values{ 192 | "q": []string{"go language"}, 193 | }, 194 | requestURI: "/?q=go%20language", 195 | }, 196 | // %20 outside query 197 | { 198 | in: "http://www.google.com/a%20b?q=c+d", 199 | uri: URL{ 200 | ValidFlag: true, 201 | url: &url.URL{ 202 | Scheme: "http", 203 | Host: "www.google.com", 204 | Path: "/a b", 205 | RawQuery: "q=c+d", 206 | }, 207 | }, 208 | hostname: "www.google.com", 209 | abs: true, 210 | port: "", 211 | queries: url.Values{ 212 | "q": []string{"c d"}, 213 | }, 214 | requestURI: "/a%20b?q=c+d", 215 | }, 216 | // path without leading /, so no parsing 217 | { 218 | in: "http:www.google.com/?q=go+language", 219 | uri: URL{ 220 | ValidFlag: true, 221 | url: &url.URL{ 222 | Scheme: "http", 223 | Opaque: "www.google.com/", 224 | RawQuery: "q=go+language", 225 | }, 226 | }, 227 | hostname: "", 228 | abs: true, 229 | port: "", 230 | queries: url.Values{ 231 | "q": []string{"go language"}, 232 | }, 233 | requestURI: "www.google.com/?q=go+language", 234 | }, 235 | // path without leading /, so no parsing 236 | { 237 | in: "http:%2f%2fwww.google.com/?q=go+language", 238 | uri: URL{ 239 | ValidFlag: true, 240 | url: &url.URL{ 241 | Scheme: "http", 242 | Opaque: "%2f%2fwww.google.com/", 243 | RawQuery: "q=go+language", 244 | }, 245 | }, 246 | hostname: "", 247 | abs: true, 248 | port: "", 249 | queries: url.Values{ 250 | "q": []string{"go language"}, 251 | }, 252 | requestURI: "%2f%2fwww.google.com/?q=go+language", 253 | }, 254 | // non-authority with path 255 | { 256 | in: "mailto:/webmaster@golang.org", 257 | uri: URL{ 258 | ValidFlag: true, 259 | url: &url.URL{ 260 | Scheme: "mailto", 261 | Path: "/webmaster@golang.org", 262 | }, 263 | }, 264 | hostname: "", 265 | abs: true, 266 | port: "", 267 | queries: url.Values{}, 268 | requestURI: "/webmaster@golang.org", 269 | }, 270 | // non-authority 271 | { 272 | in: "mailto:webmaster@golang.org", 273 | uri: URL{ 274 | ValidFlag: true, 275 | url: &url.URL{ 276 | Scheme: "mailto", 277 | Opaque: "webmaster@golang.org", 278 | }, 279 | }, 280 | hostname: "", 281 | abs: true, 282 | port: "", 283 | queries: url.Values{}, 284 | requestURI: "webmaster@golang.org", 285 | }, 286 | // unescaped :// in query should not create a scheme 287 | { 288 | in: "/foo?query=http://bad", 289 | uri: URL{ 290 | ValidFlag: true, 291 | url: &url.URL{ 292 | Path: "/foo", 293 | RawQuery: "query=http://bad", 294 | }, 295 | }, 296 | hostname: "", 297 | abs: false, 298 | port: "", 299 | queries: url.Values{ 300 | "query": []string{"http://bad"}, 301 | }, 302 | requestURI: "/foo?query=http://bad", 303 | }, 304 | // leading // without scheme should create an authority 305 | { 306 | in: "//foo", 307 | uri: URL{ 308 | ValidFlag: true, 309 | url: &url.URL{ 310 | Host: "foo", 311 | }, 312 | }, 313 | hostname: "foo", 314 | abs: false, 315 | port: "", 316 | queries: url.Values{}, 317 | requestURI: rootPath, 318 | }, 319 | // leading // without scheme, with userinfo, path, and query 320 | { 321 | in: "//user@foo/path?a=b", 322 | uri: URL{ 323 | ValidFlag: true, 324 | url: &url.URL{ 325 | User: url.User("user"), 326 | Host: "foo", 327 | Path: "/path", 328 | RawQuery: "a=b", 329 | }, 330 | }, 331 | hostname: "foo", 332 | abs: false, 333 | port: "", 334 | queries: url.Values{ 335 | "a": []string{"b"}, 336 | }, 337 | requestURI: "/path?a=b", 338 | }, 339 | // Three leading slashes isn't an authority, but doesn't return an error. 340 | // (We can't return an error, as this code is also used via 341 | // ServeHTTP -> ReadRequest -> Parse, which is arguably a 342 | // different URL parsing context, but currently shares the 343 | // same codepath) 344 | { 345 | in: "///threeslashes", 346 | uri: URL{ 347 | ValidFlag: true, 348 | url: &url.URL{ 349 | Path: "///threeslashes", 350 | }, 351 | }, 352 | hostname: "", 353 | abs: false, 354 | port: "", 355 | queries: url.Values{}, 356 | requestURI: "///threeslashes", 357 | }, 358 | { 359 | in: "http://user:password@google.com", 360 | uri: URL{ 361 | ValidFlag: true, 362 | url: &url.URL{ 363 | Scheme: "http", 364 | User: url.UserPassword("user", "password"), 365 | Host: "google.com", 366 | }, 367 | }, 368 | hostname: "google.com", 369 | abs: true, 370 | port: "", 371 | queries: url.Values{}, 372 | requestURI: rootPath, 373 | }, 374 | // unescaped @ in username should not confuse host 375 | { 376 | in: "http://j@ne:password@google.com", 377 | uri: URL{ 378 | ValidFlag: true, 379 | url: &url.URL{ 380 | Scheme: "http", 381 | User: url.UserPassword("j@ne", "password"), 382 | Host: "google.com", 383 | }, 384 | }, 385 | hostname: "google.com", 386 | abs: true, 387 | port: "", 388 | queries: url.Values{}, 389 | requestURI: rootPath, 390 | }, 391 | // unescaped @ in password should not confuse host 392 | { 393 | in: "http://jane:p@ssword@google.com", 394 | uri: URL{ 395 | ValidFlag: true, 396 | url: &url.URL{ 397 | Scheme: "http", 398 | User: url.UserPassword("jane", "p@ssword"), 399 | Host: "google.com", 400 | }, 401 | }, 402 | hostname: "google.com", 403 | abs: true, 404 | port: "", 405 | queries: url.Values{}, 406 | requestURI: rootPath, 407 | }, 408 | { 409 | in: "http://j@ne:password@google.com/p@th?q=@go", 410 | uri: URL{ 411 | ValidFlag: true, 412 | url: &url.URL{ 413 | Scheme: "http", 414 | User: url.UserPassword("j@ne", "password"), 415 | Host: "google.com", 416 | Path: "/p@th", 417 | RawQuery: "q=@go", 418 | }, 419 | }, 420 | hostname: "google.com", 421 | abs: true, 422 | port: "", 423 | queries: url.Values{ 424 | "q": []string{"@go"}, 425 | }, 426 | requestURI: "/p@th?q=@go", 427 | }, 428 | { 429 | in: "http://www.google.com/?q=go+language#foo", 430 | uri: URL{ 431 | ValidFlag: true, 432 | url: &url.URL{ 433 | Scheme: "http", 434 | Host: "www.google.com", 435 | Path: "/", 436 | RawQuery: "q=go+language", 437 | Fragment: "foo", 438 | }, 439 | }, 440 | hostname: "www.google.com", 441 | abs: true, 442 | port: "", 443 | queries: url.Values{ 444 | "q": []string{"go language"}, 445 | }, 446 | requestURI: "/?q=go+language", 447 | }, 448 | { 449 | in: "http://www.google.com/?q=go+language#foo%26bar", 450 | uri: URL{ 451 | ValidFlag: true, 452 | url: &url.URL{ 453 | Scheme: "http", 454 | Host: "www.google.com", 455 | Path: "/", 456 | RawQuery: "q=go+language", 457 | Fragment: "foo&bar", 458 | RawFragment: "foo%26bar", 459 | }, 460 | }, 461 | hostname: "www.google.com", 462 | abs: true, 463 | port: "", 464 | queries: url.Values{ 465 | "q": []string{"go language"}, 466 | }, 467 | requestURI: "/?q=go+language", 468 | }, 469 | { 470 | in: "file:///home/adg/rabbits", 471 | uri: URL{ 472 | ValidFlag: true, 473 | url: &url.URL{ 474 | Scheme: "file", 475 | Host: "", 476 | Path: "/home/adg/rabbits", 477 | }, 478 | }, 479 | hostname: "", 480 | abs: true, 481 | port: "", 482 | queries: url.Values{}, 483 | requestURI: "/home/adg/rabbits", 484 | }, 485 | // "Windows" paths are no exception to the rule. 486 | // See golang.org/issue/6027, especially comment #9. 487 | { 488 | in: "file:///C:/FooBar/Baz.txt", 489 | uri: URL{ 490 | ValidFlag: true, 491 | url: &url.URL{ 492 | Scheme: "file", 493 | Host: "", 494 | Path: "/C:/FooBar/Baz.txt", 495 | }, 496 | }, 497 | hostname: "", 498 | abs: true, 499 | port: "", 500 | queries: url.Values{}, 501 | requestURI: "/C:/FooBar/Baz.txt", 502 | }, 503 | // case-insensitive scheme 504 | { 505 | in: "MaIlTo:webmaster@golang.org", 506 | uri: URL{ 507 | ValidFlag: true, 508 | url: &url.URL{ 509 | Scheme: "mailto", 510 | Opaque: "webmaster@golang.org", 511 | }, 512 | }, 513 | hostname: "", 514 | abs: true, 515 | port: "", 516 | queries: url.Values{}, 517 | requestURI: "webmaster@golang.org", 518 | }, 519 | // Relative path 520 | { 521 | in: "a/b/c", 522 | uri: URL{ 523 | ValidFlag: true, 524 | url: &url.URL{ 525 | Path: "/a/b/c", 526 | }, 527 | }, 528 | hostname: "", 529 | abs: false, 530 | port: "", 531 | queries: url.Values{}, 532 | requestURI: "/a/b/c", 533 | }, 534 | // escaped '?' in username and password 535 | { 536 | in: "http://%3Fam:pa%3Fsword@google.com", 537 | uri: URL{ 538 | ValidFlag: true, 539 | url: &url.URL{ 540 | Scheme: "http", 541 | User: url.UserPassword("?am", "pa?sword"), 542 | Host: "google.com", 543 | }, 544 | }, 545 | hostname: "google.com", 546 | abs: true, 547 | port: "", 548 | queries: url.Values{}, 549 | requestURI: rootPath, 550 | }, 551 | // host subcomponent; IPv4 address in RFC 3986 552 | { 553 | in: "http://192.168.0.1/", 554 | uri: URL{ 555 | ValidFlag: true, 556 | url: &url.URL{ 557 | Scheme: "http", 558 | Host: "192.168.0.1", 559 | Path: "/", 560 | }, 561 | }, 562 | hostname: "192.168.0.1", 563 | abs: true, 564 | port: "", 565 | queries: url.Values{}, 566 | requestURI: rootPath, 567 | }, 568 | // host and port subcomponents; IPv4 address in RFC 3986 569 | { 570 | in: "http://192.168.0.1:8080/", 571 | uri: URL{ 572 | ValidFlag: true, 573 | url: &url.URL{ 574 | Scheme: "http", 575 | Host: "192.168.0.1:8080", 576 | Path: "/", 577 | }, 578 | }, 579 | hostname: "192.168.0.1", 580 | abs: true, 581 | port: "8080", 582 | queries: url.Values{}, 583 | requestURI: rootPath, 584 | }, 585 | // host subcomponent; IPv6 address in RFC 3986 586 | { 587 | in: "http://[fe80::1]/", 588 | uri: URL{ 589 | ValidFlag: true, 590 | url: &url.URL{ 591 | Scheme: "http", 592 | Host: "[fe80::1]", 593 | Path: "/", 594 | }, 595 | }, 596 | hostname: "fe80::1", 597 | abs: true, 598 | port: "", 599 | queries: url.Values{}, 600 | requestURI: rootPath, 601 | }, 602 | // host and port subcomponents; IPv6 address in RFC 3986 603 | { 604 | in: "http://[fe80::1]:8080/", 605 | uri: URL{ 606 | ValidFlag: true, 607 | url: &url.URL{ 608 | Scheme: "http", 609 | Host: "[fe80::1]:8080", 610 | Path: "/", 611 | }, 612 | }, 613 | hostname: "fe80::1", 614 | abs: true, 615 | port: "8080", 616 | queries: url.Values{}, 617 | requestURI: rootPath, 618 | }, 619 | // host subcomponent; IPv6 address with zone identifier in RFC 6874 620 | { 621 | in: "http://[fe80::1%25en0]/", // alphanum zone identifier 622 | uri: URL{ 623 | ValidFlag: true, 624 | url: &url.URL{ 625 | Scheme: "http", 626 | Host: "[fe80::1%en0]", 627 | Path: "/", 628 | }, 629 | }, 630 | hostname: "fe80::1%en0", 631 | abs: true, 632 | port: "", 633 | queries: url.Values{}, 634 | requestURI: rootPath, 635 | }, 636 | // host and port subcomponents; IPv6 address with zone identifier in RFC 6874 637 | { 638 | in: "http://[fe80::1%25en0]:8080/", // alphanum zone identifier 639 | uri: URL{ 640 | ValidFlag: true, 641 | url: &url.URL{ 642 | Scheme: "http", 643 | Host: "[fe80::1%en0]:8080", 644 | Path: "/", 645 | }, 646 | }, 647 | hostname: "fe80::1%en0", 648 | abs: true, 649 | port: "8080", 650 | queries: url.Values{}, 651 | requestURI: rootPath, 652 | }, 653 | // host subcomponent; IPv6 address with zone identifier in RFC 6874 654 | { 655 | in: "http://[fe80::1%25%65%6e%301-._~]/", // percent-encoded+unreserved zone identifier 656 | uri: URL{ 657 | ValidFlag: true, 658 | url: &url.URL{ 659 | Scheme: "http", 660 | Host: "[fe80::1%en01-._~]", 661 | Path: "/", 662 | }, 663 | }, 664 | hostname: "fe80::1%en01-._~", 665 | abs: true, 666 | port: "", 667 | queries: url.Values{}, 668 | requestURI: rootPath, 669 | }, 670 | // host and port subcomponents; IPv6 address with zone identifier in RFC 6874 671 | { 672 | in: "http://[fe80::1%25%65%6e%301-._~]:8080/", // percent-encoded+unreserved zone identifier 673 | uri: URL{ 674 | ValidFlag: true, 675 | url: &url.URL{ 676 | Scheme: "http", 677 | Host: "[fe80::1%en01-._~]:8080", 678 | Path: "/", 679 | }, 680 | }, 681 | hostname: "fe80::1%en01-._~", 682 | abs: true, 683 | port: "8080", 684 | queries: url.Values{}, 685 | requestURI: rootPath, 686 | }, 687 | // alternate escapings of path survive round trip 688 | { 689 | in: "http://rest.rsc.io/foo%2fbar/baz%2Fquux?alt=media", 690 | uri: URL{ 691 | ValidFlag: true, 692 | url: &url.URL{ 693 | Scheme: "http", 694 | Host: "rest.rsc.io", 695 | Path: "/foo/bar/baz/quux", 696 | RawPath: "/foo%2fbar/baz%2Fquux", 697 | RawQuery: "alt=media", 698 | }, 699 | }, 700 | hostname: "rest.rsc.io", 701 | abs: true, 702 | port: "", 703 | queries: url.Values{ 704 | "alt": []string{"media"}, 705 | }, 706 | requestURI: "/foo%2fbar/baz%2Fquux?alt=media", 707 | }, 708 | // issue 12036 709 | { 710 | in: "mysql://a,b,c/bar", 711 | uri: URL{ 712 | ValidFlag: true, 713 | url: &url.URL{ 714 | Scheme: "mysql", 715 | Host: "a,b,c", 716 | Path: "/bar", 717 | }, 718 | }, 719 | hostname: "a,b,c", 720 | abs: true, 721 | port: "", 722 | queries: url.Values{}, 723 | requestURI: "/bar", 724 | }, 725 | // worst case host, still round trips 726 | { 727 | in: "scheme://!$&'()*+,;=hello!:1/path", 728 | uri: URL{ 729 | ValidFlag: true, 730 | url: &url.URL{ 731 | Scheme: "scheme", 732 | Host: "!$&'()*+,;=hello!:1", 733 | Path: "/path", 734 | }, 735 | }, 736 | hostname: "!$&'()*+,;=hello!", 737 | abs: true, 738 | port: "1", 739 | queries: url.Values{}, 740 | requestURI: "/path", 741 | }, 742 | // worst case path, still round trips 743 | { 744 | in: "http://host/!$&'()*+,;=:@[hello]", 745 | uri: URL{ 746 | ValidFlag: true, 747 | url: &url.URL{ 748 | Scheme: "http", 749 | Host: "host", 750 | Path: "/!$&'()*+,;=:@[hello]", 751 | RawPath: "/!$&'()*+,;=:@[hello]", 752 | }, 753 | }, 754 | hostname: "host", 755 | abs: true, 756 | port: "", 757 | queries: url.Values{}, 758 | requestURI: "/!$&'()*+,;=:@[hello]", 759 | }, 760 | // golang.org/issue/5684 761 | { 762 | in: "http://example.com/oid/[order_id]", 763 | uri: URL{ 764 | ValidFlag: true, 765 | url: &url.URL{ 766 | Scheme: "http", 767 | Host: "example.com", 768 | Path: "/oid/[order_id]", 769 | RawPath: "/oid/[order_id]", 770 | }, 771 | }, 772 | hostname: "example.com", 773 | abs: true, 774 | port: "", 775 | queries: url.Values{}, 776 | requestURI: "/oid/[order_id]", 777 | }, 778 | // golang.org/issue/12200 (colon with empty port) 779 | { 780 | in: "http://192.168.0.2:8080/foo", 781 | uri: URL{ 782 | ValidFlag: true, 783 | url: &url.URL{ 784 | Scheme: "http", 785 | Host: "192.168.0.2:8080", 786 | Path: "/foo", 787 | }, 788 | }, 789 | hostname: "192.168.0.2", 790 | abs: true, 791 | port: "8080", 792 | queries: url.Values{}, 793 | requestURI: "/foo", 794 | }, 795 | { 796 | in: "http://192.168.0.2:/foo", 797 | uri: URL{ 798 | ValidFlag: true, 799 | url: &url.URL{ 800 | Scheme: "http", 801 | Host: "192.168.0.2:", 802 | Path: "/foo", 803 | }, 804 | }, 805 | hostname: "192.168.0.2", 806 | abs: true, 807 | port: "", 808 | queries: url.Values{}, 809 | requestURI: "/foo", 810 | }, 811 | // Malformed IPv6 but still accepted. 812 | { 813 | in: "http://2b01:e34:ef40:7730:8e70:5aff:fefe:edac:8080/foo", 814 | uri: URL{ 815 | ValidFlag: true, 816 | url: &url.URL{ 817 | Scheme: "http", 818 | Host: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac:8080", 819 | Path: "/foo", 820 | }, 821 | }, 822 | hostname: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac", 823 | abs: true, 824 | port: "8080", 825 | queries: url.Values{}, 826 | requestURI: "/foo", 827 | }, 828 | // Malformed IPv6 but still accepted. 829 | { 830 | in: "http://2b01:e34:ef40:7730:8e70:5aff:fefe:edac:/foo", 831 | uri: URL{ 832 | ValidFlag: true, 833 | url: &url.URL{ 834 | Scheme: "http", 835 | Host: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac:", 836 | Path: "/foo", 837 | }, 838 | }, 839 | hostname: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac", 840 | abs: true, 841 | port: "", 842 | queries: url.Values{}, 843 | requestURI: "/foo", 844 | }, 845 | { 846 | in: "http://[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:8080/foo", 847 | uri: URL{ 848 | ValidFlag: true, 849 | url: &url.URL{ 850 | Scheme: "http", 851 | Host: "[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:8080", 852 | Path: "/foo", 853 | }, 854 | }, 855 | hostname: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac", 856 | abs: true, 857 | port: "8080", 858 | queries: url.Values{}, 859 | requestURI: "/foo", 860 | }, 861 | { 862 | in: "http://[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:/foo", 863 | uri: URL{ 864 | ValidFlag: true, 865 | url: &url.URL{ 866 | Scheme: "http", 867 | Host: "[2b01:e34:ef40:7730:8e70:5aff:fefe:edac]:", 868 | Path: "/foo", 869 | }, 870 | }, 871 | hostname: "2b01:e34:ef40:7730:8e70:5aff:fefe:edac", 872 | abs: true, 873 | port: "", 874 | queries: url.Values{}, 875 | requestURI: "/foo", 876 | }, 877 | // golang.org/issue/7991 and golang.org/issue/12719 (non-ascii %-encoded in host) 878 | { 879 | in: "http://hello.世界.com/foo", 880 | uri: URL{ 881 | ValidFlag: true, 882 | url: &url.URL{ 883 | Scheme: "http", 884 | Host: "hello.世界.com", 885 | Path: "/foo", 886 | }, 887 | }, 888 | hostname: "hello.世界.com", 889 | abs: true, 890 | port: "", 891 | queries: url.Values{}, 892 | requestURI: "/foo", 893 | }, 894 | { 895 | in: "http://hello.%e4%b8%96%e7%95%8c.com/foo", 896 | uri: URL{ 897 | ValidFlag: true, 898 | url: &url.URL{ 899 | Scheme: "http", 900 | Host: "hello.世界.com", 901 | Path: "/foo", 902 | }, 903 | }, 904 | hostname: "hello.世界.com", 905 | abs: true, 906 | port: "", 907 | queries: url.Values{}, 908 | requestURI: "/foo", 909 | }, 910 | { 911 | in: "http://hello.%E4%B8%96%E7%95%8C.com/foo", 912 | uri: URL{ 913 | ValidFlag: true, 914 | url: &url.URL{ 915 | Scheme: "http", 916 | Host: "hello.世界.com", 917 | Path: "/foo", 918 | }, 919 | }, 920 | hostname: "hello.世界.com", 921 | abs: true, 922 | port: "", 923 | queries: url.Values{}, 924 | requestURI: "/foo", 925 | }, 926 | // golang.org/issue/10433 (path beginning with //) 927 | { 928 | in: "http://example.com//foo", 929 | uri: URL{ 930 | ValidFlag: true, 931 | url: &url.URL{ 932 | Scheme: "http", 933 | Host: "example.com", 934 | Path: "//foo", 935 | }, 936 | }, 937 | hostname: "example.com", 938 | abs: true, 939 | port: "", 940 | queries: url.Values{}, 941 | requestURI: "//foo", 942 | }, 943 | // test that we can reparse the host names we accept. 944 | { 945 | in: "myscheme://authority<\"hi\">/foo", 946 | uri: URL{ 947 | ValidFlag: true, 948 | url: &url.URL{ 949 | Scheme: "myscheme", 950 | Host: "authority<\"hi\">", 951 | Path: "/foo", 952 | }, 953 | }, 954 | hostname: "authority<\"hi\">", 955 | abs: true, 956 | port: "", 957 | queries: url.Values{}, 958 | requestURI: "/foo", 959 | }, 960 | // spaces in hosts are disallowed but escaped spaces in IPv6 scope IDs are grudgingly OK. 961 | // This happens on Windows. 962 | // golang.org/issue/14002 963 | { 964 | in: "tcp://[2020::2020:20:2020:2020%25Windows%20Loves%20Spaces]:2020", 965 | uri: URL{ 966 | ValidFlag: true, 967 | url: &url.URL{ 968 | Scheme: "tcp", 969 | Host: "[2020::2020:20:2020:2020%Windows Loves Spaces]:2020", 970 | }, 971 | }, 972 | hostname: "2020::2020:20:2020:2020%Windows Loves Spaces", 973 | abs: true, 974 | port: "2020", 975 | queries: url.Values{}, 976 | requestURI: rootPath, 977 | }, 978 | { 979 | in: "magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn", 980 | uri: URL{ 981 | ValidFlag: true, 982 | url: &url.URL{ 983 | Scheme: "magnet", 984 | Host: "", 985 | Path: "", 986 | RawQuery: "xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn", 987 | }, 988 | }, 989 | hostname: "", 990 | abs: true, 991 | port: "", 992 | queries: url.Values{ 993 | "xt": []string{"urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"}, 994 | "dn": []string{""}, 995 | }, 996 | requestURI: "/?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a&dn", 997 | }, 998 | { 999 | in: "mailto:?subject=hi", 1000 | uri: URL{ 1001 | ValidFlag: true, 1002 | url: &url.URL{ 1003 | Scheme: "mailto", 1004 | Host: "", 1005 | Path: "", 1006 | RawQuery: "subject=hi", 1007 | }, 1008 | }, 1009 | hostname: "", 1010 | abs: true, 1011 | port: "", 1012 | queries: url.Values{ 1013 | "subject": []string{"hi"}, 1014 | }, 1015 | requestURI: "/?subject=hi", 1016 | }, 1017 | } 1018 | 1019 | // { 1020 | // in: "validFlag false", 1021 | // uri: URL{ 1022 | // ValidFlag: false, 1023 | // url: &url.URL{ 1024 | // Scheme: "http", 1025 | // Host: "www.google.com", 1026 | // Path: "/", 1027 | // RawQuery: "q=go+language", 1028 | // Fragment: "foo", 1029 | // }, 1030 | // }, 1031 | // hostname: "", 1032 | // abs: false, 1033 | // port: "", 1034 | // queries: url.Values{}, 1035 | // requestURI: "", 1036 | // }, 1037 | // { 1038 | // in: "nil", 1039 | // uri: URL{ 1040 | // ValidFlag: true, 1041 | // url: nil, 1042 | // }, 1043 | // hostname: "", 1044 | // abs: false, 1045 | // port: "", 1046 | // queries: url.Values{}, 1047 | // requestURI: "", 1048 | // }, 1049 | ) 1050 | 1051 | func TestMarshalURL(t *testing.T) { 1052 | v, _ := url.Parse(testURLString) 1053 | 1054 | expected := URL{ 1055 | ValidFlag: true, 1056 | url: v, 1057 | } 1058 | 1059 | tests := []struct { 1060 | name string 1061 | args interface{} 1062 | want URL 1063 | wantErr bool 1064 | }{ 1065 | { 1066 | name: "URL", 1067 | args: v, 1068 | want: expected, 1069 | wantErr: false, 1070 | }, 1071 | { 1072 | name: "string", 1073 | args: testURLString, 1074 | want: expected, 1075 | wantErr: false, 1076 | }, 1077 | { 1078 | name: "invalid", 1079 | args: 10000, 1080 | want: URL{}, 1081 | wantErr: true, 1082 | }, 1083 | } 1084 | for _, tt := range tests { 1085 | t.Run(tt.name, func(t *testing.T) { 1086 | got, err := MarshalURL(tt.args) 1087 | if (err != nil) != tt.wantErr { 1088 | t.Errorf("MarshalURL() error = %v, wantErr %v", err, tt.wantErr) 1089 | return 1090 | } 1091 | if !reflect.DeepEqual(got, tt.want) { 1092 | t.Errorf("MarshalURL() = %v, want %v", got, tt.want) 1093 | } 1094 | }) 1095 | } 1096 | } 1097 | 1098 | func TestMustURL(t *testing.T) { 1099 | v, _ := url.Parse(testURLString) 1100 | 1101 | expected := URL{ 1102 | ValidFlag: true, 1103 | url: v, 1104 | } 1105 | 1106 | tests := []struct { 1107 | name string 1108 | args interface{} 1109 | want URL 1110 | wantPanic bool 1111 | }{ 1112 | { 1113 | name: "valid", 1114 | args: testURLString, 1115 | want: expected, 1116 | wantPanic: false, 1117 | }, 1118 | { 1119 | name: "valid", 1120 | args: struct{}{}, 1121 | want: URL{ 1122 | ValidFlag: false, 1123 | }, 1124 | wantPanic: true, 1125 | }, 1126 | } 1127 | for _, tt := range tests { 1128 | t.Run(tt.name, func(t *testing.T) { 1129 | if tt.wantPanic { 1130 | p := assert.Panics(t, func() { 1131 | MustURL(tt.args) 1132 | }) 1133 | if !p { 1134 | t.Errorf("MustURL() panic = %v, want panic %v", p, tt.wantPanic) 1135 | } 1136 | return 1137 | } 1138 | if got := MustURL(tt.args); !reflect.DeepEqual(got, tt.want) { 1139 | t.Errorf("MustURL() = %v, want %v", got, tt.want) 1140 | } 1141 | }) 1142 | } 1143 | } 1144 | 1145 | func TestURL_Value(t *testing.T) { 1146 | t.Skip() 1147 | } 1148 | 1149 | func TestURL_Scan(t *testing.T) { 1150 | t.Skip() 1151 | } 1152 | 1153 | func TestURL_Weak(t *testing.T) { 1154 | for _, tt := range urltests { 1155 | t.Run(tt.in, func(t *testing.T) { 1156 | if got := tt.uri.Weak(); got != tt.uri.url { 1157 | t.Errorf("URL.URL() = %v, want %v", got, tt.uri.url) 1158 | } 1159 | }) 1160 | } 1161 | } 1162 | 1163 | // func TestURL_Set(t *testing.T) { 1164 | // t.Skip() 1165 | // } 1166 | 1167 | func TestURL_String(t *testing.T) { 1168 | u1, _ := url.Parse(testURLString) 1169 | 1170 | s := "/foobar?fizz=bizz" 1171 | u2, _ := url.Parse(s) 1172 | 1173 | tests := []struct { 1174 | name string 1175 | fields URL 1176 | want string 1177 | }{ 1178 | { 1179 | name: "valid url", 1180 | fields: URL{ 1181 | ValidFlag: true, 1182 | url: u1, 1183 | }, 1184 | want: testURLString, 1185 | }, 1186 | { 1187 | name: "ValidFlag false", 1188 | fields: URL{ 1189 | ValidFlag: false, 1190 | url: u1, 1191 | }, 1192 | want: "", 1193 | }, 1194 | { 1195 | name: "only path", 1196 | fields: URL{ 1197 | ValidFlag: true, 1198 | url: u2, 1199 | }, 1200 | want: s, 1201 | }, 1202 | } 1203 | for _, tt := range tests { 1204 | t.Run(tt.name, func(t *testing.T) { 1205 | if got := tt.fields.String(); got != tt.want { 1206 | t.Errorf("URL.String() = %v, want %v", got, tt.want) 1207 | } 1208 | }) 1209 | } 1210 | } 1211 | 1212 | func TestURL_URL(t *testing.T) { 1213 | for _, tt := range urltests { 1214 | t.Run(tt.in, func(t *testing.T) { 1215 | if got := tt.uri.URL(); got != tt.uri.url { 1216 | t.Errorf("URL.URL() = %v, want %v", got, tt.uri.url) 1217 | } 1218 | }) 1219 | } 1220 | } 1221 | 1222 | func TestURL_MarshalJSON_UnmarshalJSON(t *testing.T) { 1223 | for _, tt := range urltests { 1224 | t.Run(tt.in, func(t *testing.T) { 1225 | bs, err := tt.uri.MarshalJSON() 1226 | if err != nil { 1227 | t.Errorf("URL.MarshalJSON() error = %v, not expected", err) 1228 | return 1229 | } 1230 | v := URL{} 1231 | err = v.UnmarshalJSON(bs) 1232 | if err != nil { 1233 | t.Errorf("URL.UnmarshalJSON() error = %v, not expected", err) 1234 | return 1235 | } 1236 | if v.String() != tt.uri.String() { 1237 | t.Errorf("URL.UnmarshalJSON() = %v, want %v", v, tt.uri) 1238 | } 1239 | }) 1240 | } 1241 | } 1242 | 1243 | func TestURL_UnmarshalJSON(t *testing.T) { 1244 | t.Skip() 1245 | } 1246 | 1247 | func TestURL_EscapedPath(t *testing.T) { 1248 | t.Skip() 1249 | } 1250 | 1251 | func TestURL_Hostname(t *testing.T) { 1252 | for _, tt := range urltests { 1253 | t.Run(tt.in, func(t *testing.T) { 1254 | if got := tt.uri.Hostname(); got != tt.hostname { 1255 | t.Errorf("URL.Hostname() = %v, want %v", got, tt.hostname) 1256 | } 1257 | }) 1258 | } 1259 | } 1260 | 1261 | func TestURL_IsAbs(t *testing.T) { 1262 | for _, tt := range urltests { 1263 | t.Run(tt.in, func(t *testing.T) { 1264 | if got := tt.uri.IsAbs(); got != tt.abs { 1265 | t.Errorf("URL.IsAbs() = %v, want %v", got, tt.abs) 1266 | } 1267 | }) 1268 | } 1269 | } 1270 | 1271 | func TestURL_Port(t *testing.T) { 1272 | for _, tt := range urltests { 1273 | t.Run(tt.in, func(t *testing.T) { 1274 | if got := tt.uri.Port(); got != tt.port { 1275 | t.Errorf("URL.Port() = %v, want %v", got, tt.port) 1276 | } 1277 | }) 1278 | } 1279 | } 1280 | 1281 | func TestURL_Query(t *testing.T) { 1282 | for _, tt := range urltests { 1283 | t.Run(tt.in, func(t *testing.T) { 1284 | if got := tt.uri.Query(); !reflect.DeepEqual(got, tt.queries) { 1285 | t.Errorf("URL.Query() = %#v, want %#v", got, tt.queries) 1286 | } 1287 | }) 1288 | } 1289 | } 1290 | 1291 | func TestURL_Parse(t *testing.T) { 1292 | for _, tt := range urltests { 1293 | t.Run(tt.in, func(t *testing.T) { 1294 | v := URL{} 1295 | gotResult, err := v.Parse(tt.in) 1296 | if err != nil { 1297 | t.Errorf("URL.Parse() error = %v, not expected", err) 1298 | if gotResult != v { 1299 | t.Errorf("URL.Parse() = %v, want %v", gotResult, URL{}) 1300 | } 1301 | return 1302 | } 1303 | if !reflect.DeepEqual(gotResult, tt.uri) { 1304 | t.Errorf("URL.Parse() = %v, want %v", gotResult, tt.uri) 1305 | } 1306 | }) 1307 | } 1308 | } 1309 | 1310 | func TestURL_RequestURI(t *testing.T) { 1311 | for _, tt := range urltests { 1312 | t.Run(tt.in, func(t *testing.T) { 1313 | if got := tt.uri.RequestURI(); got != tt.requestURI { 1314 | t.Errorf("URL.RequestURI() = %v, want %v", got, tt.requestURI) 1315 | } 1316 | }) 1317 | } 1318 | } 1319 | 1320 | func TestURL_ResolveReference(t *testing.T) { 1321 | u, _ := url.Parse(testURLString) 1322 | ud, _ := url.Parse("https://notfound.test?q1=a&q2=b") 1323 | v := URL{ 1324 | ValidFlag: false, 1325 | url: ud, 1326 | } 1327 | tests := []struct { 1328 | name string 1329 | args *url.URL 1330 | want URL 1331 | }{ 1332 | { 1333 | name: "valid URL", 1334 | args: u, 1335 | want: URL{ 1336 | ValidFlag: true, 1337 | url: u, 1338 | }, 1339 | }, 1340 | { 1341 | name: "nil", 1342 | args: nil, 1343 | want: URL{}, 1344 | }, 1345 | } 1346 | for _, tt := range tests { 1347 | t.Run(tt.name, func(t *testing.T) { 1348 | if got := v.ResolveReference(tt.args); !reflect.DeepEqual(got, tt.want) { 1349 | t.Errorf("URL.ResolveReference() = %v, want %v", got, tt.want) 1350 | } 1351 | }) 1352 | } 1353 | } 1354 | --------------------------------------------------------------------------------