dstruct

package module
v1.1.1 Latest Latest
Warning

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

Go to latest
Published: May 4, 2023 License: MIT Imports: 8 Imported by: 4

README

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

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.
	GetField(name string) Builder

	// GetFieldCopy returns a copy of a builder instance of the subfield of the struct that is currently being built.
	//
	// Deprecated: this method will be removed use NewBuilderFromField instead.
	GetFieldCopy(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 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)

	// 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() FieldData

	// Update updates the struct's underlying tree to represent that of the strct's value
	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 v1.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.1

func (dm *DynamicStructModifierImpl) GetFields() FieldData

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 v1.1.0

func (dm *DynamicStructModifierImpl) Update()

type FieldData added in v1.1.1

type FieldData map[string]structField

type FieldModifier

type FieldModifier func(*structField)

type FieldNode added in v1.1.1

type FieldNode map[string]*Node[structField]

type GeneratedStruct

type GeneratedStruct interface {
	DynamicStructModifier
	// Generate generates fields for the struct
	Generate()

	// GenerateAndUpdate Generates fields and updates the root tree for the underlying struct. Allowing
	// new generated fields to be accessed and modified by Set and Get methods.
	GenerateAndUpdate()

	// GetFieldGenerationConfig gets the generation config for field within the struct.
	GetFieldGenerationConfig(field string) *generator.GenerationConfig

	// 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 *generator.GenerationConfig) error
}

type GeneratedStructImpl

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

func NewGeneratedStruct

func NewGeneratedStruct(val any) *GeneratedStructImpl

func NewGeneratedStructWithConfig

func NewGeneratedStructWithConfig(val any,
	gen *generator.Generator) *GeneratedStructImpl

func (*GeneratedStructImpl) Generate

func (gs *GeneratedStructImpl) Generate()

func (*GeneratedStructImpl) GenerateAndUpdate added in v1.1.1

func (gs *GeneratedStructImpl) GenerateAndUpdate()

func (*GeneratedStructImpl) GetFieldGenerationConfig

func (gs *GeneratedStructImpl) GetFieldGenerationConfig(field string) *generator.GenerationConfig

func (*GeneratedStructImpl) GetFieldGenerator added in v0.1.2

func (gs *GeneratedStructImpl) GetFieldGenerator(field string) *generator.Generator

func (*GeneratedStructImpl) SetFieldDefaultGenerationFunction added in v0.1.2

func (gs *GeneratedStructImpl) SetFieldDefaultGenerationFunction(field string,
	generationFunction generator.GenerationFunction)

func (*GeneratedStructImpl) SetFieldGenerationConfig

func (gs *GeneratedStructImpl) SetFieldGenerationConfig(field string, generationConfig *generator.GenerationConfig) error

func (*GeneratedStructImpl) SetFieldGenerator added in v0.1.2

func (gs *GeneratedStructImpl) SetFieldGenerator(field string,
	generator *generator.Generator)

type GenerationFields

type GenerationFields map[string]*generator.GenerationUnit

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]) HasChildren

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

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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