openmeter / pkg /models /fielddescriptor.go
Leon4gr45's picture
Upload folder using huggingface_hub (part 9)
fea99b3 verified
Raw
History Blame Contribute Delete
4.8 kB
package models
import (
"encoding/json"
"strings"
"github.com/samber/lo"
"github.com/openmeterio/openmeter/pkg/treex"
)
type FieldDescriptor struct {
field string
exp FieldExpression
attrs Attributes
node *treex.Node[*FieldDescriptor]
}
func (s FieldDescriptor) Clone() *FieldDescriptor {
desc := &FieldDescriptor{
field: s.field,
exp: s.exp,
attrs: s.attrs.Clone(),
node: s.node.ShallowClone(),
}
desc.node.SetValue(desc)
return desc
}
func (s FieldDescriptor) WithExpression(exp FieldExpression) *FieldDescriptor {
s.exp = exp
s.node = s.node.ShallowClone()
s.node.SetValue(&s)
return &s
}
func (s FieldDescriptor) WithAttributes(attrs Attributes) *FieldDescriptor {
curr := s.attrs
if curr == nil {
curr = make(Attributes)
}
s.attrs = curr.Merge(attrs)
s.node = s.node.ShallowClone()
s.node.SetValue(&s)
return &s
}
func (p FieldDescriptor) GetAttributes() Attributes {
return p.attrs
}
func (p FieldDescriptor) WithPrefix(_prefix *FieldDescriptor) *FieldDescriptor {
var prefix *FieldDescriptor
if _prefix != nil {
prefix = _prefix.Clone()
}
p.node = p.node.ShallowClone()
p.node.SetValue(&p)
if prefix == nil {
return NewFieldSelectorGroup(&p)
}
return NewFieldSelectorGroup(prefix, &p)
}
func (p *FieldDescriptor) MarshalJSON() ([]byte, error) {
if p == nil {
return json.Marshal("")
}
return json.Marshal(p.JSONPath())
}
func (p *FieldDescriptor) String() string {
if p == nil {
return ""
}
b := strings.Builder{}
// We'll use a DFS traversal to build the string
if err := p.Tree(func(t *FieldDescriptorTree) error {
leafCount := 0
return t.DFS(func(n *treex.Node[*FieldDescriptor]) (bool, error) {
// Only leaf nodes (childrenless segments with field names) make up the path, the rest of the graph is just hierarchical information
if n.IsLeaf() {
desc := n.Value()
if leafCount > 0 {
b.WriteString(".")
}
b.WriteString(n.Value().field)
if desc.exp != nil {
if exp := desc.exp.String(); exp != "" {
b.WriteString("[")
b.WriteString(exp)
b.WriteString("]")
}
}
leafCount++
}
return false, nil
})
}); err != nil {
return ""
}
return b.String()
}
func (p *FieldDescriptor) JSONPath() string {
if p == nil {
return ""
}
b := strings.Builder{}
// Tree.Root().IsRoot() is always true so we have to make this assertion here
if p.node.IsRoot() {
b.WriteString("$")
b.WriteString(".")
}
if err := p.Tree(func(t *FieldDescriptorTree) error {
leafCount := 0
return t.DFS(func(n *treex.Node[*FieldDescriptor]) (bool, error) {
// Only leaf nodes (childrenless segments with field names) make up the path, the rest of the graph is just hierarchical information
if n.IsLeaf() {
desc := n.Value()
if leafCount > 0 {
b.WriteString(".")
}
b.WriteString(desc.field)
if desc.exp != nil {
expOpen := "["
expClose := "]"
if desc.exp.IsCondition() {
expOpen = "[?("
expClose = ")]"
}
if exp := desc.exp.JSONPathExpression(); exp != "" {
b.WriteString(expOpen)
b.WriteString(exp)
b.WriteString(expClose)
}
}
leafCount++
}
return false, nil
})
}); err != nil {
return ""
}
return b.String()
}
// FieldDescriptorTree is a wrapper around treex.Tree[*FieldDescriptor]
// with methods meaningful for a FieldDescriptor
type FieldDescriptorTree struct {
*treex.Tree[*FieldDescriptor]
}
// If called while walking the tree, you MUST start backtracking (return true)
// otherwise you'll keep walking the detached subtree!
func (t *FieldDescriptorTree) Swap(old, new *FieldDescriptor) error {
return t.Tree.SwapNode(old.node, new.node)
}
func (t *FieldDescriptorTree) Leafs() []*FieldDescriptor {
return lo.Map(t.Tree.Leafs(), func(n *treex.Node[*FieldDescriptor], _ int) *FieldDescriptor {
return n.Value()
})
}
// Tree returns a treex.Tree[*FieldDescriptor] from the FieldDescriptor so it can be traversed
func (s FieldDescriptor) Tree(cb func(t *FieldDescriptorTree) error) error {
tree, err := treex.NewTree(s.node)
if err != nil {
return err
}
return cb(&FieldDescriptorTree{Tree: tree})
}
func newFieldDescriptor() *FieldDescriptor {
desc := &FieldDescriptor{}
desc.node = treex.NewNode(desc)
return desc
}
func NewFieldSelector(field string) *FieldDescriptor {
desc := newFieldDescriptor()
desc.field = field
return desc
}
func NewFieldSelectorGroup(selectors ...*FieldDescriptor) *FieldDescriptor {
selectors = lo.Filter(selectors, func(item *FieldDescriptor, _ int) bool {
return item != nil
})
if len(selectors) == 0 {
return nil
}
desc := newFieldDescriptor()
for i := range selectors {
nd := selectors[i].node
desc.node.AddChild(nd)
}
return desc
}