atom/feed.go

278 lines
7.8 KiB
Go
Raw Normal View History

2024-10-16 21:28:04 +02:00
package atom
2024-10-13 17:19:40 +02:00
import (
"encoding/xml"
"fmt"
"time"
2024-10-13 17:19:40 +02:00
)
type Feed struct {
XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"`
*CommonAttributes
2024-10-17 20:10:18 +02:00
Authors []*Person `xml:"author,omitempty"`
Categories []*Category `xml:",omitempty"`
Contributors []*Person `xml:"contributor,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"`
2024-10-13 17:19:40 +02:00
Updated *Date `xml:"updated"`
2024-10-13 20:42:17 +02:00
Extensions []*ExtensionElement `xml:",any,omitempty"`
2024-10-17 20:10:18 +02:00
Entries []*Entry `xml:",omitempty"`
2024-10-13 17:19:40 +02:00
}
// NewFeed creates a new Feed. It returns a *Feed.
func NewFeed(title string) *Feed {
return &Feed{
CommonAttributes: newCommonAttributes(),
ID: NewID(NewURN()),
Title: NewText("text", title),
Updated: NewDate(time.Now()),
}
}
// AddAuthor adds the Person as an author to the Feed.
func (f *Feed) AddAuthor(p *Person) {
addToSlice(&f.Authors, p)
f.Updated = NewDate(time.Now())
}
2024-10-20 12:41:09 +02:00
// DeleteAuthor deletes the Person at index from the Feed. It return an error.
2024-10-20 12:35:26 +02:00
func (f *Feed) DeleteAuthor(index int) error {
if err := deleteFromSlice(&f.Authors, index); err != nil {
return fmt.Errorf("error deleting author %v from entry %v: %v", index, f.ID.URI, err)
}
f.Updated = NewDate(time.Now())
return nil
}
// AddCategory adds the Category to the Feed.
func (f *Feed) AddCategory(c *Category) {
addToSlice(&f.Categories, c)
f.Updated = NewDate(time.Now())
}
2024-10-20 12:41:09 +02:00
// DeleteCategory deletes the Category at index from the Feed. It return an
// error.
2024-10-20 12:35:26 +02:00
func (f *Feed) DeleteCategory(index int) error {
if err := deleteFromSlice(&f.Categories, index); err != nil {
return fmt.Errorf("error deleting category %v from entry %v: %v", index, f.ID.URI, err)
}
f.Updated = NewDate(time.Now())
return nil
}
// AddContributor adds the Person as a contributor to the Feed.
func (f *Feed) AddContributor(c *Person) {
addToSlice(&f.Contributors, c)
f.Updated = NewDate(time.Now())
}
2024-10-20 12:41:09 +02:00
// DeleteContributor deletes the Person at index from the Feed. It return an
// error.
2024-10-20 12:35:26 +02:00
func (f *Feed) DeleteContributor(index int) error {
if err := deleteFromSlice(&f.Contributors, index); err != nil {
return fmt.Errorf("error deleting contributor %v from entry %v: %v", index, f.ID.URI, err)
}
f.Updated = NewDate(time.Now())
return nil
}
// AddLink adds the Link to the Feed. There should be one Link with Rel "self".
func (f *Feed) AddLink(l *Link) {
addToSlice(&f.Links, l)
f.Updated = NewDate(time.Now())
}
2024-10-20 12:41:09 +02:00
// DeleteLink deletes the Link at index from the Feed. It return an error.
2024-10-20 12:35:26 +02:00
func (f *Feed) DeleteLink(index int) error {
if err := deleteFromSlice(&f.Links, index); err != nil {
return fmt.Errorf("error deleting link %v from entry %v: %v", index, f.ID.URI, err)
}
f.Updated = NewDate(time.Now())
return nil
}
// AddExtension adds the Extension to the Feed.
func (f *Feed) AddExtension(e *ExtensionElement) {
addToSlice(&f.Extensions, e)
f.Updated = NewDate(time.Now())
}
2024-10-20 12:41:09 +02:00
// DeleteExtension deletes the Extension at index from the Feed. It return an
// error.
2024-10-20 12:35:26 +02:00
func (f *Feed) DeleteExtension(index int) error {
if err := deleteFromSlice(&f.Extensions, index); err != nil {
return fmt.Errorf("error deleting extension %v from entry %v: %v", index, f.ID.URI, err)
}
f.Updated = NewDate(time.Now())
return nil
}
// AddEntry adds the Entry to the Feed.
func (f *Feed) AddEntry(e *Entry) {
addToSlice(&f.Entries, e)
f.Updated = NewDate(time.Now())
}
2024-10-20 12:41:09 +02:00
// DeleteEntry deletes the Entry at index from the Feed. It return an error.
2024-10-20 12:35:26 +02:00
func (f *Feed) DeleteEntry(index int) error {
if err := deleteFromSlice(&f.Entries, index); err != nil {
return fmt.Errorf("error deleting entry %v from entry %v: %v", index, f.ID.URI, err)
}
f.Updated = NewDate(time.Now())
return nil
}
// DeleteEntryByURI deletes the Entry from the Feed. It return an error.
func (f *Feed) DeleteEntryByURI(uri string) error {
if !isValidIRI(uri) {
return fmt.Errorf("error deleting entry from feed %v: uri %v invalid", f.ID.URI, uri)
}
index := -1
for i, e := range f.Entries {
if e.ID.URI == uri {
index = i
break
}
}
if index < 0 {
return fmt.Errorf("error deleting entry from feed %v: id %v not found", f.ID.URI, uri)
}
f.Entries = append(f.Entries[:index], f.Entries[index+1:]...)
f.Updated = NewDate(time.Now())
return nil
2024-10-15 16:20:11 +02:00
}
2024-10-16 19:59:28 +02:00
// Check checks the Feed for incompatibilities with RFC4287. It returns an
// error.
2024-10-13 17:19:40 +02:00
func (f *Feed) Check() error {
if f.ID == nil {
2024-10-18 19:04:08 +02:00
return fmt.Errorf("no id element of feed %v", f)
2024-10-13 17:19:40 +02:00
} else {
if err := f.ID.Check(); err != nil {
2024-10-18 19:04:08 +02:00
return fmt.Errorf("id element of feed %v: %v", f, err)
2024-10-13 17:19:40 +02:00
}
}
2024-10-15 18:40:32 +02:00
// atom:feed elements MUST contain one or more atom:author elements, unless
// all of the atom:feed element's child atom:entry elements contain at
// least one atom:author element.
2024-10-13 17:19:40 +02:00
if f.Authors == nil {
for _, e := range f.Entries {
if err := e.checkAuthors(false); err != nil {
return fmt.Errorf("no authors set in feed %v: %v", f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
} else {
for i, a := range f.Authors {
if err := a.Check(); err != nil {
return fmt.Errorf("author element %v of feed %v: %v", i, f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
}
for i, c := range f.Categories {
if err := c.Check(); err != nil {
return fmt.Errorf("category element %v of feed %v: %v", i, f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
for i, c := range f.Contributors {
if err := c.Check(); err != nil {
return fmt.Errorf("contributor element %v of feed %v: %v", i, f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
if f.Generator != nil {
if err := f.Generator.Check(); err != nil {
return fmt.Errorf("generator element of feed %v: %v", f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
if f.Icon != nil {
if err := f.Icon.Check(); err != nil {
return fmt.Errorf("icon element of feed %v: %v", f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
for i, l := range f.Links {
if err := l.Check(); err != nil {
return fmt.Errorf("link element %v of feed %v: %v", i, f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
if hasAlternateDuplicateLinks(f.Links) {
return fmt.Errorf("links with a rel attribute value of \"alternate\" and duplicate type and hreflang attribute values found in feed %v", f.ID.URI)
}
2024-10-13 17:19:40 +02:00
if f.Logo != nil {
if err := f.Logo.Check(); err != nil {
return fmt.Errorf("logo element of feed %v: %v", f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
if f.Rights != nil {
if err := f.Rights.Check(); err != nil {
return fmt.Errorf("rights element of feed %v: %v", f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
if f.Subtitle != nil {
if err := f.Subtitle.Check(); err != nil {
return fmt.Errorf("subtitle element of feed %v: %v", f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
if f.Title == nil {
return fmt.Errorf("no title element of feed %v", f.ID.URI)
2024-10-13 17:19:40 +02:00
} else {
if err := f.Title.Check(); err != nil {
return fmt.Errorf("title element of feed %v: %v", f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
if f.Updated == nil {
return fmt.Errorf("no updated element of feed %v", f.ID.URI)
2024-10-13 17:19:40 +02:00
} else {
if err := f.Updated.Check(); err != nil {
return fmt.Errorf("updated element of feed %v: %v", f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
for i, x := range f.Extensions {
if err := x.Check(); err != nil {
return fmt.Errorf("extension element %v of feed %v: %v", i, f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
for i, n := range f.Entries {
if err := n.Check(); err != nil {
return fmt.Errorf("entry element %v of feed %v: %v", i, f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
}
return nil
}
2024-10-16 19:59:28 +02:00
// ToXML converts the Feed to XML. It returns a string and an error.
2024-10-13 17:19:40 +02:00
func (f *Feed) ToXML(encoding string) (string, error) {
xml, err := xml.MarshalIndent(f, "", " ")
if err != nil {
return "", fmt.Errorf("error xml encoding feed %v: %v", f.ID.URI, err)
2024-10-13 17:19:40 +02:00
}
return fmt.Sprintln(`<?xml version="1.0" encoding="`+encoding+`"?>`) + string(xml), nil
}