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 }