feat(attr,ipub): ElementChildren to provide a universal unmarshalling of child elements
This commit is contained in:
@@ -4,6 +4,9 @@ import (
|
|||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"slices"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"forge.capytal.company/capytalcode/project-comicverse/ipub/element/attr"
|
"forge.capytal.company/capytalcode/project-comicverse/ipub/element/attr"
|
||||||
@@ -13,8 +16,55 @@ type Element interface {
|
|||||||
Kind() ElementKind
|
Kind() ElementKind
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ElementChildren []Element
|
||||||
|
|
||||||
|
func (ec *ElementChildren) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
|
||||||
|
elErr := fmt.Errorf("unable to unsmarshal element %q", attr.FmtXMLName(start.Name))
|
||||||
|
|
||||||
|
i := slices.IndexFunc(start.Attr, func(a xml.Attr) bool {
|
||||||
|
return a.Name == elementKindAttrName
|
||||||
|
})
|
||||||
|
if i == -1 {
|
||||||
|
return errors.Join(elErr, fmt.Errorf("element kind not specified"))
|
||||||
|
}
|
||||||
|
|
||||||
|
var k ElementKind
|
||||||
|
if err := k.UnmarshalXMLAttr(start.Attr[i]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ks := elementKindList[k]
|
||||||
|
|
||||||
|
// Get a pointer of a new instance of the underlying implementation so we can
|
||||||
|
// change it without manipulating the value inside the elementKindList.
|
||||||
|
ep := reflect.New(reflect.TypeOf(ks))
|
||||||
|
if ep.Elem().Kind() == reflect.Pointer {
|
||||||
|
// If the implementation is a pointer, we need the underlying value so we can
|
||||||
|
// manipulate it.
|
||||||
|
ep = reflect.New(reflect.TypeOf(ks).Elem())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := d.DecodeElement(ep.Interface(), &start); err != nil && err != io.EOF {
|
||||||
|
return errors.Join(elErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ec == nil {
|
||||||
|
c := ElementChildren{}
|
||||||
|
ec = &c
|
||||||
|
}
|
||||||
|
|
||||||
|
s := *ec
|
||||||
|
s = append(s, ep.Interface().(Element))
|
||||||
|
*ec = s
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
type ElementKind string
|
type ElementKind string
|
||||||
|
|
||||||
|
// NewElementKind registers a new Element implementation to a private list which is
|
||||||
|
// consumed bu [ElementChildren] to properly find what underlying type is a children
|
||||||
|
// of another element struct.
|
||||||
func NewElementKind(n string, s Element) ElementKind {
|
func NewElementKind(n string, s Element) ElementKind {
|
||||||
k := ElementKind(n)
|
k := ElementKind(n)
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,8 @@ func (Section) Kind() ElementKind {
|
|||||||
type Body struct {
|
type Body struct {
|
||||||
XMLName xml.Name `xml:"body"`
|
XMLName xml.Name `xml:"body"`
|
||||||
Test string `xml:"test,attr"`
|
Test string `xml:"test,attr"`
|
||||||
|
|
||||||
|
Children ElementChildren `xml:",any"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var KindBody = NewElementKind("body", Body{})
|
var KindBody = NewElementKind("body", Body{})
|
||||||
|
|||||||
Reference in New Issue
Block a user