safe and easy casting from one type to another in Go

cast

GoDoc Build Status Go Report Card

Easy and safe casting from one type to another in Go

Don’t Panic! ... Cast

What is Cast?

Cast is a library to convert between different go types in a consistent and easy way.

Cast provides simple functions to easily convert a number to a string, an interface into a bool, etc. Cast does this intelligently when an obvious conversion is possible. It doesn’t make any attempts to guess what you meant, for example you can only convert a string to an int when it is a string representation of an int such as “8”. Cast was developed for use in Hugo, a website engine which uses YAML, TOML or JSON for meta data.

Why use Cast?

When working with dynamic data in Go you often need to cast or convert the data from one type into another. Cast goes beyond just using type assertion (though it uses that when possible) to provide a very straightforward and convenient library.

If you are working with interfaces to handle things like dynamic content you’ll need an easy way to convert an interface into a given type. This is the library for you.

If you are taking in data from YAML, TOML or JSON or other formats which lack full types, then Cast is the library for you.

Usage

Cast provides a handful of To_____ methods. These methods will always return the desired type. If input is provided that will not convert to that type, the 0 or nil value for that type will be returned.

Cast also provides identical methods To_____E. These return the same result as the To_____ methods, plus an additional error which tells you if it successfully converted. Using these methods you can tell the difference between when the input matched the zero value or when the conversion failed and the zero value was returned.

The following examples are merely a sample of what is available. Please review the code for a complete set.

Example ‘ToString’:

cast.ToString("mayonegg")         // "mayonegg"
cast.ToString(8)                  // "8"
cast.ToString(8.31)               // "8.31"
cast.ToString([]byte("one time")) // "one time"
cast.ToString(nil)                // ""

var foo interface{} = "one more time"
cast.ToString(foo)                // "one more time"

Example ‘ToInt’:

cast.ToInt(8)                  // 8
cast.ToInt(8.31)               // 8
cast.ToInt("8")                // 8
cast.ToInt(true)               // 1
cast.ToInt(false)              // 0

