github.com/egonelbre/exp@v0.0.0-20240430123955-ed1d3aa93911/spec/soap/list.go (about)

     1  package soap
     2  
     3  import "reflect"
     4  
     5  type TagListSpec struct {
     6  	Name  string
     7  	Items reflect.Value
     8  }
     9  
    10  func TagList(name string, items interface{}) Spec {
    11  	v := reflect.ValueOf(items)
    12  	if v.Kind() != reflect.Ptr {
    13  		// todo: better check,
    14  		// i.e. that it's a pointer to elements that implement Spec
    15  		panic("not a pointer to array")
    16  	}
    17  	return &TagListSpec{
    18  		Name:  name,
    19  		Items: v,
    20  	}
    21  }
    22  
    23  func (spec *TagListSpec) Spec() Spec { return spec }
    24  
    25  //TODO: this can be improved for accepting more different types
    26  func (spec *TagListSpec) Encode() *Node {
    27  	node := &Node{}
    28  	node.XMLName.Local = spec.Name
    29  
    30  	items := spec.Items.Elem()
    31  	for i, n := 0, items.Len(); i < n; i++ {
    32  		v := items.Index(i)
    33  		if v.Kind() != reflect.Ptr {
    34  			v = v.Addr()
    35  		}
    36  		child := v.Interface().(Speced)
    37  		node.Nodes = append(node.Nodes, *child.Spec().Encode())
    38  	}
    39  
    40  	return node
    41  }
    42  
    43  func (spec *TagListSpec) Decode(node *Node) {
    44  	if spec.Name != node.XMLName.Local {
    45  		panic("invalid name expected " + spec.Name + " got " + node.XMLName.Local)
    46  	}
    47  	items := spec.Items.Elem()
    48  	n := len(node.Nodes)
    49  
    50  	// Grow slice if necessary
    51  	if n >= items.Cap() {
    52  		newitems := reflect.MakeSlice(items.Type(), items.Len(), n)
    53  		items.Set(newitems)
    54  	}
    55  	items.SetLen(n)
    56  
    57  	for i, child := range node.Nodes {
    58  		v := items.Index(i)
    59  		if v.Kind() == reflect.Ptr && v.IsNil() {
    60  			v.Set(reflect.New(v.Type()))
    61  		}
    62  		if v.Kind() != reflect.Ptr {
    63  			v = v.Addr()
    64  		}
    65  
    66  		childspec := v.Interface().(Speced).Spec()
    67  		childspec.Decode(&child)
    68  	}
    69  }