pax_global_header00006660000000000000000000000064143163657300014522gustar00rootroot0000000000000052 comment=489daf05a6ccfffdd72c1d5e972bb14163fa07b1 go-scalar-1.2.0/000077500000000000000000000000001431636573000133725ustar00rootroot00000000000000go-scalar-1.2.0/.github/000077500000000000000000000000001431636573000147325ustar00rootroot00000000000000go-scalar-1.2.0/.github/workflows/000077500000000000000000000000001431636573000167675ustar00rootroot00000000000000go-scalar-1.2.0/.github/workflows/go.yml000066400000000000000000000012061431636573000201160ustar00rootroot00000000000000name: Go on: push: branches: [ master ] pull_request: branches: [ master ] jobs: build_and_test: name: Build and test runs-on: ubuntu-latest strategy: fail-fast: false matrix: go: ['1.17', '1.18', '1.19'] steps: - id: go name: Set up Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} - name: Checkout uses: actions/checkout@v2 - name: Build run: go build -v . - name: Test run: go test -v -coverprofile=profile.cov . - name: Send coverage run: bash <(curl -s https://codecov.io/bash) -f profile.cov go-scalar-1.2.0/.gitignore000066400000000000000000000004121431636573000153570ustar00rootroot00000000000000# Compiled Object files, Static and Dynamic libs (Shared Objects) *.o *.a *.so # Folders _obj _test # Architecture specific extensions/prefixes *.[568vq] [568vq].out *.cgo1.go *.cgo2.c _cgo_defun.c _cgo_gotypes.go _cgo_export.* _testmain.go *.exe *.test *.prof go-scalar-1.2.0/LICENSE000066400000000000000000000024161431636573000144020ustar00rootroot00000000000000Copyright (c) 2015, Alex Flint All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. go-scalar-1.2.0/README.md000066400000000000000000000020711431636573000146510ustar00rootroot00000000000000[![GoDoc](https://godoc.org/github.com/alexflint/go-scalar?status.svg)](https://godoc.org/github.com/alexflint/go-scalar) [![Build Status](https://travis-ci.org/alexflint/go-scalar.svg?branch=master)](https://travis-ci.org/alexflint/go-scalar) [![Coverage Status](https://coveralls.io/repos/alexflint/go-scalar/badge.svg?branch=master&service=github)](https://coveralls.io/github/alexflint/go-scalar?branch=master) [![Report Card](https://goreportcard.com/badge/github.com/alexflint/go-scalar)](https://goreportcard.com/badge/github.com/alexflint/go-scalar) ## Scalar parsing library Scalar is a library for parsing strings into arbitrary scalars (integers, floats, strings, booleans, etc). It is helpful for tasks such as parsing strings passed as environment variables or command line arguments. ```shell go get github.com/alexflint/go-scalar ``` The main API works as follows: ```go var value int err := scalar.Parse(&value, "123") ``` There is also a variant that takes a `reflect.Value`: ```go var value int err := scalar.ParseValue(reflect.ValueOf(&value), "123") ``` go-scalar-1.2.0/go.mod000066400000000000000000000003011431636573000144720ustar00rootroot00000000000000module github.com/alexflint/go-scalar go 1.15 require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/stretchr/testify v1.2.2 ) go-scalar-1.2.0/go.sum000066400000000000000000000010111431636573000145160ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= go-scalar-1.2.0/scalar.go000066400000000000000000000075331431636573000151760ustar00rootroot00000000000000// Package scalar parses strings into values of scalar type. package scalar import ( "encoding" "errors" "fmt" "net" "net/mail" "net/url" "reflect" "strconv" "time" ) // The reflected form of some special types var ( textUnmarshalerType = reflect.TypeOf([]encoding.TextUnmarshaler{}).Elem() durationType = reflect.TypeOf(time.Duration(0)) mailAddressType = reflect.TypeOf(mail.Address{}) macType = reflect.TypeOf(net.HardwareAddr{}) urlType = reflect.TypeOf(url.URL{}) ) var ( errNotSettable = errors.New("value is not settable") errPtrNotSettable = errors.New("value is a nil pointer and is not settable") ) // Parse assigns a value to v by parsing s. func Parse(dest interface{}, s string) error { return ParseValue(reflect.ValueOf(dest), s) } // ParseValue assigns a value to v by parsing s. func ParseValue(v reflect.Value, s string) error { // If we have a nil pointer then allocate a new object if v.Kind() == reflect.Ptr && v.IsNil() { if !v.CanSet() { return errPtrNotSettable } v.Set(reflect.New(v.Type().Elem())) } // If it implements encoding.TextUnmarshaler then use that if scalar, ok := v.Interface().(encoding.TextUnmarshaler); ok { return scalar.UnmarshalText([]byte(s)) } // If it's a value instead of a pointer, check that we can unmarshal it // via TextUnmarshaler as well if v.CanAddr() { if scalar, ok := v.Addr().Interface().(encoding.TextUnmarshaler); ok { return scalar.UnmarshalText([]byte(s)) } } // If we have a pointer then dereference it if v.Kind() == reflect.Ptr { v = v.Elem() } if !v.CanSet() { return errNotSettable } // Switch on concrete type switch scalar := v.Interface(); scalar.(type) { case time.Duration: duration, err := time.ParseDuration(s) if err != nil { return err } v.Set(reflect.ValueOf(duration)) return nil case mail.Address: addr, err := mail.ParseAddress(s) if err != nil { return err } v.Set(reflect.ValueOf(*addr)) return nil case net.HardwareAddr: ip, err := net.ParseMAC(s) if err != nil { return err } v.Set(reflect.ValueOf(ip)) return nil case url.URL: url, err := url.Parse(s) if err != nil { return err } v.Set(reflect.ValueOf(*url)) return nil } // Switch on kind so that we can handle derived types switch v.Kind() { case reflect.String: v.SetString(s) case reflect.Bool: x, err := strconv.ParseBool(s) if err != nil { return err } v.SetBool(x) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: x, err := strconv.ParseInt(s, 0, v.Type().Bits()) if err != nil { return err } v.SetInt(x) case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: x, err := strconv.ParseUint(s, 0, v.Type().Bits()) if err != nil { return err } v.SetUint(x) case reflect.Float32, reflect.Float64: x, err := strconv.ParseFloat(s, v.Type().Bits()) if err != nil { return err } v.SetFloat(x) default: return fmt.Errorf("cannot parse into %v", v.Type()) } return nil } // CanParse returns true if the type can be parsed from a string. func CanParse(t reflect.Type) bool { // If it implements encoding.TextUnmarshaler then use that if t.Implements(textUnmarshalerType) || reflect.PtrTo(t).Implements(textUnmarshalerType) { return true } // If we have a pointer then dereference it if t.Kind() == reflect.Ptr { t = t.Elem() } // Check for other special types switch t { case durationType, mailAddressType, macType, urlType: return true } // Fall back to checking the kind switch t.Kind() { case reflect.Bool: return true case reflect.String, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64: return true } return false } go-scalar-1.2.0/scalar_test.go000066400000000000000000000100051431636573000162210ustar00rootroot00000000000000package scalar import ( "net" "net/mail" "net/url" "reflect" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) type textUnmarshaler struct { val int } func (f *textUnmarshaler) UnmarshalText(b []byte) error { f.val = len(b) return nil } func assertParse(t *testing.T, expected interface{}, str string) { v := reflect.New(reflect.TypeOf(expected)).Elem() err := ParseValue(v, str) if assert.NoError(t, err) { assert.Equal(t, expected, v.Interface()) } ptr := reflect.New(reflect.PtrTo(reflect.TypeOf(expected))).Elem() err = ParseValue(ptr, str) if assert.NoError(t, err) { assert.Equal(t, expected, ptr.Elem().Interface()) } assert.True(t, CanParse(v.Type())) assert.True(t, CanParse(ptr.Type())) } func TestParseValue(t *testing.T) { // strings assertParse(t, "abc", "abc") // booleans assertParse(t, true, "true") assertParse(t, false, "false") // integers assertParse(t, int(123), "123") assertParse(t, int(123), "1_2_3") assertParse(t, int8(123), "123") assertParse(t, int16(123), "123") assertParse(t, int32(123), "123") assertParse(t, int64(123), "123") // unsigned integers assertParse(t, uint(123), "123") assertParse(t, uint(123), "1_2_3") assertParse(t, byte(123), "123") assertParse(t, uint8(123), "123") assertParse(t, uint16(123), "123") assertParse(t, uint32(123), "123") assertParse(t, uint64(123), "123") assertParse(t, uintptr(123), "123") assertParse(t, rune(123), "123") // floats assertParse(t, float32(123), "123") assertParse(t, float64(123), "123") // durations assertParse(t, 3*time.Hour+15*time.Minute, "3h15m") // IP addresses assertParse(t, net.IPv4(1, 2, 3, 4), "1.2.3.4") // email addresses assertParse(t, mail.Address{Address: "joe@example.com"}, "joe@example.com") // MAC addresses assertParse(t, net.HardwareAddr("\x01\x23\x45\x67\x89\xab"), "01:23:45:67:89:ab") // MAC addresses assertParse(t, net.HardwareAddr("\x01\x23\x45\x67\x89\xab"), "01:23:45:67:89:ab") // URL assertParse(t, url.URL{Scheme: "https", Host: "example.com", Path: "/a/b/c"}, "https://example.com/a/b/c") // custom text unmarshaler assertParse(t, textUnmarshaler{3}, "abc") } func TestParseErrors(t *testing.T) { var err error // this should fail because the pointer is nil and will not be settable var p *int err = ParseValue(reflect.ValueOf(p), "123") assert.Equal(t, errPtrNotSettable, err) // this should fail because the value will not be settable var v int err = ParseValue(reflect.ValueOf(v), "123") assert.Equal(t, errNotSettable, err) // this should fail due to a malformed boolean var b bool err = ParseValue(reflect.ValueOf(&b), "malformed") assert.Error(t, err) // this should fail due to a malformed boolean var i int err = ParseValue(reflect.ValueOf(&i), "malformed") assert.Error(t, err) // this should fail due to a malformed boolean var u uint err = ParseValue(reflect.ValueOf(&u), "malformed") assert.Error(t, err) // this should fail due to a malformed boolean var f float64 err = ParseValue(reflect.ValueOf(&f), "malformed") assert.Error(t, err) // this should fail due to a malformed time duration var d time.Duration err = ParseValue(reflect.ValueOf(&d), "malfomed") assert.Error(t, err) // this should fail due to a malformed email address var email mail.Address err = ParseValue(reflect.ValueOf(&email), "malfomed") assert.Error(t, err) // this should fail due to a malformed time duration var mac net.HardwareAddr err = ParseValue(reflect.ValueOf(&mac), "malfomed") assert.Error(t, err) // this should fail due to a malformed time duration var url url.URL err = ParseValue(reflect.ValueOf(&url), "$:") assert.Error(t, err) // this should fail due to an unsupported type var x struct{} err = ParseValue(reflect.ValueOf(&x), "$") assert.Error(t, err) } func TestParse(t *testing.T) { var v int err := Parse(&v, "123") require.NoError(t, err) assert.Equal(t, 123, v) } func TestCanParseReturnsFalse(t *testing.T) { var x struct{} assert.Equal(t, false, CanParse(reflect.TypeOf(x))) }