11 Commits

Author SHA1 Message Date
9c38048bd2 Properly use xml names 2024-10-17 20:10:18 +02:00
26e0c99150 Use *XHTMLDiv 2024-10-17 19:44:27 +02:00
6117876a59 Check uris before applying them 2024-10-17 19:28:09 +02:00
cb61d90cae Make NewLogo more flexible 2024-10-17 19:19:22 +02:00
30623fdfe0 More ",chardata" 2024-10-17 19:14:37 +02:00
4ae8cecb17 More ",chardata" 2024-10-17 19:11:33 +02:00
8e226b48ef isValidXML is no longer of any use 2024-10-17 19:05:23 +02:00
478d679985 Small bug fix 2024-10-17 18:59:24 +02:00
d11b229691 No more undefined content in link and category
It seems that undefined content is only mentioned in RFC4287 because
link and category are elements and those usually need some content.
2024-10-17 18:57:43 +02:00
e73b78ef30 Content can also be empty 2024-10-17 18:40:52 +02:00
5a82f1799f Force undefined content of category and link to be empty or valid xml 2024-10-17 18:39:57 +02:00
17 changed files with 99 additions and 61 deletions

View File

@ -1,22 +1,23 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"html" "html"
) )
type Category struct { type Category struct {
XMLName xml.Name `xml:"category"`
*CommonAttributes *CommonAttributes
Content string `xml:",chardata"` // undefinedContent in RFC4287 Term string `xml:"term,attr"`
Term string `xml:"term,attr"` Scheme IRI `xml:"scheme,attr,omitempty"`
Scheme IRI `xml:"scheme,attr,omitempty"` Label string `xml:"label,attr,omitempty"`
Label string `xml:"label,attr,omitempty"`
} }
// NewCategory creates a new Category. It returns a *Category and an error. // NewCategory creates a new Category. It returns a *Category.
func NewCategory(term, content string) (*Category, error) { func NewCategory(term string) *Category {
return &Category{Term: term, Content: content}, nil return &Category{Term: term}
} }
// SetLabel sets the label of the Category. // SetLabel sets the label of the Category.
@ -41,9 +42,5 @@ func (c *Category) Check() error {
return fmt.Errorf("label attribute %v of category not correctly escaped", c.Label) return fmt.Errorf("label attribute %v of category not correctly escaped", c.Label)
} }
if c.Content == "" {
return errors.New("content element of category empty")
}
return nil return nil
} }

View File

