dstruct

package module
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2025 License: MIT Imports: 12 Imported by: 4

README

Notice

  • Please do not install any of these versions: v1.1.1 v1.1.0 v1.0.0 v0.1.2 v0.1.1 as these were removed from the repo - (but are still available at pkg.go.dev).
  • When installing please explicitly install the actual latest version of dstruct which is currently v0.6.0.

dstruct

A golang package that allows one to create, modify and generate structs dynamically.

Features:

  • Building structs at runtime
  • Extending existing struct at runtime
  • Merging multiple structs at runtime
  • Adding new fields into struct
  • Removing existing fields from struct
  • Modifying field values in structs
  • Reading field values in structs
  • Generating struct values

Limitations:

  • You cannot extend structs with unexported embedded fields.
  • If a struct pointer cannot be fully dereferenced then the struct's subfields won't be added to the dynamic struct. This is done mainly to avoid self-referencing structs as these will create infinite node trees.
  • Dynamic structs with struct fields of type any (interface {}) cannot be created. If you try extend or merge structs which have struct fields of type any their value must be set to a concrete type.

Sections

Install

go get github.com/MartinSimango/dstruct@v0.6.0

How it works?

Dstruct uses a tree to represent dynamic structs which allows these structs to easily to be manipulated. Nodes and their children represent struct fields and their subfields respectively. Once the tree structure of the struct is created the tree is converted into a dynamic struct using the reflect package.

Dstruct has 3 main interfaces that are implemented in order to allow these features:

  1. dstruct.Builder is responsible for adding and removing fields from a struct.
type Builder interface {
    AddField(name string, value interface{}, tag string) Builder
    AddEmbeddedField(value interface{}, tag string) Builder
    Build() DynamicStructModifier
    GetField(name string) Builder
    GetFieldCopy(field string) Builder
    NewBuilderFromField(field string) Builder
    RemoveField(name string) Builder
}
  1. dstruct.DynamicStructModifier is responsible for reading and editing fields with the struct as well as storing the actual struct.

type DynamicStructModifier interface {
    Instance() any
    New() any
    Get(field string) (any, error)
    Set(field string, value any) error
    GetFields() map[string]field
}

  1. dstruct.GeneratedStruct is responsible for generating struct values and is an extension of the DynamicStructModifier. A generated struct values are randomly generation based on Generation functions.
type GeneratedStruct interface {
    DynamicStructModifier
    Generate()
    GetFieldGenerationConfig(field string) *generator.GenerationConfig
    SetFieldGenerationConfig(field string, generationConfig *generator.GenerationConfig) error
}

Using the Builder


type Person struct {
	Name string
	Age  int
}

func main() {
	structBuilder := dstruct.NewBuilder().
		AddField("Person", Person{Name: "Martin", Age: 25}, `json:"person"`).
		AddField("Job", "Software Developer", "").
		RemoveField("Person.Age")

	fmt.Printf("Struct: %+v\n", structBuilder.Build().Instance())
}

Output

$ Struct: {Person:{Name:Martin} Job:Software Developer}

Using the Modifier


type Person struct {
	Name string
	Age  int
}

func main() {
	structBuilder := dstruct.NewBuilder().
		AddField("Person", Person{Name: "Martin", Age: 25}, `json:"person"`).
		AddField("Job", "Software Developer", "")

	structModifier := structBuilder.Build()
	structModifier.Set("Person.Name", "Martin Simango")
	structModifier.Set("Job", "Software Engineer")

	name, _ := structModifier.Get("Person.Name")

	fmt.Printf("New name: %s\n", name.(string))
	fmt.Printf("Struct: %+v\n", structModifier.Instance())
}

Output

$ New name: Martin Simango
$ Struct: {Person:{Name:Martin Simango Age:25} Job:Software Engineer}

Using the Struct Generator


type Person struct {
	Name string
	Age  int
}

func main() {
	structBuilder := dstruct.NewBuilder().
		AddField("Person", Person{Name: "Martin", Age: 25}, `json:"person"`).
		AddField("Job", "Software Developer", "")

		

	strct := structBuilder.Build().Instance()

	generatedStruct := dstruct.NewGeneratedStruct(strct)
	// change the age to be between 50 and 60
	generatedStruct.GetFieldGenerationConfig("Person.Age").SetIntMin(50).SetIntMax(60)
	generatedStruct.Generate()
	fmt.Printf("Struct with age between 50 and 60: %+v\n", generatedStruct.Instance())
	// change the age to be between 10 and 20
	generatedStruct.GetFieldGenerationConfig("Person.Age").SetIntMin(10).SetIntMax(20)
	generatedStruct.Generate()
	fmt.Printf("Struct with age between 10 and 20: %+v\n", generatedStruct.Instance())

}

Output:

$ Struct with age between 50 and 60: {Person:{Name:string Age:59} Job:string}
$ Struct with age between 10 and 20: {Person:{Name:string Age:16} Job:string}

Extending a struct

type Address struct {
	Street string
}

type Person struct {
	Name    string
	Age     int
	Address Address
}

func main() {
	strct := dstruct.ExtendStruct(Person{
		Name: "Martin",
		Age:  25,
		Address: Address{
			Street: "Alice Street",
		},
	})
	strct.GetField("Address").AddField("StreetNumber", 1, "")

	fmt.Printf("Extended struct: %+v\n", strct.Build().Instance())

}

Output:

$ Extended struct: {Name:Martin Age:25 Address:{Street:Alice Street StreetNumber:1}}

Merging structs

type PersonDetails struct {
	Age    int
	Height float64
}

type PersonName struct {
	Name    string
	Surname string
}

func main() {
	strct, _ := dstruct.MergeStructs(PersonDetails{Age: 0, Height: 190.4}, PersonName{Name: "Martin", Surname: "Simango"})
	fmt.Printf("Merged structs: %+v\n", strct)
}

Output:

$ Merged structs: {Age:0 Height:190.4 Name:Martin Surname:Simango}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ExtendStruct

func ExtendStruct(val any) *treeBuilderImpl

func MergeStructs

func MergeStructs(strcts ...interface{}) (a any, err error)

MergeStructs merges two structs

func NewBuilder

func NewBuilder() *treeBuilderImpl

Types

type Builder

type Builder interface {
	// AddField adds a field to the struct.
	AddField(name string, value interface{}, tag string) Builder

	// AddEmbeddedFields adds an embedded field to the struct.
	AddEmbeddedField(value interface{}, tag string) Builder

	// Build returns a DynamicStructModifier instance.
	Build() DynamicStructModifier

	// GetField returns a builder instance of the subfield of the struct that is currently being built. It will panic if the
	// field is not a struct or does not fully dereference to a struct value. It returns nil if field does not exist.
	GetField(name string) Builder

	// GetNewBuilderFromField returns a new builder instance where the subfield of the struct "field" is the root of the struct.
	NewBuilderFromField(field string) Builder

	// RemoveField removes a field from the struct. If the field is a subfield of a nil struct it will not be removed.
	RemoveField(field string) Builder
}

type CustomType added in v0.1.0

type CustomType struct {
	Value          any
	FunctionHolder core.FunctionHolder
}

CustomType allows types with the same type of 'Value' to have a specific generation function that is stored by the 'FunctionHolder' field. This is useful for types that are not supported by the default generation functions.

type DStructGeneratedStruct added in v0.1.0

type DStructGeneratedStruct[T any] struct {
	*DynamicStructModifierImpl
	// contains filtered or unexported fields
}

DStructGeneratedStruct implements GeneratedStruct.

func NewGeneratedStruct

func NewGeneratedStruct[T any](val T) *DStructGeneratedStruct[T]

func NewGeneratedStructWithConfig

func NewGeneratedStructWithConfig[T any](val T,
	cfg config.Config,
	settings config.GenerationSettings,
	customTypes ...CustomType,
) *DStructGeneratedStruct[T]

func (*DStructGeneratedStruct[T]) AddCustomType added in v0.1.0

func (gs *DStructGeneratedStruct[T]) AddCustomType(customType CustomType)

func (*DStructGeneratedStruct[T]) Generate added in v0.1.0

func (gs *DStructGeneratedStruct[T]) Generate()

Generate implements GeneratedStruct.Generate.

func (*DStructGeneratedStruct[T]) GetFieldGenerationConfig added in v0.1.0

func (gs *DStructGeneratedStruct[T]) GetFieldGenerationConfig(field string) (config.Config, error)

func (*DStructGeneratedStruct[T]) GetFieldGenerationConfig_ added in v0.1.0

func (gs *DStructGeneratedStruct[T]) GetFieldGenerationConfig_(field string) config.Config

GetFieldGenerationConfig_ implements GeneratedStruct.

func (*DStructGeneratedStruct[T]) GetFieldGenerationSettings added in v0.1.0

func (gs *DStructGeneratedStruct[T]) GetFieldGenerationSettings(
	field string,
) (config.GenerationSettings, error)

GetFieldGenerationSettings implements GeneratedStruct.GetFieldGenerationSettings

func (*DStructGeneratedStruct[T]) GetFieldGenerationSettings_ added in v0.1.0

func (gs *DStructGeneratedStruct[T]) GetFieldGenerationSettings_(
	field string,
) config.GenerationSettings

GetFieldGenerationSettings_ implements GeneratedStruct.

func (*DStructGeneratedStruct[T]) GetGenerationConfig added in v0.1.0

func (gs *DStructGeneratedStruct[T]) GetGenerationConfig() config.Config

GetGenerationConfig implements GeneratedStruct.GetGenerationConfig

func (*DStructGeneratedStruct[T]) GetGenerationSettings added in v0.1.0

func (gs *DStructGeneratedStruct[T]) GetGenerationSettings() config.GenerationSettings

GetGenerationSettings implements GeneratedStruct.GetGenerationSettings

func (*DStructGeneratedStruct[T]) InstanceT added in v0.1.0

func (gs *DStructGeneratedStruct[T]) InstanceT() T

InstanceT returns the instance of the generated struct.

func (*DStructGeneratedStruct[T]) NewT added in v0.1.0

func (gs *DStructGeneratedStruct[T]) NewT() *T

NewT returns a new instance of the generated struct

func (*DStructGeneratedStruct[T]) Set added in v0.1.0

func (gs *DStructGeneratedStruct[T]) Set(field string, value any) error

func (*DStructGeneratedStruct[T]) SetFieldFromTaskInstance added in v0.1.0

func (gs *DStructGeneratedStruct[T]) SetFieldFromTaskInstance(
	field string,
	taskInstance generator.TaskInstance,
) error

SetFieldFromTaskInstance implements GeneratedStruct.SetFieldFromTaskInstanc

func (*DStructGeneratedStruct[T]) SetFieldGenerationConfig added in v0.1.0

func (gs *DStructGeneratedStruct[T]) SetFieldGenerationConfig(
	field string,
	cfg config.Config,
) error

SetFieldGenerationConfig implements GeneratedStruct.SetFieldGenerationConfig

func (*DStructGeneratedStruct[T]) SetFieldGenerationFunction added in v0.1.0

func (gs *DStructGeneratedStruct[T]) SetFieldGenerationFunction(field string,
	functionHolder core.FunctionHolder,
) error

SetFieldGenerationFunction implements GeneratedStruct.SetFieldGenerationFunction

func (*DStructGeneratedStruct[T]) SetFieldGenerationFunctions added in v0.1.0

func (gs *DStructGeneratedStruct[T]) SetFieldGenerationFunctions(
	field string,
	functions core.DefaultGenerationFunctions,
) error
func (gs *DStructGeneratedStruct[T]) GetFieldGenerationFunction_(field string) core.FunctionHolder {
	if err := gs.assertFieldExists(field); err != nil {
		panic(err)
	}

	if gs.fieldContexts[field] == nil {
		panic(fmt.Errorf("field %s does not have a generation function", field))
	}

	return gs.fieldContexts[field].GeneratedField.GenerationFunction
}

SetFieldGenerationFunctions implements GeneratedStruct.SetFieldGenerationFunctions

func (*DStructGeneratedStruct[T]) SetFieldGenerationSettings added in v0.1.0

func (gs *DStructGeneratedStruct[T]) SetFieldGenerationSettings(
	field string,
	settings config.GenerationSettings,
) error

SetFieldGenerationSettings implements GeneratedStruct.SetFieldGenerationSettings

func (*DStructGeneratedStruct[T]) SetGenerationConfig added in v0.1.0

func (gs *DStructGeneratedStruct[T]) SetGenerationConfig(cfg config.Config)

SetGenerationConfig implements GeneratedStruct.SetGenerationConfig

