Skip to content

Commit

Permalink
Merge pull request #5 from usk81/add_extending_url_type
Browse files Browse the repository at this point in the history
add URL type
  • Loading branch information
usk81 committed Jul 30, 2018
2 parents 4532c2b + 19b8fc6 commit 9cd4143
Show file tree
Hide file tree
Showing 8 changed files with 1,590 additions and 1 deletion.
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
language: go
go:
- "1.7"
- "1.8"
- "1.9"
- "1.10"
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@

flexible data type for Go

support: Go 1.8+

## Install

standard `go get`:
Expand Down
15 changes: 15 additions & 0 deletions convert.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package generic

import (
"net/url"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -343,3 +344,17 @@ func asTimestampWithFunc(x interface{}, f func(i int64) time.Time) (result time.
}
return f(i), true, nil
}

func asURL(x interface{}) (result *url.URL, isValid ValidFlag, err error) {
switch x.(type) {
case nil:
return nil, false, nil
case *url.URL:
result = x.(*url.URL)
case string:
result, err = url.Parse(x.(string))
default:
err = ErrInvalidGenericValue{Value: x}
}
return result, (err == nil), err
}
70 changes: 70 additions & 0 deletions convert_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package generic

import (
"net/url"
"reflect"
"testing"
)

func Test_asURL(t *testing.T) {
u, _ := url.Parse(testURLString)

tests := []struct {
name string
args interface{}
wantResult *url.URL
wantIsValid ValidFlag
wantErr bool
}{
{
name: "nil",
args: nil,
wantResult: nil,
wantIsValid: false,
wantErr: false,
},
{
name: "string",
args: testURLString,
wantResult: u,
wantIsValid: true,
wantErr: false,
},
{
name: "url.URL",
args: u,
wantResult: u,
wantIsValid: true,
wantErr: false,
},
{
name: "int",
args: 1000,
wantResult: nil,
wantIsValid: false,
wantErr: true,
},
{
name: "bool",
args: true,
wantResult: nil,
wantIsValid: false,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
gotResult, gotIsValid, err := asURL(tt.args)
if (err != nil) != tt.wantErr {
t.Errorf("asURL() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(gotResult, tt.wantResult) {
t.Errorf("asURL() gotResult = %v, want %v", gotResult, tt.wantResult)
}
if gotIsValid != tt.wantIsValid {
t.Errorf("asURL() gotIsValid = %v, want %v", gotIsValid, tt.wantIsValid)
}
})
}
}
14 changes: 14 additions & 0 deletions json_marshal_benchmark_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package generic

import (
"net/url"
"testing"
"time"
)
Expand Down Expand Up @@ -104,3 +105,16 @@ func BenchmarkMarshalJSONUint(b *testing.B) {
x.MarshalJSON()
}
}

func BenchmarkMarshalJSONURL(b *testing.B) {
x := URL{
ValidFlag: true,
url: &url.URL{
Scheme: "https",
Host: "www.google.com",
},
}
for i := 0; i < b.N; i++ {
x.MarshalJSON()
}
}
11 changes: 11 additions & 0 deletions json_unmarshal_benchmark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ func BenchmarkUnmarshalJSONTimeFromString(b *testing.B) {
unmarshalJSONTimeBenchmark(b, []byte(`"`+now.String()+`"`))
}

func BenchmarkUnmarshalJSONURLFromString(b *testing.B) {
unmarshalJSONURLBenchmark(b, []byte(`"https://google.com"`))
}

