2023-12-22 13:20:45 +00:00
|
|
|
package dyn
|
2023-12-22 10:38:09 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
)
|
|
|
|
|
|
|
|
type pathComponent struct {
|
|
|
|
key string
|
|
|
|
index int
|
|
|
|
}
|
|
|
|
|
2024-02-16 20:54:38 +00:00
|
|
|
func (c pathComponent) Key() string {
|
|
|
|
return c.key
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c pathComponent) Index() int {
|
|
|
|
return c.index
|
|
|
|
}
|
|
|
|
|
2024-08-20 13:34:49 +00:00
|
|
|
func (c pathComponent) IsKey() bool {
|
Add functionality to visit values in `dyn.Value` tree (#1142)
## Changes
This change adds the following functions:
* `dyn.Get(value, "foo.bar") -> (dyn.Value, error)`
* `dyn.Set(value, "foo.bar", newValue) -> (dyn.Value, error)`
* `dyn.Map(value, "foo.bar", func) -> (dyn.Value, error)`
And equivalent functions that take a previously constructed `dyn.Path`:
* `dyn.GetByPath(value, dyn.Path) -> (dyn.Value, error)`
* `dyn.SetByPath(value, dyn.Path, newValue) -> (dyn.Value, error)`
* `dyn.MapByPath(value, dyn.Path, func) -> (dyn.Value, error)`
Changes made by the "set" and "map" functions are never reflected in the
input argument; they return new `dyn.Value` instances for all nodes in
the path leading up to the changed value.
## Tests
New unit tests cover all critical paths.
2024-01-24 18:38:46 +00:00
|
|
|
return c.key != ""
|
|
|
|
}
|
|
|
|
|
2024-08-20 13:34:49 +00:00
|
|
|
func (c pathComponent) IsIndex() bool {
|
Add functionality to visit values in `dyn.Value` tree (#1142)
## Changes
This change adds the following functions:
* `dyn.Get(value, "foo.bar") -> (dyn.Value, error)`
* `dyn.Set(value, "foo.bar", newValue) -> (dyn.Value, error)`
* `dyn.Map(value, "foo.bar", func) -> (dyn.Value, error)`
And equivalent functions that take a previously constructed `dyn.Path`:
* `dyn.GetByPath(value, dyn.Path) -> (dyn.Value, error)`
* `dyn.SetByPath(value, dyn.Path, newValue) -> (dyn.Value, error)`
* `dyn.MapByPath(value, dyn.Path, func) -> (dyn.Value, error)`
Changes made by the "set" and "map" functions are never reflected in the
input argument; they return new `dyn.Value` instances for all nodes in
the path leading up to the changed value.
## Tests
New unit tests cover all critical paths.
2024-01-24 18:38:46 +00:00
|
|
|
return c.key == ""
|
|
|
|
}
|
|
|
|
|
2023-12-22 10:38:09 +00:00
|
|
|
// Path represents a path to a value in a [Value] configuration tree.
|
|
|
|
type Path []pathComponent
|
|
|
|
|
|
|
|
// EmptyPath is the empty path.
|
|
|
|
// It is defined for convenience and clarity.
|
|
|
|
var EmptyPath = Path{}
|
|
|
|
|
|
|
|
// Key returns a path component for a key.
|
|
|
|
func Key(k string) pathComponent {
|
|
|
|
return pathComponent{key: k}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Index returns a path component for an index.
|
|
|
|
func Index(i int) pathComponent {
|
|
|
|
return pathComponent{index: i}
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewPath returns a new path from the given components.
|
|
|
|
// The individual components may be created with [Key] or [Index].
|
|
|
|
func NewPath(cs ...pathComponent) Path {
|
|
|
|
return cs
|
|
|
|
}
|
|
|
|
|
|
|
|
// Append appends the given components to the path.
|
2024-03-19 09:49:26 +00:00
|
|
|
// Mutations to the returned path do not affect the original path.
|
2023-12-22 10:38:09 +00:00
|
|
|
func (p Path) Append(cs ...pathComponent) Path {
|
2024-03-19 09:49:26 +00:00
|
|
|
out := make(Path, len(p)+len(cs))
|
|
|
|
copy(out, p)
|
|
|
|
copy(out[len(p):], cs)
|
|
|
|
return out
|
2023-12-22 10:38:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Equal returns true if the paths are equal.
|
|
|
|
func (p Path) Equal(q Path) bool {
|
|
|
|
pl := len(p)
|
|
|
|
ql := len(q)
|
|
|
|
if pl != ql {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := 0; i < pl; i++ {
|
|
|
|
if p[i] != q[i] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// HasPrefix returns true if the path has the specified prefix.
|
|
|
|
// The empty path is a prefix of all paths.
|
|
|
|
func (p Path) HasPrefix(q Path) bool {
|
|
|
|
pl := len(p)
|
|
|
|
ql := len(q)
|
|
|
|
if pl < ql {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := 0; i < ql; i++ {
|
|
|
|
if p[i] != q[i] {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// String returns a string representation of the path.
|
|
|
|
func (p Path) String() string {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
|
|
|
|
for i, c := range p {
|
|
|
|
if i > 0 && c.key != "" {
|
|
|
|
buf.WriteRune('.')
|
|
|
|
}
|
|
|
|
if c.key != "" {
|
|
|
|
buf.WriteString(c.key)
|
|
|
|
} else {
|
|
|
|
buf.WriteString(fmt.Sprintf("[%d]", c.index))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.String()
|
|
|
|
}
|