Giter Site home page Giter Site logo

defaults's Introduction

defaults

CircleCI codecov GitHub release License

Initialize structs with default values

  • Supports almost all kind of types
    • Scalar types
      • int/8/16/32/64, uint/8/16/32/64, float32/64
      • uintptr, bool, string
    • Complex types
      • map, slice, struct
    • Nested types
      • map[K1]map[K2]Struct, []map[K1]Struct[]
    • Aliased types
      • time.Duration
      • e.g., type Enum string
    • Pointer types
      • e.g., *SampleStruct, *int
  • Recursively initializes fields in a struct
  • Dynamically sets default values by defaults.Setter interface
  • Preserves non-initial values from being reset with a default value

Usage

package main

import (
	"encoding/json"
	"fmt"
	"math/rand"

	"github.com/creasty/defaults"
)

type Gender string

type Sample struct {
	Name    string `default:"John Smith"`
	Age     int    `default:"27"`
	Gender  Gender `default:"m"`
	Working bool   `default:"true"`

	SliceInt    []int    `default:"[1, 2, 3]"`
	SlicePtr    []*int   `default:"[1, 2, 3]"`
	SliceString []string `default:"[\"a\", \"b\"]"`

	MapNull            map[string]int          `default:"{}"`
	Map                map[string]int          `default:"{\"key1\": 123}"`
	MapOfStruct        map[string]OtherStruct  `default:"{\"Key2\": {\"Foo\":123}}"`
	MapOfPtrStruct     map[string]*OtherStruct `default:"{\"Key3\": {\"Foo\":123}}"`
	MapOfStructWithTag map[string]OtherStruct  `default:"{\"Key4\": {\"Foo\":123}}"`

	Struct    OtherStruct  `default:"{\"Foo\": 123}"`
	StructPtr *OtherStruct `default:"{\"Foo\": 123}"`

	NoTag    OtherStruct // Recurses into a nested struct by default
	NoOption OtherStruct `default:"-"` // no option
}

type OtherStruct struct {
	Hello  string `default:"world"` // Tags in a nested struct also work
	Foo    int    `default:"-"`
	Random int    `default:"-"`
}

// SetDefaults implements defaults.Setter interface
func (s *OtherStruct) SetDefaults() {
	if defaults.CanUpdate(s.Random) { // Check if it's a zero value (recommended)
		s.Random = rand.Int() // Set a dynamic value
	}
}

func main() {
	obj := &Sample{}
	if err := defaults.Set(obj); err != nil {
		panic(err)
	}

	out, err := json.MarshalIndent(obj, "", "	")
	if err != nil {
		panic(err)
	}
	fmt.Println(string(out))

	// Output:
	// {
	// 	"Name": "John Smith",
	// 	"Age": 27,
	// 	"Gender": "m",
	// 	"Working": true,
	// 	"SliceInt": [
	// 		1,
	// 		2,
	// 		3
	// 	],
	// 	"SlicePtr": [
	// 		1,
	// 		2,
	// 		3
	// 	],
	// 	"SliceString": [
	// 		"a",
	// 		"b"
	// 	],
	// 	"MapNull": {},
	// 	"Map": {
	// 		"key1": 123
	// 	},
	// 	"MapOfStruct": {
	// 		"Key2": {
	// 			"Hello": "world",
	// 			"Foo": 123,
	// 			"Random": 5577006791947779410
	// 		}
	// 	},
	// 	"MapOfPtrStruct": {
	// 		"Key3": {
	// 			"Hello": "world",
	// 			"Foo": 123,
	// 			"Random": 8674665223082153551
	// 		}
	// 	},
	// 	"MapOfStructWithTag": {
	// 		"Key4": {
	// 			"Hello": "world",
	// 			"Foo": 123,
	// 			"Random": 6129484611666145821
	// 		}
	// 	},
	// 	"Struct": {
	// 		"Hello": "world",
	// 		"Foo": 123,
	// 		"Random": 4037200794235010051
	// 	},
	// 	"StructPtr": {
	// 		"Hello": "world",
	// 		"Foo": 123,
	// 		"Random": 3916589616287113937
	// 	},
	// 	"NoTag": {
	// 		"Hello": "world",
	// 		"Foo": 0,
	// 		"Random": 6334824724549167320
	// 	},
	// 	"NoOption": {
	// 		"Hello": "",
	// 		"Foo": 0,
	// 		"Random": 0
	// 	}
	// }
}