func unmarshalJSONBoolBenchmark(b *testing.B, bs []byte) {
x := Bool{}
for i := 0; i < b.N; i++ {
Expand Down Expand Up @@ -119,3 +123,10 @@ func unmarshalJSONUintBenchmark(b *testing.B, bs []byte) {
x.UnmarshalJSON(bs)
}
}

func unmarshalJSONURLBenchmark(b *testing.B, bs []byte) {
x := URL{}
for i := 0; i < b.N; i++ {
x.UnmarshalJSON(bs)
}
}
171 changes: 171 additions & 0 deletions type_url.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
package generic

import (
"database/sql/driver"
"encoding/json"
"net/url"
)

// URL is generic url.URL type structure
type URL struct {
ValidFlag
url *url.URL
}

// MarshalURL return generic.URL converting of request data
func MarshalURL(x interface{}) (URL, error) {
v := URL{}
err := v.Scan(x)
return v, err
}

// Value implements the driver Valuer interface.
func (v URL) Value() (driver.Value, error) {
if !v.Valid() || v.url == nil {
return nil, nil
}
return v.url.String(), nil
}

// Scan implements the sql.Scanner interface.
func (v *URL) Scan(x interface{}) (err error) {
v.url, v.ValidFlag, err = asURL(x)
return
}

// Weak returns *url.URL, but if String.ValidFlag is false, returns nil.
func (v URL) Weak() interface{} {
return v.URL()
}

// Set sets a specified value.
func (v *URL) Set(x interface{}) (err error) {
return v.Scan(x)
}

// String implements the Stringer interface.
func (v URL) String() string {
if !v.Valid() || v.url == nil {
return ""
}
return v.url.String()
}

// URL returns *url.URL, but if String.ValidFlag is false, returns nil.
func (v URL) URL() *url.URL {
if !v.Valid() || v.url == nil {
return nil
}
return v.url
}

// MarshalJSON implements the json.Marshaler interface.
func (v URL) MarshalJSON() ([]byte, error) {
if !v.Valid() || v.url == nil {
return nullBytes, nil
}
return json.Marshal(v.url.String())
}

// UnmarshalJSON implements the json.Unmarshaler interface.
func (v *URL) UnmarshalJSON(data []byte) error {
if len(data) == 0 {
return nil
}
var in interface{}
if err := json.Unmarshal(data, &in); err != nil {
return err
}
return v.Scan(in)
}

// EscapedPath returns the escaped form of v.url.Path.
// In general there are multiple possible escaped forms of any path.
//
// EscapedPath returns v.url.RawPath when it is a valid escaping of v.url.Path.
// Otherwise EscapedPath ignores v.url.RawPath and computes an escaped form on its own.
// The String and RequestURI methods use EscapedPath to construct their results.
// In general, code should call EscapedPath instead of reading v.url.RawPath directly.
func (v URL) EscapedPath() string {
if !v.Valid() || v.url == nil {
return ""
}
return v.url.EscapedPath()
}

// Hostname returns v.url.Host, without any port number.
//
// If Host is an IPv6 literal with a port number, Hostname returns the IPv6 literal without the square brackets.
// IPv6 literals may include a zone identifier.
func (v URL) Hostname() string {
if !v.Valid() || v.url == nil {
return ""
}
return v.url.Hostname()
}

// IsAbs reports whether the URL is absolute. Absolute means that it has a non-empty scheme.
func (v URL) IsAbs() bool {
if !v.Valid() || v.url == nil {
return false
}
return v.url.IsAbs()
}

// Port returns the port part of u.Host, without the leading colon.
// If u.Host doesn't contain a port, Port returns an empty string.
func (v URL) Port() string {
if !v.Valid() || v.url == nil {
return ""
}
return v.url.Port()
}

// Query parses RawQuery and returns the corresponding values.
// It silently discards malformed value pairs. To check errors use ParseQuery.
func (v URL) Query() url.Values {
if !v.Valid() || v.url == nil {
return url.Values{}
}
return v.url.Query()
}

// Parse parses a URL in the context of the receiver.
// The provided URL may be relative or absolute.
// Parse returns nil, err on parse failure, otherwise its return value is the same as ResolveReference.
func (v URL) Parse(ref string) (result URL, err error) {
if v.url == nil {
u := url.URL{}
v.url, err = u.Parse(ref)
} else {
v.url, err = v.url.Parse(ref)
}
if err != nil {
v = URL{}
return v, err
}
v.ValidFlag = true
return v, err
}

// RequestURI returns the encoded path?query or opaque?query string that would be used in an HTTP request for v.
func (v URL) RequestURI() string {
if !v.Valid() || v.url == nil {
return ""
}
return v.url.RequestURI()
}

// ResolveReference resolves a URI reference to an absolute URI from an absolute base URI, per RFC 3986 Section 5.2.
// 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.
// If ref is an absolute URL, then ResolveReference ignores base and returns a copy of ref.
func (v URL) ResolveReference(ref *url.URL) URL {
if ref == nil {
v.ValidFlag = false
v.url = nil
} else {
v.ValidFlag = true
v.url = v.url.ResolveReference(ref)
}
return v
}
Loading

0 comments on commit 9cd4143

Please sign in to comment.