strconvx
is a small Go package that defines a unified interface for converting values to and from strings using ToString
and FromString
. It also supports wrapping existing types — including those implementing encoding.TextMarshaler
or fmt.Stringer
— into a consistent StringCodec
interface.
- Unified
ToString
andFromString
interfaces - Compatible with
encoding.TextMarshaler
/TextUnmarshaler
- Dynamically wraps existing values with
strconvx.New(...)
- Support for most built-in Go scalar types (int, float, bool, etc.)
- Easy to integrate into
flag.Value
,envconfig
,url.Values
, etc.
var yesno bool
sb, err := strconvx.New(&yesno)
sb.FromString("true")
sb.ToString()
- string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128
time.Time
[]byte
When calling strconvx.New(x)
with an instance x
that is not a StringCodec
itself, nor any of the above builtin types, it will try to create a "hybrid" StringCodec instance from x
for you.
Here is how the "hybrid" StringCodec instance will be created:
- Create a hybrid instance
h
from the given instancex
; - If
x
has implemented one ofstrconvx.StringMarshaler
andencoding.TextMarshaler
,h
will use it as the implementation ofstrconvx.StringMarshaler
, i.e. theToString()
method; - If
x
has implemented one ofstrconvx.StringUnmarshaler
andencoding.TextUnmarshaler
,h
will use it as the implementation ofstrconvx.StringUnmarshaler
, i.e. theFromString()
method; - As long as
h
has an implementation of eitherstrconvx.StringMarshaler
orstrconvx.StringUnmarshaler
, we considerh
is a validStringCodec
instance. You can require both by passing in aCompleteHybrid()
option toNew
method. For a validh
,strconvx.New(x)
will returnh
. Otherwise, anErrUnsupportedType
occurs.
Example:
type Location struct {
X int
Y int
}
func (l *Location) MarshalText() ([]byte, error) {
return []byte(fmt.Sprintf("L(%d,%d)", l.X, l.Y)), nil
}
loc := &Location{3, 4}
sb, err := strconvx.New(loc) // err is nil
sb.ToString() // L(3,4)
sb.FromString("L(5,6)") // ErrNotStringUnmarshaler, "not a StringUnmarshaler"
New(v, NoHybrid())
: preventNew
from trying to create a hybrid instance fromv
at all. Instead, returnsErrUnsupportedType
.New(v, CompleteHybrid())
: still allowNew
trying to create a hybrid instance fromv
if necessary, but with the present ofCompleteHybrid()
option, the returned hybrid instance must have a valid implementation of bothFromString
andToString
.
The Namespace.Adapt()
API is used to customize the behaviour of strconvx.StringCodec
of a specific type. The principal is to create a type alias to the target type you want to override, and implement the StringCodec
interface on the new type.
When should you use this API?
- change the conversion logic of the builtin types.
- change the conversion logic of existing types that are "hybridizable", but you don't want to change their implementations.
For example, the default support of bool
type in this package uses strconv.ParseBool
method to convert strings like "true", "TRUE", "f", "0", etc. to a bool value. If you want to support also converting "YES", "NO", "はい" to a bool value, you can implement a custom bool type and register it to a Namespace
instance:
type YesNo bool
func (yn YesNo) ToString() (string, error) {
if yn {
return "yes", nil
} else {
return "no", nil
}
}
func (yn *YesNo) FromString(s string) error {
switch strings.ToLower(s) {
case "yes":
*yn = true
case "no":
*yn = false
default:
return errors.New("invalid value")
}
return nil
}
func main() {
ns := strconvx.NewNamespace()
typ, adaptor := ToAnyAdaptor(func(b *bool) (StringCodec, error) {
return (*YesNo)(b), nil
})
ns.Adapt(typ, adaptor)
var yesno bool = true
sb, err := ns.New(&yesno)
}