figure

package module
v3.1.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 27, 2024 License: MIT Imports: 13 Imported by: 14

README

Figure

Library to parse interface{}, map[string]interface{}, etc. to Go structs. Mostly used to parse configs.

Usage

package main

import "gitlab.com/distributed_lab/figure/v3"

type Config struct {
    Name string `fig:"name,required"`
    Age  int    `fig:"age"`
}

func main() {
	// parse somehow your config to variable values
	
    config := Config{}
    err := figure.
        Out(&config).// variable to parse to
        From(values).// map[string]interface{} to parse from
        Please() // parse
    if err != nil {
        panic(err)
    }
}

Parsing slices and primitive types

If your raw config []interface{} or interface{} you can use FromInterface function

Setting custom hooks

If you want to enable parsing custom types with custom rules, you can use WithHooks function

func main() {
    config := Config{}
    err := figure.
        Out(&config).
        From(values).
        WithHooks(figure.Hooks{
            "*url.URL": func(value interface{}) (reflect.Value, error) {
                switch v := value.(type) {
                case string:
                    u, err := url.Parse(v)
                    if err != nil {
                        return reflect.Value{}, errors.Wrap(err, "failed to parse url")
                    }
                    return reflect.ValueOf(u), nil
                case nil:
                    return reflect.ValueOf(nil), nil
                default:
                    return reflect.Value{}, fmt.Errorf("unsupported conversion from %T", value)
                },
            },
        }).
        Please()
    if err != nil {
        panic(err)
    }
}

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrNonZeroValue  = errors.New("you must set non zero value to this field")
	ErrRequiredValue = errors.New("you must set the value in field")
	ErrNoHook        = errors.New("no such hook")
	ErrNotValid      = errors.New("not valid value")
)
View Source
var (
	ErrUnknownAttribute      = errors.New("Unknown syntax of tag")
	ErrConflictingAttributes = errors.New("Conflict attributes")
)
View Source
var (
	// BaseHooks set of default hooks for common types
	BaseHooks = Hooks{
		"string": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToStringE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse string")
			}
			return reflect.ValueOf(result), nil
		},
		"*string": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToStringE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse string")
			}
			return reflect.ValueOf(&result), nil
		},
		"[]int64": func(value interface{}) (reflect.Value, error) {
			var a []int64

			switch v := value.(type) {
			case []int64:
				return reflect.ValueOf(value), nil
			case []int:
				for _, intValue := range v {
					a = append(a, int64(intValue))
				}
				return reflect.ValueOf(a), nil
			case []interface{}:
				for i, u := range v {
					int64Value, err := cast.ToInt64E(u)
					if err != nil {
						return reflect.Value{}, errors.Errorf("failed to cast slice element number %d: %#v of type %T into int64", i, value, value)
					}
					a = append(a, int64Value)
				}
				return reflect.ValueOf(a), nil
			case interface{}:
				int64Value, err := cast.ToInt64E(value)
				if err != nil {
					return reflect.Value{}, errors.Errorf("failed to cast %#v of type %T to int64", value, value)
				}
				return reflect.ValueOf([]int64{int64Value}), nil
			default:
				return reflect.Value{}, errors.Errorf("failed to cast %#v of type %T to []int64", value, value)
			}
		},
		"[]string": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToStringSliceE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse []string")
			}
			return reflect.ValueOf(result), nil
		},
		"int": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToIntE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse int")
			}
			return reflect.ValueOf(result), nil
		},
		"int32": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToInt32E(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse int32")
			}
			return reflect.ValueOf(result), nil
		},
		"int64": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToInt64E(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse int64")
			}
			return reflect.ValueOf(result), nil
		},
		"uint": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToUintE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse uint")
			}
			return reflect.ValueOf(result), nil
		},
		"uint32": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToUint32E(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse uint32")
			}
			return reflect.ValueOf(result), nil
		},
		"uint64": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToUint64E(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse uint64")
			}
			return reflect.ValueOf(result), nil
		},
		"float64": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToFloat64E(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse float64")
			}
			return reflect.ValueOf(result), nil
		},
		"bool": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToBoolE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse bool")
			}
			return reflect.ValueOf(result), nil
		},
		"*bool": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToBoolE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse bool")
			}
			return reflect.ValueOf(&result), nil
		},
		"time.Time": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToTimeE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse time")
			}
			return reflect.ValueOf(result), nil
		},
		"*time.Time": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToTimeE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse time pointer")
			}
			return reflect.ValueOf(&result), nil
		},
		"time.Duration": func(value interface{}) (reflect.Value, error) {
			result, err := cast.ToDurationE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse duration")
			}
			return reflect.ValueOf(result), nil
		},
		"*time.Duration": func(value interface{}) (reflect.Value, error) {
			if value == nil {
				return reflect.ValueOf(nil), nil
			}
			result, err := cast.ToDurationE(value)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse duration")
			}
			return reflect.ValueOf(&result), nil
		},
		"*big.Int": func(value interface{}) (reflect.Value, error) {
			switch v := value.(type) {
			case string:
				i, ok := new(big.Int).SetString(v, 10)
				if !ok {
					return reflect.Value{}, errors.New("failed to parse")
				}
				return reflect.ValueOf(i), nil
			case int:
				return reflect.ValueOf(big.NewInt(int64(v))), nil
			default:
				return reflect.Value{}, errors.Errorf("unsupported conversion from %T", value)
			}
		},
		"logan.Level": func(value interface{}) (reflect.Value, error) {
			switch v := value.(type) {
			case string:
				lvl, err := logan.ParseLevel(v)
				if err != nil {
					return reflect.Value{}, errors.Wrap(err, "failed to parse log level")
				}
				return reflect.ValueOf(lvl), nil
			default:
				return reflect.Value{}, errors.Errorf("unsupported conversion from %T", value)
			}
		},
		"*uint64": func(value interface{}) (reflect.Value, error) {
			switch v := value.(type) {
			case string:
				puint, err := cast.ToUint64E(v)
				if err != nil {
					return reflect.Value{}, errors.New("failed to parse")
				}
				return reflect.ValueOf(&puint), nil
			default:
				return reflect.Value{}, errors.Errorf("unsupported conversion from %T", value)
			}
		},
		"*url.URL": func(value interface{}) (reflect.Value, error) {
			switch v := value.(type) {
			case string:
				u, err := url.Parse(v)
				if err != nil {
					return reflect.Value{}, errors.Wrap(err, "failed to parse url")
				}
				return reflect.ValueOf(u), nil
			case nil:
				return reflect.ValueOf(nil), nil
			default:
				return reflect.Value{}, errors.Errorf("unsupported conversion from %T", value)
			}
		},
		"json.RawMessage": func(value interface{}) (reflect.Value, error) {
			if value == nil {
				return reflect.Value{}, nil
			}

			var params map[string]interface{}
			switch s := value.(type) {
			case map[interface{}]interface{}:
				params = make(map[string]interface{})
				for key, value := range s {
					params[key.(string)] = value
				}
			case map[string]interface{}:
				params = make(map[string]interface{})
				for key, value := range s {
					params[key] = value
				}
			default:
				return reflect.Value{}, errors.New("unexpected type while figure []json.RawMessage")
			}

			result, err := json.Marshal(params)
			if err != nil {
				return reflect.Value{}, errors.Wrap(err, "failed to parse json.RawMessage")
			}
			return reflect.ValueOf(json.RawMessage(result)), nil
		},
		"map[string]interface {}": func(value interface{}) (reflect.Value, error) {
			if value == nil {
				return reflect.Value{}, nil
			}

			var params map[string]interface{}
			switch s := value.(type) {
			case map[interface{}]interface{}:
				params = make(map[string]interface{})
				for key, value := range s {
					params[key.(string)] = value
				}
			case map[string]interface{}:
				params = s
			default:
				return reflect.Value{}, errors.New("unexpected type while figure map[string]interface{}")
			}

			return reflect.ValueOf(params), nil
		},
	}
)
View Source
var (
	// EthereumHooks set of default hooks for common types of Ethereum
	EthereumHooks = Hooks{
		"common.Address": func(value interface{}) (reflect.Value, error) {
			switch v := value.(type) {
			case string:
				if !common.IsHexAddress(v) {

					return reflect.Value{}, errors.New("invalid address")
				}
				return reflect.ValueOf(common.HexToAddress(v)), nil
			default:
				return reflect.Value{}, errors.Errorf("unsupported conversion from %T", value)
			}
		},
		"*common.Address": func(value interface{}) (reflect.Value, error) {
			switch v := value.(type) {
			case string:
				if !common.IsHexAddress(v) {

					return reflect.Value{}, errors.New("invalid address")
				}
				address := common.HexToAddress(v)
				return reflect.ValueOf(&address), nil
			default:
				return reflect.Value{}, errors.Errorf("unsupported conversion from %T", value)
			}
		},
		"*ecdsa.PrivateKey": func(raw interface{}) (reflect.Value, error) {
			switch value := raw.(type) {
			case string:
				kp, err := crypto.HexToECDSA(value)
				if err != nil {
					return reflect.Value{}, errors.Wrap(err, "failed to init keypair")
				}
				return reflect.ValueOf(kp), nil
			default:
				return reflect.Value{}, errors.Errorf("cant init keypair from type: %T", value)
			}
		},
		"*ethclient.Client": func(value interface{}) (reflect.Value, error) {
			switch v := value.(type) {
			case string:
				client, err := ethclient.Dial(v)
				if err != nil {
					return reflect.Value{}, err
				}
				return reflect.ValueOf(client), nil
			default:
				return reflect.Value{}, errors.Errorf("unsupported conversion from %T", value)
			}
		},
	}
)