defaults's People

Contributors

boynchan avatar creasty avatar hrogge avatar licaonfee avatar rfyiamcool avatar sdghchj avatar spectrumqt avatar zenthangplus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

defaults's Issues

Unset()

hi, could you consider implementing an Unset() to zero fields at their default value so they can then be omitted when marshalling?

bug: not set default for fields of a pointer struct field in an object.

version: 1.3.0

type Child struct {
	Name string `default:"Tom"`
	Age  int    `default:"20"`
}

type Parent struct {
	Child *Child
}

func TestPointerStructMember(t *testing.T) {
	m := Parent{Child: &Child{Name: "Jim"}}
	Set(&m)
	if m.Child.Age != 20 {
		t.Errorf("20 is expected")   //will fail here
	}
}

Default value to the entry that key already exists in a map

Hi, thank you for making and maintaining such an amazing package. It helps me a lot!

I'm facing an issue that is not able to set default values within a map[string]struct{}. I'd like to set the default value to the entry that key already exists in a map. But it doesn't work well:

package main

import (
	"fmt"

	"github.com/creasty/defaults"
)

type T struct {
	Field string `default:"value-1"`
}

type Foo struct {
	Map map[string]T
	T   T
}

func main() {
	f := &Foo{
		Map: map[string]T{
			// I want to set the default value here.
			"key-1": T{},
		},
	}
	if err := defaults.Set(f); err != nil {
		panic(err)
	}
	fmt.Printf("field in map: %q\n", f.Map["key-1"].Field)
	fmt.Printf("field in struct: %q\n", f.T.Field)

	// Output:
	//
	// field in map: ""
	// field in struct: "value-1"
}

Currently, I workaround like:

	for k, v := range f.Map {
		_ = defaults.Set(&v)
		f.Map[k] = v
	}

Is there another way to set a default value for a value that already has a key in the map?

Thanks!

bug: []time.Duration not works

Example struct

type MyStruct struct {
    BackoffPolicy []time.Duration `default:"...."`
}

Checked variants:

  • default:"{[\"10s\",\"1m\"]}"
  • default:"[\"10s\",\"1m\"]"
  • default:"[10s,1m]"
  • default:"\"10s\",\"1m\""
  • default:"10s,1m"

all of them not works

Add require field tag

It would be fantastic if this library had the option to set a struct field as required. Then when defaults.Set is called on a struct with zero value fields that are marked as required the function returns an error.

Default value for slice of struct doesn't work for golang v 1.12

package controllers

import (
	"fmt"

	"github.com/creasty/defaults"
)

func main() {

	foo := &Parent{}
	if err := defaults.Set(foo); err != nil {
		fmt.Println(err)
	}
	fmt.Print(foo)
}

type Child struct {
	Name string
	Age  int `default:"33"`
}

type Parent struct {
	children []Child
}

output: &{[]}

Wrong example in Readme?

I see the following code.

	StructPtr *OtherStruct `default:"{\"Foo\": 123}"`

It may be wrong, because OtherStruct is not an integer.

Recurse into nested and embedded structs automatically

I seems that you need to tag struct fields with default:"{}" to recurse into them. Should this be the default, even with no "default" tag? You can opt out with default:"-". That follows the conventions of most packages which do similar tag-based stuff.

So, for example:

type DBConfig struct {
  URL string `default:"pg://localhost:5432/db"`
}

type Config struct {
  DBConfig
}

var cfg Config
defaults.Set(&cfg)

