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
ToStringandFromStringinterfaces - 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
hfrom the given instancex; - If
xhas implemented one ofstrconvx.StringMarshalerandencoding.TextMarshaler,hwill use it as the implementation ofstrconvx.StringMarshaler, i.e. theToString()method; - If
xhas implemented one ofstrconvx.StringUnmarshalerandencoding.TextUnmarshaler,hwill use it as the implementation ofstrconvx.StringUnmarshaler, i.e. theFromString()method; - As long as
hhas an implementation of eitherstrconvx.StringMarshalerorstrconvx.StringUnmarshaler, we considerhis a validStringCodecinstance. You can require both by passing in aCompleteHybrid()option toNewmethod. For a validh,strconvx.New(x)will returnh. Otherwise, anErrUnsupportedTypeoccurs.
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()): preventNewfrom trying to create a hybrid instance fromvat all. Instead, returnsErrUnsupportedType.New(v, CompleteHybrid()): still allowNewtrying to create a hybrid instance fromvif necessary, but with the present ofCompleteHybrid()option, the returned hybrid instance must have a valid implementation of bothFromStringandToString.
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)
}