Functions

This section is empty.

Types

type Figurator

type Figurator struct {
	// contains filtered or unexported fields
}

Figurator holds state for chained call

func Out

func Out(target interface{}) *Figurator

Out is main entry point for package, used to start figure out chain

func (*Figurator) From

func (f *Figurator) From(values map[string]interface{}) *Figurator

From takes raw config values to be used in figure out process

func (*Figurator) FromInterface added in v3.1.0

func (f *Figurator) FromInterface(values interface{}) *Figurator

FromInterface takes raw config values to be used in figure out process

func (*Figurator) Please

func (f *Figurator) Please() error

Please exit point for figure out chain. Will modify target partially in case of error

func (*Figurator) With

func (f *Figurator) With(hooks ...Hooks) *Figurator

With accepts hooks to be used for figuring out target from raw values. `BaseHooks` will be used implicitly if no hooks are provided

type Hook

type Hook func(value interface{}) (reflect.Value, error)

Hook signature for custom hooks. Takes raw value expected to return target value

type Hooks

type Hooks map[string]Hook

Hooks is mapping raw type -> `Hook` instance

func Merge

func Merge(manyHooks ...Hooks) Hooks

Merge does not modify any Hooks, only produces new Hooks. If duplicated keys - the value from the last Hooks with such key will be taken.

type IsZeroer

type IsZeroer interface {
	IsZero() bool
}

IsZeroer is used to check whether an object is zero to determine whether it should be omitted when marshaling with the omitempty flag. One notable implementation is time.Time.

type Tag

type Tag struct {
	Key      string
	Required bool
	NonZero  bool
}

type Validatable

type Validatable interface {
	// Validate validates the data and returns an error if validation fails.
	Validate() error
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL