1
0
mirror of https://github.com/imgproxy/imgproxy.git synced 2025-12-23 22:11:10 +02:00
Files
imgproxy/xmlparser/attributes.go

139 lines
3.3 KiB
Go

package xmlparser
import (
"iter"
"slices"
)
// Attribute represents a single XML attribute.
type Attribute struct {
Name Name
Value string
}
// Attributes represents a collection of XML attributes.
type Attributes struct {
s []*Attribute // Slice for ordered iteration
m map[Name]*Attribute // Map for quick lookup
}
// NewAttributes creates a new [Attributes] instance from the given attributes.
func NewAttributes(attrs ...*Attribute) *Attributes {
// If no attributes are provided, return an empty Attributes instance
if len(attrs) == 0 {
return &Attributes{}
}
// Create a map for quick lookup
m := make(map[Name]*Attribute, len(attrs))
for _, attr := range attrs {
m[attr.Name] = attr
}
return &Attributes{
s: attrs,
m: m,
}
}
// Get retrieves the value of the attribute with the given name.
// It returns the value and true if the attribute exists, or an empty string and false otherwise.
func (a *Attributes) Get(name Name) (string, bool) {
if attr, ok := a.m[name]; ok {
return attr.Value, true
}
return "", false
}
// GetByString is like Get but accepts a string name.
func (a *Attributes) GetByString(name string) (string, bool) {
return a.Get(Name(name))
}
// Set sets the value of the attribute with the given name.
// If the attribute does not exist, it is created.
func (a *Attributes) Set(name Name, value string) {
// Initialize the map if it is nil
if a.m == nil {
a.m = make(map[Name]*Attribute)
}
// If attribute exists, update its value
if attr, exists := a.m[name]; exists {
attr.Value = value
return
}
// Otherwise, create a new attribute
attr := &Attribute{Name: name, Value: value}
a.m[name] = attr
a.s = append(a.s, attr)
}
// SetByString is like Set but accepts a string name.
func (a *Attributes) SetByString(name, value string) {
a.Set(Name(name), value)
}
// Delete removes the attribute with the given name.
func (a *Attributes) Delete(name Name) {
// If attribute does not exist, nothing to do
attr, exists := a.m[name]
if !exists {
return
}
// Remove from map
delete(a.m, name)
// Remove from slice
if idx := slices.Index(a.s, attr); idx != -1 {
a.s = slices.Delete(a.s, idx, idx+1)
}
}
// DeleteByString is like Delete but accepts a string name.
func (a *Attributes) DeleteByString(name string) {
a.Delete(Name(name))
}
// Has returns true if the attribute with the given name exists.
func (a *Attributes) Has(name Name) bool {
_, exists := a.m[name]
return exists
}
// HasByString is like Has but accepts a string name.
func (a *Attributes) HasByString(name string) bool {
return a.Has(Name(name))
}
// Len returns the number of attributes.
func (a *Attributes) Len() int {
return len(a.s)
}
// Iter returns an iterator over the attributes.
//
// !WARNING! Don't delete items from the Attributes while iterating.
// If you need to filter attributes, use the Filter method.
func (a *Attributes) Iter() iter.Seq[*Attribute] {
return slices.Values(a.s)
}
// Filter removes all attributes that do not satisfy the predicate function.
func (a *Attributes) Filter(pred func(*Attribute) bool) {
a.s = slices.DeleteFunc(a.s, func(attr *Attribute) bool {
del := !pred(attr)
if del {
delete(a.m, attr.Name)
}
return del
})
}
// Sort sorts the attributes according to the provided function.
func (a *Attributes) Sort(cmp func(i, j *Attribute) int) {
slices.SortFunc(a.s, cmp)
}