13 Commits

Author SHA1 Message Date
3172a4865a Create needed functions for common attributes and their extensions 2024-10-17 20:48:32 +02:00
3b3f1f7e41 Change title 2024-10-17 20:47:37 +02:00
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
18 changed files with 123 additions and 67 deletions

View File

@ -1,4 +1,3 @@
# atom-feed
# atom
An extensible implementation of an Atom feed that aims to be very close to RFC4287.

View File

@ -1,22 +1,23 @@
package atom
import (
"encoding/xml"
"errors"
"fmt"
"html"
)
type Category struct {
XMLName xml.Name `xml:"category"`
*CommonAttributes
Content string `xml:",chardata"` // undefinedContent in RFC4287
Term string `xml:"term,attr"`
Scheme IRI `xml:"scheme,attr,omitempty"`
Label string `xml:"label,attr,omitempty"`
}
// NewCategory creates a new Category. It returns a *Category and an error.
func NewCategory(term, content string) (*Category, error) {
return &Category{Term: term, Content: content}, nil
// NewCategory creates a new Category. It returns a *Category.
func NewCategory(term string) *Category {
return &Category{Term: term}
}
// 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)
}
if c.Content == "" {
return errors.New("content element of category empty")
}
return nil
}

View File

@ -8,6 +8,22 @@ type CommonAttributes struct {
UndefinedAttributes []*ExtensionAttribute `xml:",any"`
}
// NewCommonAttributes creates a new set of CommonAttributes. It returns a
// *CommonAttributes.
func NewCommonAttributes() *CommonAttributes {
return new(CommonAttributes)
}
// AddExtensionAttribute adds the ExtensionAttribute to the CommonAttributes.
func (c *CommonAttributes) AddExtensionAttribute(e *ExtensionAttribute) {
if c.UndefinedAttributes == nil {
c.UndefinedAttributes = make([]*ExtensionAttribute, 1)
c.UndefinedAttributes[0] = e
} else {
c.UndefinedAttributes = append(c.UndefinedAttributes, e)
}
}
// Check checks the CommonAttributes for incompatibilities with RFC4287. It
// returns an error.
func (c *CommonAttributes) Check() error {

View File

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

View File

@ -1,19 +1,24 @@
package atom
import (
"encoding/xml"
"errors"
"fmt"
)
type ExtensionAttribute struct {
Value any `xml:",attr"`
XMLName xml.Name
Attr string `xml:",attr"`
}
// NewExtensionAttribute creates a new ExtensionAttribute. It returns a
// *ExtensionAttribute.
func NewExtensionAttribute(name, value string) *ExtensionAttribute {
return &ExtensionAttribute{Attr: fmt.Sprint(name, `="`, value, `"`)}
}
// Check checks the ExtensionAttribute for incompatibilities with RFC4287. It
// returns an error.
func (e *ExtensionAttribute) Check() error {
if e.Value == nil {
if e.Attr == "" {
return errors.New("value element of extension attribute empty")
}

21
feed.go
View File

@ -11,19 +11,19 @@ type Feed struct {
XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"`
*CommonAttributes
Authors []*Person `xml:"author,omitempty"`
Categories []*Category `xml:"category,omitempty"`
Categories []*Category `xml:",omitempty"`
Contributors []*Person `xml:"contributor,omitempty"`
Generator *Generator `xml:"generator,omitempty"`
Icon *Icon `xml:"icon,omitempty"`
ID *ID `xml:"id"`
Links []*Link `xml:"link,omitempty"`
Logo *Logo `xml:"logo,omitempty"`
Generator *Generator `xml:",omitempty"`
Icon *Icon `xml:",omitempty"`
ID *ID
Links []*Link `xml:",omitempty"`
Logo *Logo `xml:",omitempty"`
Rights Text `xml:"rights,omitempty"`
Subtitle Text `xml:"subtitle,omitempty"`
Title Text `xml:"title"`
Updated *Date `xml:"updated"`
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.
@ -33,8 +33,13 @@ func NewFeed(title string) (*Feed, error) {
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{
ID: NewID(NewURN()),
ID: id,
Title: text,
Updated: NewDate(time.Now()),
}, nil

View File

@ -1,16 +1,18 @@
package atom
import (
"encoding/xml"
"errors"
"fmt"
"html"
)
type Generator struct {
XMLName xml.Name `xml:"generator"`
*CommonAttributes
URI IRI `xml:"uri,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.

14
icon.go
View File

@ -1,18 +1,24 @@
package atom
import (
"encoding/xml"
"errors"
"fmt"
)
type Icon struct {
XMLName xml.Name `xml:"icon"`
*CommonAttributes
URI IRI `xml:"uri"`
URI IRI `xml:",chardata"`
}
// NewIcon creates a new Icon. It returns a *Icon.
func NewIcon(uri string) *Icon {
return &Icon{URI: IRI(uri)}
// NewIcon creates a new Icon. It returns a *Icon and an error.
func NewIcon(uri IRI) (*Icon, error) {
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

12
id.go
View File

@ -1,18 +1,24 @@
package atom
import (
"encoding/xml"
"errors"
"fmt"
)
type ID struct {
XMLName xml.Name `xml:"id"`
*CommonAttributes
URI IRI `xml:",chardata"`
}
// NewID creates a new ID. It returns a *ID.
func NewID(id IRI) *ID {
return &ID{URI: IRI(id)}
// NewID creates a new ID. It returns a *ID and an error.
func NewID(uri IRI) (*ID, error) {
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.

View File

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

View File

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

View File

@ -1,14 +1,16 @@
package atom
import (
"encoding/xml"
"errors"
"fmt"
)
type InlineXHTMLContent struct {
XMLName xml.Name `xml:"content"`
*CommonAttributes
XHTMLDiv *XHTMLDiv
Type string `xml:"type,attr"`
XHTMLDiv XHTMLDiv
}
// 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)
}
xhtmlDiv, ok := content.(XHTMLDiv)
xhtmlDiv, ok := content.(*XHTMLDiv)
if !ok {
return nil, fmt.Errorf("content type %T incompatible with inline xhtml content", content)
}

13
link.go
View File

@ -1,15 +1,16 @@
package atom
import (
"encoding/xml"
"errors"
"fmt"
"strings"
)
type Link struct {
XMLName xml.Name `xml:"link"`
*CommonAttributes
Title string `xml:"title,attr,omitempty"`
Content string `xml:",chardata"` // undefinedContent in RFC4287
Href IRI `xml:"href,attr"`
Rel string `xml:"rel,attr,omitempty"`
Type MediaType `xml:"type,attr,omitempty"`
@ -17,9 +18,9 @@ type Link struct {
Length uint `xml:"length,attr,omitempty"`
}
// NewLink creates a new Link. It returns a *Link and an error.
func NewLink(href, content string) (*Link, error) {
return &Link{Href: IRI(href), Content: content}, nil
// NewLink creates a new Link. It returns a *Link.
func NewLink(href string) *Link {
return &Link{Href: IRI(href)}
}
// 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
}

14
logo.go
View File

@ -1,20 +1,24 @@
package atom
import (
"encoding/xml"
"errors"
"fmt"
"github.com/google/uuid"
)
type Logo struct {
XMLName xml.Name `xml:"logo"`
*CommonAttributes
URI IRI `xml:"uri"`
URI IRI `xml:",chardata"`
}
// NewLogo creates a new Logo. It returns a *Logo.
func NewLogo() *Logo {
return &Logo{URI: IRI(fmt.Sprint("urn:uuid:", uuid.New()))}
func NewLogo(uri IRI) (*Logo, error) {
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

View File

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

View File

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

View File

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

View File

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