var eight interface{} = 8
cast.ToInt(eight)              // 8
cast.ToInt(nil)                // 0
Owner
Steve Francia
@golang product lead at @google • Author, Speaker, Developer • Creator of @gohugoio, Cobra, Viper & spf13-vim • former @docker & @mongodb
Steve Francia
Comments
  • Add support for maps defined as a json string

    Add support for maps defined as a json string

    • As a fix for https://github.com/spf13/viper/issues/451 , this PR adds support for casting map types from JSON strings. This allows viper to support binding environment variables for more types. This uses the stdlib's encoding/json package for unmarshalling the data and bubbles up unmarshalling errors to the caller.
    • Add test cases to all map casting methods to test this feature
    • Add test cases to TestToDurationSliceE in order to reach 100% total coverage for the lib
  • use errUnableToCast instead of fmt.Errorf

    use errUnableToCast instead of fmt.Errorf

    • [x] added benchmarks
    • [x] use predefined errUnableToCast instead of fmt.Errorf
    • [x] fixed golint

    before:

    go test -bench=.
    goos: darwin
    goarch: amd64
    pkg: github.com/yanmhlv/cast
    BenchmarkTooBool/valid_bool(true)-4         	200000000	         8.25 ns/op	       0 B/op	       0 allocs/op
    BenchmarkTooBool/valid_bool(1)-4            	200000000	         8.36 ns/op	       0 B/op	       0 allocs/op
    BenchmarkTooBool/invalid_bool("xxx")-4      	30000000	        45.1 ns/op	      48 B/op	       1 allocs/op
    BenchmarkTooBool/invalid_bool(1.0)-4        	 5000000	       302 ns/op	      96 B/op	       3 allocs/op
    PASS
    

    after:

    goos: darwin
    goarch: amd64
    pkg: github.com/yanmhlv/cast
    BenchmarkTooBool/valid_bool(true)-4         	200000000	         8.03 ns/op	       0 B/op	       0 allocs/op
    BenchmarkTooBool/valid_bool(1)-4            	200000000	         8.20 ns/op	       0 B/op	       0 allocs/op
    BenchmarkTooBool/invalid_bool("xxx")-4      	30000000	        44.8 ns/op	      48 B/op	       1 allocs/op
    BenchmarkTooBool/invalid_bool(1.0)-4        	20000000	        76.0 ns/op	     112 B/op	       2 allocs/op
    PASS
    
  • add time.Time">

    add "yyyy-MM-dd HH:mm:ss" string format > time.Time

    I notice that the Hugo use this project to convert the string to time.Time. When I migrate to Hugo from Hexo (another static blog generator). The Hugo said :

    page.go:750: Failed to parse date '2016-03-06 15:28:01' in page xxxxx.md

    I hope you can merge this Pull request, so that those people who migrate to Hugo from Hexo are easy to use Hugo to generate the static blog.

    Thanks. ^_^

  • Add ToTimeInDefaultLocation/E

    Add ToTimeInDefaultLocation/E

    Go's time parsing uses UTC when the format doesn't have a timezone, and has even weirder behavior when it has a zone name but no numeric offset. A caller to cast.ToTime cannot know if the returned time was explicitly in UTC or was set by default, so they cannot fix it. These new functions allow a user to supply a different timezone to default to, with nil using the local zone.

    This addresses spf13/hugo#1882 by allowing that code to use cast.ToTimeInDefaultLocation(raw, nil).

    A note on the implementation: It's not possible to use time.ParseInLocation because parsing with a format that has a named zone but no numeric offset (such as time.RC1123Z) returns a time with a 0 offset in that name iff that's not the current local timezone (!!). You can verify this by changing like 521 to use time.ParseInLocation(format.format, s, defaultLocation) and commenting out the manual override in the body of the if clause – tests will fail.

    I think the root cause is that the time pkg has two parsing functions: time.Parse and time.ParseInLocation, both of which use a private workhorse fn time.parse. That private fn takes two zones: a default and what's considered "local". I'm not sure, but I think in order to get the effect we want, we'd need to call that fn with our default and the current local zone. However, time.Parse uses default of UTC, whereas time.ParseInLocation uses the given zone for both default & local.

  • Adding ToStringMapStringSlice Method

    Adding ToStringMapStringSlice Method

    This should fix #15 and from my personal testing works. I did notice there is no test method for a ToStringMapString so I didn't add a test for ToStringMapStringSlice. Having a ToStringMapString test would have been nice to follow/modify for a TestToStringMapStringSlice function.

  • Fix multiple interface{} values to ToStringMapStringSliceE

    Fix multiple interface{} values to ToStringMapStringSliceE

    This fixes passing map[string]interface{} with the map containing multiple strings. Previously the result is a slice with an empty string in it.

    I discovered when using https://github.com/spf13/viper with the following:

    {
      "key": [
        "val1",
        "val2"
      ]
    }
    

    Viper resolved this to map[string]interface{} and the val key became []string{""}

  • Add bool case to ToStringE

    Add bool case to ToStringE

    Casting an interface{} of type bool to string was missing. So added with this PR.

    s,err := ToStringE(true)
    fmt.Printf("%q Err: %s",s,err)
    // Output: "true" Err:
    
  • 'cast.ToString()' conversion error

    'cast.ToString()' conversion error

    A strange error occurs when I use toString. The example code is as follows:

    package main
    
    import (
    	"fmt"
    	"github.com/spf13/cast"
    )
    type NewString string
    
    func main() {
    	var test NewString
    	test = "this is a test string"
    	fmt.Println("original: ",test)
    	fmt.Println("use string() func: ",string(test))
    	fmt.Println("use cast.ToString() func: ",cast.ToString(test))
    	fmt.Println(cast.ToString(test)=="")
    }
    

    Output:

    original:  this is a test string
    use string() func:  this is a test string
    use cast.ToString() func:  
    true
    
  • StringToDate: +more RFC3339 forms without TZ colon

    StringToDate: +more RFC3339 forms without TZ colon

    Adds a form to handle the common format strftime("%FT%T%z"), which omits the (optional) colon from the timezone. Also adds a matching T-omitted form.

  • Can 「cast.toString()」API Support User-defined String Type?

    Can 「cast.toString()」API Support User-defined String Type?

    https://github.com/spf13/cast/blob/2b0eb0f724e320b655240e331aef36d1175986c2/caste.go#L903 when i define a type as: type InstStatus string this cast.toString() API cannot work cause 「indirectToStringerOrErrorand」will not get the type's Elem with reflect package API, it will turn to default branch and the result is ""

  • add support to cast int64 slices

    add support to cast int64 slices

    What does this MR does?

    Create ToInt64SliceE and ToInt64Slice method to support int64 slice casting;

    How to test?

    • Run the unit tests:
    go test -v  -run TestToInt64SliceE .
    
    • Test code:
    input := []interface{}{1.2, 3.2}
    v = ToInt64Slice(input)
    fmt.Println(v)
    
  • doesn't handle named types

    doesn't handle named types

    I'd expect this test to pass:

    func TestToFloat64NamedType(t *testing.T) {
    	type X float64
    	f := ToFloat64(X(2))
    	if f != 2.0 {
    		t.Fatalf("want %#v, got %#v", 2.0, f)
    	}
    }
    

    The fix is pretty easy. Near the beginning of ToFloat64E, just after the call to indirect, add something like:

    	v := reflect.ValueOf(i)
    	if v.CanFloat() {
    		return v.Float(), nil
    	}
    

    I'm filing an issue rather than sending a PR because the proper fix applies this to all conversions, not just floats, and that's a pretty substantive change, so I wanted to discuss first.

  • cast protobuf enum return 0

    cast protobuf enum return 0

    version: 1.5 1.3

    test.proto
    
    enum N {
       DEFAULT = 0;
       ONE =  1;
    }
    
    message Test {
        N  num  = 1;
    }
    
    
    main.go
    
    a := &pb.Test{ Num:  123 }
    
    cast.toInt32(a)   //return 0
    int32(a)   //return 123
    
    
  • cast.ToInt64E(

    cast.ToInt64E("16025123132364928") return 0

    parser string to int64 should use strconv.ParseInt(trimZeroDecimal(s), 10, 64) not the follow; v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { return v, nil }

  • cast v1.5.0 bug

    cast v1.5.0 bug

    func ToUint64E(i interface{}) (uint64, error) use the v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) error:strconv.ParseInt: parsing "11166014772858028509": value out of range unable to cast "11166014772858028509" of type string to uint64

    case string: v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0) if err == nil { if v < 0 { return 0, errNegativeNotAllowed } return uint64(v), nil } return 0, fmt.Errorf("unable to cast %#v of type %T to uint64", i, i)

  • Support cast defined type

    Support cast defined type

    // You can edit this code!
    // Click here and start typing.
    package main
    
    import (
    	"fmt"
    
    	"github.com/spf13/cast"
    )
    
    type Hoge = string
    type Fuga string
    
    func main() {
    	h := Hoge("hoge")
    	f := Fuga("fuga")
    	fmt.Println(h, f)
    	fmt.Println(cast.ToString(h), cast.ToString(f))
    }
    
    // output
    hoge fuga
    hoge 
    
Related tags
Type-safe Prometheus metrics builder library for golang

gotoprom A Prometheus metrics builder gotoprom offers an easy to use declarative API with type-safe labels for building and using Prometheus metrics.

Dec 5, 2022
Generate type-safe Go converters by simply defining an interface

goverter a "type-safe Go converter" generator goverter is a tool for creating type-safe converters. All you have to do is create an interface and exec

Jan 4, 2023
Type-safe atomic values for Go

Type-safe atomic values for Go One issue with Go's sync/atomic package is that there is no guarantee from the type system that operations on an intege

Apr 8, 2022
IBus Engine for GoVarnam. An easy way to type Indian languages on GNU/Linux systems.

IBus Engine For GoVarnam An easy way to type Indian languages on GNU/Linux systems. goibus - golang implementation of libibus Thanks to sarim and haun

Feb 10, 2022
Library to work with MimeHeaders and another mime types. Library support wildcards and parameters.

Mime header Motivation This library created to help people to parse media type data, like headers, and store and match it. The main features of the li

Nov 9, 2022
Toy program for benchmarking safe and unsafe ways of saving a file

save-a-file benchmarks the many strategies an editor could use to save a file. Example output on a SSD: ext4: $ ./save-a-file ~/tmp/foo 29.195µs per s

Jan 4, 2023
Yet another Go REPL that works nicely. Featured with line editing, code completion, and more.
  Yet another Go REPL that works nicely. Featured with line editing, code completion, and more.