That should recurse into the embedded DBConfig.

Default true on bool does not allow to set false

I explain myself since the title might looks a bit rubbish. Basically I have a yaml file (not official one)

foo:
   bar: false # You can play of changing this to true / false

I have my configuration in go:

type Foo struct {
  Bar bool `default:"true" yaml:"bar"`
}

when I do run my defaults I have something like this:

	if err := defaults.Set(myConfig); err != nil {
		panic(err)
	}
       fmt.Printf("Show me the value: %v", myConfig.Bar)
       // The result it always show: Show me the value: true

I don't have any issue with any other default except the boolean type.

Note: I didn't try to run the code currently here, but it represents pretty much what it looks like in my code. I have my code in a not so public repo.

Unable to keep zero value in case of setting the zero value on purpose

Hi @creasty, thanks for the excellent package. I faced a typical case but am unsure how to use your package to address that.

In the case of the field of type bool, the go default bool value is false - boolean zero value. If I want to set the default value to true with your defaults package, it works as expected in case no value was set to that field. If the value for that field is set to zero value on purpose (bool zero is false), the defaults.Set() always override that field value with the configured value regardless of the original value.

package main

import (
	"fmt"
	"github.com/creasty/defaults"
)

type Sample struct {
	Hello  string `default:"world"` // Tags in a nested struct also work
	Foo    int    `default:"-"`
	Random int    `default:"-"`
	Bool   bool   `default:"true"`
}

func main() {
	obj := &Sample{Hello: "hello", Bool: false}
	if err := defaults.Set(obj); err != nil {
		panic(err)
	}
	fmt.Println(obj)
}

I tried with defaults.CanUpdate(obj.Bool) too, but it looks like it checks for the zero value of type and compares with the current set value, which is already a zero value, hence not have any meaning here since the set value is zero value.

In case of check with defaults.CanUpdate(obj), all the values will not be set since obj.Hello is not zero value of string, and that cause obj.Bool will not be set to true in case it's not yet set.

play: https://play.golang.org/p/5LoqUH5d1wj

no errors if default value doesn't parse

If the value in the default tag is not parseable as the type of the field, there is no error returned. Is this intended? For example:

type Widget struct {
  Colors []string `env:"[red,blue]"`
}

func main() {
  var c Colors
  err := defaults.Set(&c)

  fmt.Println("err: ", err) // nothing printed, no error

  fmt.Println(c.Colors) // nothing printed, tag value was not valid JSON, so parsing silently failed
}

Default values map[string][]string for struct

Hi Team !

i have tried with map[string][]string but not work. does have support it?

type HttpVar struct {
        AttrHeader 		map[string]string `default:"[]string{\"foo\":\"test\"}"`
	Wrapper 			interface{}
}

Struct tag boolean values are not parsed properly when specified with double quotes

The README shows this example in the Sample struct:

...
Working bool   `default:"true"`
...

When running the tests in the defaults_test.go boolean values are not properly being parsed when specified in double quotes above. The following pattern passes:

...
Working bool   `default:true`
...

However this is not a preferred way to specify tags in yaml. I'm validating whether this is based on a recent change to the defaults library behavior or if it is related to a specific go version. If I can validate the isssue, I may open a PR related to this soon.

Support unmarshalling custom types.

It's often a demand to use defaults for complex types from 3rd party libraries.
Generally those types support text.Unmarshaller interface to parse from text.

Let's support that.

Panic when attempting to initialize a field which references an interface

type Thingy struct {
  Context context.Context
}
func (obj *Thingy) SetDefaults() {
  if defaults.CanUpdate(obj.Context) { // panic
    obj.Context = context.Background()
  }
}
panic: reflect: call of reflect.Value.Type on zero Value

reflect.Value.typeSlow
	/usr/local/go/src/reflect/value.go:2634
reflect.Value.Type
	/usr/local/go/src/reflect/value.go:2629
github.com/creasty/defaults.isInitialValue
	/usr/root/function/vendor/github.com/creasty/defaults/defaults.go:221