func (*DStructGeneratedStruct[T]) SetGenerationFunctions added in v0.1.0

func (gs *DStructGeneratedStruct[T]) SetGenerationFunctions(
	functions core.DefaultGenerationFunctions,
)

SetGenerationFunctions implements GeneratedStruct.SetGenerationFunctions

func (*DStructGeneratedStruct[T]) SetGenerationSettings added in v0.1.0

func (gs *DStructGeneratedStruct[T]) SetGenerationSettings(
	settings config.GenerationSettings,
)

SetGenerationSettings implements GeneratedStruct.SetGenerationSettings

type DynamicStructModifier

type DynamicStructModifier interface {
	// Instance returns a copy of the struct
	Instance() any

	// New returns a pointer to the struct
	New() any

	// Get gets the value of the struct field `field` and returns an error if the field is not found
	Get(field string) (any, error)

	// Get_ gets the value of the struct field `field` and panics if the field is not found
	Get_(field string) any

	// Set sets the value of the struct field `field` and returns an error if the field is not found.
	//
	// The program will panic if the type of value does not match the type of the struct field `field`.
	Set(field string, value any) error

	// GetFields returns a map containing all fields within a struct
	GetFields() map[string]StructField

	// Update updates the struct's underlying tree to represent that of the strct's value.
	// The structs underlying tree can change if new fields are added due to fields within the struct changing from
	// nil to become not nil. This can lead to new additional fields being introduced within the struct
	Update()

	// Apply is a combination of Set and Update. Update is not called if Apply fails.
	Apply(field string, value any) error
}

type DynamicStructModifierImpl

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

func (*DynamicStructModifierImpl) Apply added in v0.1.0

func (dm *DynamicStructModifierImpl) Apply(field string, value any) error

func (*DynamicStructModifierImpl) Get

func (dm *DynamicStructModifierImpl) Get(field string) (any, error)

func (*DynamicStructModifierImpl) GetFields added in v0.1.0

func (dm *DynamicStructModifierImpl) GetFields() map[string]StructField

func (*DynamicStructModifierImpl) Get_ added in v0.1.0

func (dm *DynamicStructModifierImpl) Get_(field string) any

func (*DynamicStructModifierImpl) Instance

func (dm *DynamicStructModifierImpl) Instance() any

func (*DynamicStructModifierImpl) New

func (dm *DynamicStructModifierImpl) New() any

func (*DynamicStructModifierImpl) Set

func (dm *DynamicStructModifierImpl) Set(field string, value any) error

func (*DynamicStructModifierImpl) String

func (dm *DynamicStructModifierImpl) String() string

func (*DynamicStructModifierImpl) Update added in v0.1.0

func (dm *DynamicStructModifierImpl) Update()

type FieldData added in v0.1.0

type FieldData map[string]StructField

type FieldModifier

type FieldModifier func(*StructField)

type FieldNode added in v0.1.0

type FieldNode map[string]*Node[StructField]

type GeneratedFieldContexts added in v0.1.0

type GeneratedFieldContexts map[string]*core.GeneratedFieldContext

type GeneratedStruct