gore Yet another Go REPL that works nicely. Featured with line editing, code completion, and more. (Screencast taken with cho45/KeyCast) Usage gore Af

Jan 7, 2023
Lithia is an experimental functional programming language with an implicit but strong and dynamic type system.

Lithia is an experimental functional programming language with an implicit but strong and dynamic type system. Lithia is designed around a few core concepts in mind all language features contribute to.

Dec 24, 2022
Start of a project that would let people stay informed about safe running spaces in their area.

SafeRun Start of a project that would let people stay informed about safe running spaces in their area. Too many people I'm friends with feel unsafe w

Feb 11, 2022
Analyze the binary outputted by `go build` to get type information etc.

Analyze the binary outputted by go build to get type information etc.

Oct 5, 2022
A tool to generate Pulumi Package schemas from Go type definitions

MkSchema A tool to generate Pulumi Package schemas from Go type definitions. This tool translates annotated Go files into Pulumi component schema meta

Sep 1, 2022
Quickly clone an entire org/users repositories into one directory - Supports GitHub, GitLab, Bitbucket, and more
Quickly clone an entire org/users repositories into one directory - Supports GitHub, GitLab, Bitbucket, and more

ghorg ghorg allows you to quickly clone all of an orgs, or users repos into a single directory. This can be useful in many situations including Search

Jan 1, 2023
Squizit is a simple tool, that aim to help you get the grade you want, not the one you have learnt for.
Squizit is a simple tool, that aim to help you get the grade you want, not the one you have learnt for.

Squizit is a simple tool, that aim to help you get the grade you want, not the one you have learnt for. Screenshots First, input PIN Then enjoy! Hoste

Mar 11, 2022
Go implementation Welford’s method for one-pass variance computation

Variance and standard deviation caluculation using variance's algorithm Table of Contents Introduction Installation Usage Contributing License Introdu

Jun 5, 2022
Run The World. Command aggregator output. Define many services watch them in one place.

Run The World. Command aggregator output. Define many services watch them in one place.

Feb 2, 2022
K3ai Executor is the runner pod to execute the "one-click" pipelines
K3ai Executor is the runner pod to execute the

Welcome to K3ai Project K3ai is a lightweight tool to get an AI Infrastructure Stack up in minutes not days. NOTE on the K3ai origins Original K3ai Pr

Nov 11, 2021
Create one endpoint with add user functionality

hubuc-task Create one endpoint with add user functionality

Nov 13, 2021
Assemble multiple CODEOWNERS file into one

Codeowners Tool to generate a GitHub CODEOWNERS file from multiple CODEOWNERS files throughout the repo. This makes it easier to manage code ownership

Apr 11, 2022
gonewire: one wire library that uses the w1 kernel module

gonewire one wire library that uses the w1 kernel module. current support: DS18(S)20

Jan 25, 2022