feat(attr,ipub): Element interface and ElementKind to prepare unmarshalling of un-structured childre
This commit is contained in:
36
ipub/element/attr/errors.go
Normal file
36
ipub/element/attr/errors.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package attr
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ErrInvalidName struct {
|
||||
Actual xml.Name
|
||||
Expected xml.Name
|
||||
}
|
||||
|
||||
var _ error = ErrInvalidName{}
|
||||
|
||||
func (err ErrInvalidName) Error() string {
|
||||
return fmt.Sprintf("attribute %q has invalid name, expected %q", FmtXMLName(err.Actual), FmtXMLName(err.Expected))
|
||||
}
|
||||
|
||||
type ErrInvalidValue struct {
|
||||
Attr xml.Attr
|
||||
Message string
|
||||
}
|
||||
|
||||
var _ error = ErrInvalidValue{}
|
||||
|
||||
func (err ErrInvalidValue) Error() string {
|
||||
return fmt.Sprintf("attribute %q's value %q is invalid: %s", FmtXMLName(err.Attr.Name), err.Attr.Value, err.Message)
|
||||
}
|
||||
|
||||
func FmtXMLName(n xml.Name) string {
|
||||
s := n.Local
|
||||
if n.Space != "" {
|
||||
s = fmt.Sprintf("%s:%s", n.Space, n.Local)
|
||||
}
|
||||
return s
|
||||
}
|
||||
62
ipub/element/element.go
Normal file
62
ipub/element/element.go
Normal file
@@ -0,0 +1,62 @@
|
||||
package element
|
||||
|
||||
import (
|
||||
"encoding/xml"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"forge.capytal.company/capytalcode/project-comicverse/ipub/element/attr"
|
||||
)
|
||||
|
||||
type Element interface {
|
||||
Kind() ElementKind
|
||||
}
|
||||
|
||||
type ElementKind string
|
||||
|
||||
func NewElementKind(n string, s Element) ElementKind {
|
||||
k := ElementKind(n)
|
||||
|
||||
if _, ok := elementKindList[k]; ok {
|
||||
panic(fmt.Sprintf("element kind %q already registered", n))
|
||||
}
|
||||
|
||||
elementKindList[k] = s
|
||||
return k
|
||||
}
|
||||
|
||||
func (k ElementKind) MarshalXMLAttr(n xml.Name) (xml.Attr, error) {
|
||||
if n != elementKindAttrName {
|
||||
return xml.Attr{}, attr.ErrInvalidName{Actual: n, Expected: elementKindAttrName}
|
||||
}
|
||||
return xml.Attr{Name: elementKindAttrName, Value: k.String()}, nil
|
||||
}
|
||||
|
||||
func (k *ElementKind) UnmarshalXMLAttr(a xml.Attr) error {
|
||||
ak := ElementKind(a.Value)
|
||||
|
||||
if _, ok := elementKindList[ak]; !ok {
|
||||
v := make([]string, 0, len(elementKindList))
|
||||
for k := range elementKindList {
|
||||
v = append(v, k.String())
|
||||
}
|
||||
return attr.ErrInvalidValue{
|
||||
Attr: a,
|
||||
Message: fmt.Sprintf("must be a registered element (%q)", strings.Join(v, `", "`)),
|
||||
}
|
||||
}
|
||||
|
||||
*k = ak
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k ElementKind) String() string {
|
||||
return string(k)
|
||||
}
|
||||
|
||||
var (
|
||||
elementKindList = make(map[ElementKind]Element)
|
||||
elementKindAttrName = xml.Name{Local: "data-ipub-element"}
|
||||
)
|
||||
@@ -7,11 +7,23 @@ type Section struct {
|
||||
Body Body `xml:"body"`
|
||||
}
|
||||
|
||||
var KindSection = NewElementKind("section", Section{})
|
||||
|
||||
func (Section) Kind() ElementKind {
|
||||
return KindSection
|
||||
}
|
||||
|
||||
type Body struct {
|
||||
XMLName xml.Name `xml:"body"`
|
||||
Test string `xml:"test,attr"`
|
||||
}
|
||||
|
||||
var KindBody = NewElementKind("body", Body{})
|
||||
|
||||
func (Body) Kind() ElementKind {
|
||||
return KindBody
|
||||
}
|
||||
|
||||
type Paragraph struct {
|
||||
XMLName xml.Name `xml:"p"`
|
||||
DataElement ElementKind `xml:"data-ipub-element,attr"`
|
||||
@@ -20,3 +32,8 @@ type Paragraph struct {
|
||||
Text string `xml:",chardata"`
|
||||
}
|
||||
|
||||
var KindParagraph = NewElementKind("paragraph", Paragraph{})
|
||||
|
||||
func (Paragraph) Kind() ElementKind {
|
||||
return KindParagraph
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user