github.com/creasty/defaults.CanUpdate
	/usr/root/function/vendor/github.com/creasty/defaults/defaults.go:243
function/internal/common/utils.(*InvokeOpts).SetDefaults
	/usr/root/function/internal/common/utils/invoke.go:33
github.com/creasty/defaults.callSetter
	/usr/root/function/vendor/github.com/creasty/defaults/setter.go:10
github.com/creasty/defaults.Set
	/usr/root/function/vendor/github.com/creasty/defaults/defaults.go:42
// creasty/defaults/defaults.go#220
func isInitialValue(field reflect.Value) bool {
  return reflect.DeepEqual(reflect.Zero(field.Type()).Interface(), field.Interface())
}

How to support slice of struct?

For example:

type confModule struct {
	Name   string   `yaml:"name" default:"abc"`
	Params []string `yaml:"params" default:"[\"-c\", \"conf.yml\"]"`
}

type confStruct struct {
	ID     string       `yaml:"id"`
	Modules []confModule `yaml:"modules"`
}

defaults will skip the slice of struct (confStruct.Modules), how to support this case?

Booleans and defaults seems not to be working

Hi, I'm trying to use this library but I'm facing an issue setting defaults values for a boolean field

In my example, I am setting a boolean default value to true for the Enabled field but when passing a false value it turns to true

package main

import (
	"fmt"
	"github.com/creasty/defaults"
)

type Sample struct {
	Enabled bool `default:"true"`
}

func main() {
	obj := &Sample{Enabled: false}
	if err := defaults.Set(obj); err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", obj)
}

Output: &{Enabled:true}

am I missing something?

Here is a playground where you can test it: https://play.golang.org/p/RIeiduC2c8C

The Setter interface only works on struct pointer fields, not the struct itself

package test

import (
	"github.com/creasty/defaults"
	"testing"
)

type Main struct {
	MainInt int `default:"-"`
	*Other  `default:"{}"`
}

type Other struct {
	OtherInt int `default:"-"`
}

func (s *Main) SetDefaults() {
	if defaults.CanUpdate(s.MainInt) {
		s.MainInt = 1
	}
}

func (s *Other) SetDefaults() {
	if defaults.CanUpdate(s.OtherInt) {
		s.OtherInt = 1
	}
}

func TestDefaultsSetter(t *testing.T) {
	main := &Main{}
	defaults.Set(main)
	if main.OtherInt != 1 {
		t.Errorf("expected 1 for OtherInt, got %d", main.OtherInt)
	}
	if main.MainInt != 1 {
		t.Errorf("expected 1 for MainInt, got %d", main.MainInt)
	}
}

Which outputs:

=== RUN   TestDefaultsSetter
    TestDefaultsSetter: dev_test.go:36: expected 1 for MainInt, got 0
--- FAIL: TestDefaultsSetter (0.00s)
FAIL

Process finished with exit code 1

nil slices, maps, and pointers are initialized to non-nil values

If a struct has map, slice, or pointer fields which have an initial value of nil, Set() will set them to empty (zero) values, even if the field has no "default" tag. See this test case:

type TT struct{}

type SS struct {
	Ptr *TT
	Slice []string
	Map map[string]interface{}
}

func TestEmptySlice(t *testing.T) {
	s := SS{}
	err := Set(&s)
	if err != nil {
		t.Fatalf("Set() returned error: %#v", err)
	}

	if s.Ptr != nil {
		t.Error("Set() initialized nil pointer")
	}

	if s.Slice != nil {
		t.Error("Set() initialized nil slice")
	}

	if s.Map != nil {
		t.Error("Set() initialized nil map")
	}
}

Seems like, if the field has no "default" tag, and there is nothing to recurse into (because the value is nil), it should be left unchanged.

I think this was a side effect of the last change for recursing into nested values with no "default" tag.

Add MustSet() function

Please consider to add a MustSet function that calls Set() function and panics in case of error.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.