Structures default value filling with support in almost all types of data using struct tags or struct type
This repo is inspired by go-defaults and applies the same LICENSE.
The aforementioned repo provides basic default value filling for simple data types. However,
- It does not support complex structure like
pointer
,interface
,map
orslice of map
. - It always recursively fills the struct with default value but there are cases struct filling should be skipped.
- The default filler provided is not exported which makes it hard for customization.
I created this repo to provide more data types support, more flexibility in default value filling for struct and export the function for better customization.
-
Primitive Types:
bool
int
,int8
,int16
,int32
,int64
uint
,uint8
,uint16
,uint32
,uint64
float32
,float64
[]byte
,string
-
Custom Types:
time.Duration
,time.Time
- Aliased types. e.g
type UserName string
- Self-defined types. e.g.
type User struct {Name string, Age int}
-
Complex Types:
map
,slice
,interface
,struct
-
Nested Types:
- e.g.
map[int][]*User
,[]map[int]*User
,map[int]map[int]*User
- e.g.
-
Pointer Types:
- e.g.
*int
,*User
,**int
,**User
- e.g.
-
Filling rules can be defined either by Kind or Type
If both rules found when filling a field, FuncsByKind is used first before FuncsByType -
Skip default filling for non-zero fields to prevent fields with initial value being reset
-
By default struct is recursively filled only when it is empty
Usedefault:"omit"
to always skip struct filling
Usedefault:"dive"
to always apply struct filling even when it is not empty
-
Installation:
go get github.com/sidai/defaults
-
FuncsByKind Examples:
type Role string type Admin struct { Name string Role Role `default:"admin"` } func (a *Admin) GetRole() Role { return a.Role } type User interface { GetRole() Role } type ExampleFuncsByKind struct { Int int `default:"1"` // Primitive IntPtrPtr **int `default:"1"` // Ptr type Role Role `default:"DBA"` // Alias of Primitive Duration time.Duration `default:"1s"` // Duration Time time.Time `default:"2007-07-07T07:07:07.007Z"` // Time ListOfInt []int `default:"[1,2,3,4]"` // Slice ListOfIntList [][]int `default:"[[1,2],[3,4]]"` // 2D Slice ListOfIntMap []map[int]int `default:"[{1:10,2:20},{3:30,4:40}]"` // Slice of Map Admin Admin // Struct AdminPtr *Admin // Struct Ptr AdminOmit Admin `default:"omit"` // Struct w Omit AdminWithVal Admin // Struct w Initial Value AdminWithValDive Admin `default:"dive"` // Struct w Dive User User // Interface UserWithVal User // Interface w Implementation UserWithValDive User `default:"dive"` // Interface w Implementation & Dive } ... foo := ExampleFuncsByKind{ AdminWithVal: Admin{Name: "admin1"}, AdminWithValDive: Admin{Name: "admin2"}, UserWithVal: &Admin{Name: "admin3"}, UserWithValDive: &Admin{Name: "admin4"}, } SetDefaults(&foo) foo = { "Int": 1, "IntPtrPtr": (**int) 1, "Role": "DBA", "Duration": 1s, "Time": "2007-07-07 07:07:07.007 +0000 UTC", "ListOfInt": [1, 2, 3, 4], "ListOfIntList": [[1, 2], [3, 4]], "ListOfIntMap": [{1: 10, 2: 20}, {3: 30, 4: 40}], "Admin": {"Name": "", "Role": "admin"}, // Role filled with default value "admin" for emtpy field "AdminPtr": (*Admin) {"Name": "", "Role": "admin"}, // Role filled with default value "admin" for emtpy pointer field "AdminOmit": {"Name": "", "Role": ""}, // Role not filled even when AdminOmit is emtpy since "omit" tag found "AdminWithVal": {"Name": "admin1", "Role": ""}, // Role not filled since AdminWithVal field is not empty "AdminWithValDive": {"Name": "admin2", "Role": "admin"}, // Role filled even when AdminWithValDive field is not empty since "dive" tag found "User": nil, // User not filled no implementation found "UserWithVal": (*Admin) {"Name": "admin3", "Role": ""}, // User Role not filled since implementation UserWithVal is not empty "UserWithValDive": (*Admin) {"Name": "admin4", "Role": "admin"} // User Role filled since implementation UserWithValDive has "dive" tag }
-
FuncsByType Examples:
type Enum string type DefaultData struct { String string Int int } type ExampleFuncsByType struct { Enum Enum EnumWithTag Enum `default:"tag"` EnumWithValueNTag Enum `default:"tag"` DefaultData DefaultData DefaultDataOmit DefaultData `default:"omit"` DefaultDataWithVal DefaultData DefaultDataWithValDive DefaultData `default:"dive"` } foo := ExampleFuncsByType{ EnumWithValueNTag: Enum("value"), DefaultDataWithVal: DefaultData{Int: 1}, DefaultDataWithValDive: DefaultData{Int: 1}, } RegisterDefaultType(Enum("type")) RegisterDefaultType(DefaultData{String: "type", Int: 7}) SetDefaults(&foo) ... foo = { "Enum": "type", // Use FuncsByType "EnumWithTag": "tag", // Use tag as FuncsByKind has higher precedence "EnumWithValueNTag": "value", // No filling applied as value is not empty "DefaultData": {String: "type", Int: 7}, // Use FuncsByType "DefaultDataOmit": {String: "", Int: 0}, // Omit tag works for FuncsByType "DefaultDataWithVal": {String: "", Int: 1}, // FuncsByType skip filling when value is not empty "DefaultDataWithValDive": {String: "", Int: 1} // FuncsByType ignores dive tag as it works on the extra type only }
-
More Examples Here