type GeneratedStruct interface {
	DynamicStructModifier
	// Generate generates fields for the struct. If new fields are generated, the root tree for the underlying
	// struct is updated. This allows new generated fields to be accessed and modified by Set and Get methods
	Generate()

	// SetFieldGenerationSettings sets the generation value config for field within the struct.
	// If the field does not exist or if the field has no generation settings an error is returned.
	SetFieldGenerationSettings(
		field string,
		settings config.GenerationSettings,
	) error

	// GetFieldGenerationSettings gets the generation config for field within the struct.
	// If the field does not exist or if the field has no generation settings an error is returned.
	GetFieldGenerationSettings(field string) (config.GenerationSettings, error)

	// GetFieldGenerationSettings_ is like GetFieldGenerationSettings but panics if an error occurs.
	GetFieldGenerationSettings_(field string) config.GenerationSettings

	SetGenerationSettings(settings config.GenerationSettings)

	// GetGenerationSettings gets the generation settings for the struct.
	GetGenerationSettings() config.GenerationSettings

	// SetFieldGenerationConfig sets the generation config for field within the struct. It returns
	// an error if the field does not exist or if the field cannot be generated.
	// Fields that can be generated are struct fields of the most basic type i.e a struct fields
	// that are structs cannot be generated, however it's fields can be.
	//
	// Fields types that cannot be generated: structs, func, chan, any (will default to a nil value being generated).
	//
	// Note: Pointers to structs can be generated.
	SetFieldGenerationConfig(field string, generationConfig config.Config) error

	// GetFieldGenerationConfig gets the generation config for field within the struct.
	GetFieldGenerationConfig(field string) (config.Config, error)

	// GetFieldGenerationConfig_ is like GetFieldGenerationConfig but panics if an error occurs.
	GetFieldGenerationConfig_(field string) config.Config

	// SetGenerationConfig sets the generation config for the struct and propagates the settings to all fields
	SetGenerationConfig(config config.Config)

	// GetGenerationConfig gets the generation config for the struct.
	GetGenerationConfig() config.Config

	// SetFieldGenerationFunction sets the generation function for field within the struct. It returns an error if the field does not exist or if the field cannot be generated.
	SetFieldGenerationFunction(field string, functionHolder core.FunctionHolder) error

	// // GetFieldGenerationConfig gets the generation function for field within the struct.
	// GetFieldGenerationFunction(field string) (core.FunctionHolder, error)
	//
	// // GetFieldGenerationFunction_ is like GetFieldGenerationFunction but panics if an error occurs.
	// GetFieldGenerationFunction_(field string) core.FunctionHolder
	//
	// SetFieldDefaultFunctions sets the default generation functions for field within the struct. It returns an error if the field does not exist or if the field cannot be generated.
	SetFieldGenerationFunctions(field string, functions core.DefaultGenerationFunctions) error

	// SetGenerationFunctions sets the generation functions for the struct and propagates the settings to all fields
	SetGenerationFunctions(functions core.DefaultGenerationFunctions)

	// SetFieldFromTaskInstance sets the field value from the task instance. The task is used to generate the value for the field.
	SetFieldFromTaskInstance(
		field string,
		taskInstance generator.TaskInstance,
	) error

	// AddCustomType adds a custom type to the struct. The custom type is used to generate values for fields of the custom type.
	AddCustomType(customType CustomType)
}

type Node

type Node[T any] struct {
	// contains filtered or unexported fields
}

func (*Node[T]) AddNode

func (n *Node[T]) AddNode(name string, data *T)

func (*Node[T]) Copy

func (n *Node[T]) Copy() *Node[T]

func (*Node[T]) DeleteNode

func (n *Node[T]) DeleteNode(name string)

func (*Node[T]) GetNode

func (n *Node[T]) GetNode(name string) *Node[T]

func (*Node[T]) HasChild added in v0.1.0

func (n *Node[T]) HasChild(name string) bool

func (*Node[T]) HasChildren

func (n *Node[T]) HasChildren() bool

type StructField added in v0.1.0

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

func (StructField) GetDstructType added in v0.1.0

func (f StructField) GetDstructType() reflect.Type

GetDstructType returns the Dstruct type of the field which can be different from the Go type (reflect.TypeOf(val))

func (StructField) GetEnumValues added in v0.1.0

func (f StructField) GetEnumValues() (enumValues map[string]int)

TODO: shoudl this even be here as it is not used ?

func (StructField) GetFieldName added in v0.1.0

func (f StructField) GetFieldName() string

GetFieldName returns the name of the field.

func (StructField) GetJsonName added in v0.1.0

func (f StructField) GetJsonName() string

GetJsonName returns the json name of the field.

func (StructField) GetQualifiedName added in v0.1.0

func (f StructField) GetQualifiedName() string

GetQualifiedName returns the fully qualified name of the field.

func (StructField) GetTag added in v0.1.0

func (f StructField) GetTag(t string) string

GetTag returns the tag of the field.

func (StructField) GetTypeHash added in v0.1.0

func (f StructField) GetTypeHash() string

GetTypeHash returns the hash of the type of the field.

func (StructField) GetValue added in v0.1.0

func (f StructField) GetValue() any

GetValue returns the value of the field.

func (StructField) IsFieldDereferencable added in v0.1.0

func (f StructField) IsFieldDereferencable() bool

Directories

Path Synopsis
config
Package config provides the configuration for the generation of dynamic structs.
Package config provides the configuration for the generation of dynamic structs.

Jump to

Keyboard shortcuts

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