@ -1,6 +1,7 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
@ -12,16 +13,17 @@ import (
// a non-empty atom:summary element when the entry contains no atom:content // a non-empty atom:summary element when the entry contains no atom:content
// element. // element.
type Entry struct { type Entry struct {
XMLName xml.Name `xml:"entry"`
*CommonAttributes *CommonAttributes
Authors []*Person `xml:"author,omitempty"` Authors []*Person `xml:"author,omitempty"`
Categories []*Category `xml:"category,omitempty"` Categories []*Category `xml:",omitempty"`
Content Content `xml:"content,omitempty"` Content Content `xml:",omitempty"`
Contributors []*Person `xml:"contributors,omitempty"` Contributors []*Person `xml:"contributors,omitempty"`
ID *ID `xml:"id"` ID *ID
Links []*Link `xml:"link,omitempty"` Links []*Link `xml:",omitempty"`
Published *Date `xml:"published,omitempty"` Published *Date `xml:"published,omitempty"`
Rights Text `xml:"rights,omitempty"` Rights Text `xml:"rights,omitempty"`
Source *Source `xml:"source,omitempty"` Source *Source `xml:",omitempty"`
Summary Text `xml:"summary,omitempty"` Summary Text `xml:"summary,omitempty"`
Title Text `xml:"title"` Title Text `xml:"title"`
Updated *Date `xml:"updated"` Updated *Date `xml:"updated"`
@ -62,8 +64,13 @@ func NewEntry(title string) (*Entry, error) {
return nil, fmt.Errorf("error creating new entry: %v", err) return nil, fmt.Errorf("error creating new entry: %v", err)
} }
id, err := NewID(NewURN())
if err != nil {
return nil, fmt.Errorf("error creating new entry: %v", err)
}
return &Entry{ return &Entry{
ID: NewID(NewURN()), ID: id,
Title: text, Title: text,
Updated: NewDate(time.Now()), Updated: NewDate(time.Now()),
}, nil }, nil

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
) )
// TODO: Is this really correct?
type ExtensionAttribute struct { type ExtensionAttribute struct {
Value any `xml:",attr"` Value any `xml:",attr"`
XMLName xml.Name XMLName xml.Name

View File

@ -5,6 +5,7 @@ import (
"errors" "errors"
) )
// TODO: Is this really correct?
type ExtensionElement struct { type ExtensionElement struct {
Value any `xml:",innerxml"` Value any `xml:",innerxml"`
XMLName xml.Name XMLName xml.Name

25
feed.go
View File

@ -10,20 +10,20 @@ import (
type Feed struct { type Feed struct {
XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"` XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"`
*CommonAttributes *CommonAttributes
Authors []*Person `xml:"author,omitempty"` Authors []*Person `xml:"author,omitempty"`
Categories []*Category `xml:"category,omitempty"` Categories []*Category `xml:",omitempty"`
Contributors []*Person `xml:"contributor,omitempty"` Contributors []*Person `xml:"contributor,omitempty"`
Generator *Generator `xml:"generator,omitempty"` Generator *Generator `xml:",omitempty"`
Icon *Icon `xml:"icon,omitempty"` Icon *Icon `xml:",omitempty"`
ID *ID `xml:"id"` ID *ID
Links []*Link `xml:"link,omitempty"` Links []*Link `xml:",omitempty"`
Logo *Logo `xml:"logo,omitempty"` Logo *Logo `xml:",omitempty"`
Rights Text `xml:"rights,omitempty"` Rights Text `xml:"rights,omitempty"`
Subtitle Text `xml:"subtitle,omitempty"` Subtitle Text `xml:"subtitle,omitempty"`
Title Text `xml:"title"` Title Text `xml:"title"`
Updated *Date `xml:"updated"` Updated *Date `xml:"updated"`
Extensions []*ExtensionElement `xml:",any,omitempty"` Extensions []*ExtensionElement `xml:",any,omitempty"`
Entries []*Entry `xml:"entry,omitempty"` Entries []*Entry `xml:",omitempty"`
} }
// NewFeed creates a new Feed. It returns a *Feed and an error. // NewFeed creates a new Feed. It returns a *Feed and an error.
@ -33,8 +33,13 @@ func NewFeed(title string) (*Feed, error) {
return nil, fmt.Errorf("error creating new feed: %v", err) return nil, fmt.Errorf("error creating new feed: %v", err)
} }
id, err := NewID(NewURN())
if err != nil {
return nil, fmt.Errorf("error creating new feed: %v", err)
}
return &Feed{ return &Feed{
ID: NewID(NewURN()), ID: id,
Title: text, Title: text,
Updated: NewDate(time.Now()), Updated: NewDate(time.Now()),
}, nil }, nil

View File

@ -1,16 +1,18 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"html" "html"
) )
type Generator struct { type Generator struct {
XMLName xml.Name `xml:"generator"`
*CommonAttributes *CommonAttributes
URI IRI `xml:"uri,attr,omitempty"` URI IRI `xml:"uri,attr,omitempty"`
Version string `xml:"version,attr,omitempty"` Version string `xml:"version,attr,omitempty"`
Text string `xml:"text"` Text string `xml:",chardata"`
} }
// NewGenerator creates a new Generator. It returns a *Generator. // NewGenerator creates a new Generator. It returns a *Generator.

14
icon.go
View File

@ -1,18 +1,24 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
) )
type Icon struct { type Icon struct {
XMLName xml.Name `xml:"icon"`
*CommonAttributes *CommonAttributes
URI IRI `xml:"uri"` URI IRI `xml:",chardata"`
} }
// NewIcon creates a new Icon. It returns a *Icon. // NewIcon creates a new Icon. It returns a *Icon and an error.
func NewIcon(uri string) *Icon { func NewIcon(uri IRI) (*Icon, error) {
return &Icon{URI: IRI(uri)} if !isValidIRI(uri) {
return nil, fmt.Errorf("uri %v not correctly formatted", uri)
}
return &Icon{URI: uri}, nil
} }
// Check checks the Icon for incompatibilities with RFC4287. It returns an // Check checks the Icon for incompatibilities with RFC4287. It returns an

12
id.go
View File

@ -1,18 +1,24 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
) )
type ID struct { type ID struct {
XMLName xml.Name `xml:"id"`
*CommonAttributes *CommonAttributes
URI IRI `xml:",chardata"` URI IRI `xml:",chardata"`
} }
// NewID creates a new ID. It returns a *ID. // NewID creates a new ID. It returns a *ID and an error.
func NewID(id IRI) *ID { func NewID(uri IRI) (*ID, error) {
return &ID{URI: IRI(id)} if !isValidIRI(uri) {
return nil, fmt.Errorf("uri %v not correctly formatted", uri)
}
return &ID{URI: IRI(uri)}, nil
} }
// Check checks the ID for incompatibilities with RFC4287. It returns an error. // Check checks the ID for incompatibilities with RFC4287. It returns an error.

View File

@ -1,12 +1,14 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"mime" "mime"
) )
type InlineOtherContent struct { type InlineOtherContent struct {
XMLName xml.Name `xml:"content"`
*CommonAttributes *CommonAttributes
AnyElement any `xml:",chardata"` AnyElement any `xml:",chardata"`
Type MediaType `xml:"type,attr,omitempty"` Type MediaType `xml:"type,attr,omitempty"`

View File

@ -1,11 +1,13 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
) )
type InlineTextContent struct { type InlineTextContent struct {
XMLName xml.Name `xml:"content"`
*CommonAttributes *CommonAttributes
Type string `xml:"type,attr,omitempty"` // Must be text or html Type string `xml:"type,attr,omitempty"` // Must be text or html
Text string `xml:",chardata"` Text string `xml:",chardata"`

View File

@ -1,14 +1,16 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
) )
type InlineXHTMLContent struct { type InlineXHTMLContent struct {
XMLName xml.Name `xml:"content"`
*CommonAttributes *CommonAttributes
XHTMLDiv *XHTMLDiv
Type string `xml:"type,attr"` Type string `xml:"type,attr"`
XHTMLDiv XHTMLDiv
} }
// newInlineXHTMLContent creates a new InlineXHTMLContent. It returns a // newInlineXHTMLContent creates a new InlineXHTMLContent. It returns a
@ -18,7 +20,7 @@ func newInlineXHTMLContent(mediaType string, content any) (*InlineXHTMLContent,
return nil, fmt.Errorf("media type %v incompatible with inline xhtml content", mediaType) return nil, fmt.Errorf("media type %v incompatible with inline xhtml content", mediaType)
} }
xhtmlDiv, ok := content.(XHTMLDiv) xhtmlDiv, ok := content.(*XHTMLDiv)
if !ok { if !ok {
return nil, fmt.Errorf("content type %T incompatible with inline xhtml content", content) return nil, fmt.Errorf("content type %T incompatible with inline xhtml content", content)
} }

13
link.go
View File

@ -1,15 +1,16 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"strings" "strings"
) )
type Link struct { type Link struct {
XMLName xml.Name `xml:"link"`
*CommonAttributes *CommonAttributes
Title string `xml:"title,attr,omitempty"` Title string `xml:"title,attr,omitempty"`
Content string `xml:",chardata"` // undefinedContent in RFC4287
Href IRI `xml:"href,attr"` Href IRI `xml:"href,attr"`
Rel string `xml:"rel,attr,omitempty"` Rel string `xml:"rel,attr,omitempty"`
Type MediaType `xml:"type,attr,omitempty"` Type MediaType `xml:"type,attr,omitempty"`
@ -17,9 +18,9 @@ type Link struct {
Length uint `xml:"length,attr,omitempty"` Length uint `xml:"length,attr,omitempty"`
} }
// NewLink creates a new Link. It returns a *Link and an error. // NewLink creates a new Link. It returns a *Link.
func NewLink(href, content string) (*Link, error) { func NewLink(href string) *Link {
return &Link{Href: IRI(href), Content: content}, nil return &Link{Href: IRI(href)}
} }
// Check checks the Link for incompatibilities with RFC4287. It returns an // Check checks the Link for incompatibilities with RFC4287. It returns an
@ -51,10 +52,6 @@ func (l *Link) Check() error {
} }
} }
if l.Content == "" {
return errors.New("content element of link empty")
}
return nil return nil
} }

14
logo.go
View File

@ -1,20 +1,24 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"github.com/google/uuid"
) )
type Logo struct { type Logo struct {
XMLName xml.Name `xml:"logo"`
*CommonAttributes *CommonAttributes
URI IRI `xml:"uri"` URI IRI `xml:",chardata"`
} }
// NewLogo creates a new Logo. It returns a *Logo. // NewLogo creates a new Logo. It returns a *Logo.
func NewLogo() *Logo { func NewLogo(uri IRI) (*Logo, error) {
return &Logo{URI: IRI(fmt.Sprint("urn:uuid:", uuid.New()))} if !isValidIRI(uri) {
return nil, fmt.Errorf("uri %v not correctly formatted", uri)
}
return &Logo{URI: uri}, nil
} }
// Check checks the Logo for incompatibilities with RFC4287. It returns an // Check checks the Logo for incompatibilities with RFC4287. It returns an

View File

@ -1,12 +1,14 @@
package atom package atom
import ( import (
"encoding/xml"
"errors" "errors"
"fmt" "fmt"
"mime" "mime"
) )
type OutOfLineContent struct { type OutOfLineContent struct {
XMLName xml.Name `xml:"content"`
*CommonAttributes *CommonAttributes
Type MediaType `xml:"type,attr,omitempty"` Type MediaType `xml:"type,attr,omitempty"`
SRC IRI `xml:"src,attr"` SRC IRI `xml:"src,attr"`

View File

@ -1,17 +1,21 @@
package atom package atom
import "fmt" import (
"encoding/xml"
"fmt"
)
type Source struct { type Source struct {
XMLName xml.Name `xml:"source"`
*CommonAttributes *CommonAttributes
Authors []*Person `xml:"author,omitempty"` Authors []*Person `xml:"author,omitempty"`
Categories []*Category `xml:"category,omitempty"` Categories []*Category `xml:",omitempty"`
Contributors []*Person `xml:"contributor,omitempty"` Contributors []*Person `xml:"contributor,omitempty"`
Generator *Generator `xml:"generator,omitempty"` Generator *Generator `xml:",omitempty"`
Icon *Icon `xml:"icon,omitempty"` Icon *Icon `xml:",omitempty"`
ID *ID `xml:"id,omitempty"` ID *ID `xml:",omitempty"`
Links []*Link `xml:"link,omitempty"` Links []*Link `xml:",omitempty"`
Logo *Logo `xml:"logo,omitempty"` Logo *Logo `xml:",omitempty"`
Rights Text `xml:"rights,omitempty"` Rights Text `xml:"rights,omitempty"`
Subtitle Text `xml:"subtitle,omitempty"` Subtitle Text `xml:"subtitle,omitempty"`
Title Text `xml:"title,omitempty"` Title Text `xml:"title,omitempty"`

View File

@ -20,7 +20,7 @@ func NewText(textType, content string) (Text, error) {
case "xhtml": case "xhtml":
return &XHTMLText{ return &XHTMLText{
Type: textType, Type: textType,
XHTMLDiv: XHTMLDiv{ XHTMLDiv: &XHTMLDiv{
XMLNS: "http://www.w3.org/1999/xhtml", XMLNS: "http://www.w3.org/1999/xhtml",
Content: content, Content: content,
}, },

View File

@ -7,8 +7,8 @@ import (
type XHTMLText struct { type XHTMLText struct {
*CommonAttributes *CommonAttributes
XHTMLDiv *XHTMLDiv
Type string `xml:"type,attr"` // Must be xhtml Type string `xml:"type,attr"` // Must be xhtml
XHTMLDiv XHTMLDiv
} }
// isText checks whether the XHTMLText is a Text. It returns a bool. // isText checks whether the XHTMLText is a Text. It returns a bool.