Documentation
¶
Index ¶
- func Apply(target any, patch map[string]any) error
- func ApplyToMap(original map[string]any, patch map[string]any) map[string]any
- func ApplyToStruct(target any, patch map[string]any) error
- func Diff(old, new any) map[string]any
- func DiffMaps(old, new map[string]any) map[string]any
- func DiffStructs(old, new any) map[string]any
- func ToMap(v any) map[string]any
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Apply ¶
Apply applies a patch to a target, which can be either a struct or a map. For structs: the target must be a pointer to a struct, and the struct is modified in-place. For maps: the target must be a pointer to a map[string]any, and the map is replaced with the patched result.
The patch should be generated by Diff, DiffMaps, or follow the same format: - Keys with values: set/update the key/field to that value - Keys with nil values: delete the key or zero the field if possible - Nested maps/structs: recursively apply patches
Returns an error if the patch cannot be applied due to type incompatibilities or structural constraints.
Example ¶
Example function showing how to use the unified Apply function
// Example 1: Applying patch to a struct type User struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email"` } user := &User{ Name: "John", Age: 30, Email: "john@example.com", } patch := map[string]any{ "name": "Jane", "age": 31, } Apply(user, patch) fmt.Printf("Struct result: %+v\n", *user) // Example 2: Applying patch to a map data := &map[string]any{ "user": "John", "settings": map[string]any{ "theme": "dark", }, } mapPatch := map[string]any{ "user": "Jane", "settings": map[string]any{ "theme": "light", "notifications": true, }, } Apply(data, mapPatch) fmt.Printf("Map result: %+v\n", *data)
Output: Struct result: {Name:Jane Age:31 Email:john@example.com} Map result: map[settings:map[notifications:true theme:light] user:Jane]
func ApplyToMap ¶
ApplyToMap applies a diff/patch to a starting map to produce a new map. The patch should be generated by Diff or DiffMaps, or follow the same format:
- Keys with values: set/update the key to that value - Keys with nil values: delete the key from the result - Nested maps: recursively apply patches to nested maps - Struct values: if original value is a struct and patch is a map, apply patch to struct using ApplyToStruct
The original map is not modified; a new map is returned.
Example ¶
original := map[string]any{"x": 1, "y": 2} patch := map[string]any{"y": 3, "z": 4, "x": nil} // x deleted result := structdiff.ApplyToMap(original, patch) fmt.Printf("Result: %+v\n", result)
Output: Result: map[y:3 z:4]
func ApplyToStruct ¶
ApplyToStruct applies a patch map to a struct, modifying the struct in-place. The patch should be generated by Diff or follow the same format.
Rules: - nil values in patch: delete/zero the field if possible, error if field is not nillable - Type mismatches: attempt conversion for compatible types, error otherwise - JSON tags: honored for field mapping - any fields: accept any value type - Numeric conversions: attempted (like JSON deserialization)
Returns an error if the patch cannot be applied due to type incompatibilities or structural constraints.
Example ¶
type User struct { Name string `json:"name"` Age int `json:"age"` } var user User patch := map[string]any{ "name": "Alice", "age": "25", // string converted to int } err := structdiff.ApplyToStruct(&user, patch) if err != nil { panic(err) } fmt.Printf("User: %+v\n", user)
Output: User: {Name:Alice Age:25}
func Diff ¶
Diff computes a diff/patch between two values that can be any combination of structs and maps. This is a unified function that automatically handles: - struct vs struct: uses DiffStructs - map vs map: uses DiffMaps - struct vs map: converts struct to map using ToMap, then uses DiffMaps - map vs struct: converts struct to map using ToMap, then uses DiffMaps
The resulting map contains only the changes needed to transform old into new: - Keys with same values: omitted - Keys with different values: included with new value - Keys only in new: included with new value - Keys only in old: included with nil value (indicates deletion) - Nested structures: recursively diffed
Returns nil if both values are nil or if there are no differences.
func DiffMaps ¶
DiffMaps computes a diff/patch from old map to new map. The resulting map contains only the changes needed to transform old into new:
- Keys with same values in both maps: omitted - Keys with different values: included with new value - Keys only in new: included with new value - Keys only in old: included with nil value (indicates deletion) - Nested maps: recursively diffed using DiffMaps - Struct values: compared using the unified Diff function for any combination of structs and maps
Applying all changes in the result to the old map would produce the new map.
func DiffStructs ¶
DiffStructs compares two structs and returns a patch map containing only the differences.
The function performs direct struct diffing without creating intermediate maps, providing significant performance improvements for nested structures: - 75% less memory usage - 35% faster execution - 40% fewer allocations
Rules: - Keys with same values: omitted from result - Keys with different values: included with new value - Keys only in new: included with new value - Keys only in old: included with nil value (indicates deletion) - Nested structs and maps: compared using the unified Diff function for any combination of structs and maps
The resulting patch can be applied using ApplyToStruct or ApplyToMap.
Example ¶
type User struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email"` } oldUser := User{Name: "John", Age: 30, Email: "john@old.com"} newUser := User{Name: "John", Age: 31, Email: "john@new.com"} diff := structdiff.DiffStructs(oldUser, newUser) fmt.Printf("Changes: %+v\n", diff)
Output: Changes: map[age:31 email:john@new.com]
Example (Nested) ¶
type Address struct { Street string `json:"street"` City string `json:"city"` } type Employee struct { Name string `json:"name"` Address Address `json:"address"` } old := Employee{ Name: "Alice", Address: Address{Street: "123 Main St", City: "NYC"}, } new := Employee{ Name: "Alice", Address: Address{Street: "456 Oak Ave", City: "NYC"}, } diff := structdiff.DiffStructs(old, new) fmt.Printf("Changes: %+v\n", diff)
Output: Changes: map[address:map[street:456 Oak Ave]]
Example (RoundTrip) ¶
type Person struct { Name string `json:"name"` Born time.Time `json:"born"` } alice := Person{ Name: "Alice", Born: time.Date(1990, 1, 1, 0, 0, 0, 0, time.UTC), } bob := Person{ Name: "Bob", Born: time.Date(1985, 5, 15, 0, 0, 0, 0, time.UTC), } // Compute diff diff := structdiff.DiffStructs(alice, bob) // Apply diff to transform alice into bob result := alice err := structdiff.ApplyToStruct(&result, diff) if err != nil { panic(err) } // Verify the transformation worked fmt.Printf("Original: %s, born %s\n", alice.Name, alice.Born.Format("2006-01-02")) fmt.Printf("Result: %s, born %s\n", result.Name, result.Born.Format("2006-01-02")) fmt.Printf("Matches target: %t\n", result.Name == bob.Name && result.Born.Equal(bob.Born))
Output: Original: Alice, born 1990-01-01 Result: Bob, born 1985-05-15 Matches target: true
func ToMap ¶
ToMap converts a struct to a map[string]any representation. It follows JSON struct tag conventions and handles nested structures, slices, maps, and special types like time.Time.
Rules: - Only exported fields are included - JSON tags are honored for field naming - Fields tagged with `json:"-"` are excluded - Nil pointers are omitted - Empty values (0, "", false, []) are included
Example ¶
type Config struct { Host string `json:"host"` Port int `json:"port"` Password *string `json:"password,omitempty"` Debug bool `json:"debug"` } config := Config{Host: "localhost", Port: 8080, Debug: false} m := structdiff.ToMap(config) fmt.Printf("Map: %+v\n", m)
Output: Map: map[debug:false host:localhost port:8080]
Types ¶
This section is empty.