feat(attr,ipub): ElementChildren to provide a universal unmarshalling of child elements

This commit is contained in:
Guz
2025-05-22 11:09:58 -03:00
parent 7f6f9f7682
commit 9ecacc3808
2 changed files with 52 additions and 0 deletions

View File

@@ -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)

View File

@@ -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{})