Skip to content

🧣 A lightweight Go package that provides a unified interface for string conversion via ToString and FromString.

License

Notifications You must be signed in to change notification settings

ggicci/strconvx

Repository files navigation

strconvx

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.

Go codecov Go Report Card Go Reference

✨ Features

  • Unified ToString and FromString 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.

Basic API

var yesno bool
sb, err := strconvx.New(&yesno)

sb.FromString("true")
sb.ToString()

Supported Builtin Types

  • string, bool, int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, float32, float64, complex64, complex128
  • time.Time
  • []byte

The Hybrid String Codec Instance

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:

  1. Create a hybrid instance h from the given instance x;
  2. If x has implemented one of strconvx.StringMarshaler and encoding.TextMarshaler, h will use it as the implementation of strconvx.StringMarshaler, i.e. the ToString() method;
  3. If x has implemented one of strconvx.StringUnmarshaler and encoding.TextUnmarshaler, h will use it as the implementation of strconvx.StringUnmarshaler, i.e. the FromString() method;
  4. As long as h has an implementation of either strconvx.StringMarshaler or strconvx.StringUnmarshaler, we consider h is a valid StringCodec instance. You can require both by passing in a CompleteHybrid() option to New method. For a valid h, strconvx.New(x) will return h. Otherwise, an ErrUnsupportedType 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"

Hybrid Options

  1. New(v, NoHybrid()): prevent New from trying to create a hybrid instance from v at all. Instead, returns ErrUnsupportedType.
  2. New(v, CompleteHybrid()): still allow New trying to create a hybrid instance from v if necessary, but with the present of CompleteHybrid() option, the returned hybrid instance must have a valid implementation of both FromString and ToString.

Adapt/Override Existing Types

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?

  1. change the conversion logic of the builtin types.
  2. 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)
}

About

🧣 A lightweight Go package that provides a unified interface for string conversion via ToString and FromString.

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors 2

  •  
  •