package atomfeed import ( "encoding/xml" "errors" "fmt" "time" ) 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"` 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"` 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"` } // NewFeed creates a new feed. func NewFeed(title string) (*Feed, error) { text, err := NewText("text", title) if err != nil { return nil, fmt.Errorf("error creating new feed: %v", err) } return &Feed{ ID: NewID(), Title: text, Updated: NewDate(time.Now()), }, nil } // AddAuthor adds the person as an author to the feed. func (f *Feed) AddAuthor(p *Person) { if f.Authors == nil { f.Authors = make([]*Person, 1) f.Authors[0] = p } else { f.Authors = append(f.Authors, p) } f.Updated.DateTime = DateTime(time.Now()) } // AddCategory adds the category to the feed. func (f *Feed) AddCategory(c *Category) { if f.Categories == nil { f.Categories = make([]*Category, 1) f.Categories[0] = c } else { f.Categories = append(f.Categories, c) } f.Updated.DateTime = DateTime(time.Now()) } // AddContributor adds the contributor to the feed. func (f *Feed) AddContributor(c *Person) { if f.Contributors == nil { f.Contributors = make([]*Person, 1) f.Contributors[0] = c } else { f.Contributors = append(f.Contributors, c) } f.Updated.DateTime = DateTime(time.Now()) } // AddLink adds the link to the feed. // There should be one link with rel "self". func (f *Feed) AddLink(l *Link) { if f.Links == nil { f.Links = make([]*Link, 1) f.Links[0] = l } else { f.Links = append(f.Links, l) } f.Updated.DateTime = DateTime(time.Now()) } // AddExtension adds the extension to the feed. func (f *Feed) AddExtension(e *ExtensionElement) { if f.Extensions == nil { f.Extensions = make([]*ExtensionElement, 1) f.Extensions[0] = e } else { f.Extensions = append(f.Extensions, e) } f.Updated.DateTime = DateTime(time.Now()) } // AddEntry adds the entry to the feed. func (f *Feed) AddEntry(e *Entry) { if f.Entries == nil { f.Entries = make([]*Entry, 1) f.Entries[0] = e } else { f.Entries = append(f.Entries, e) } f.Updated.DateTime = DateTime(time.Now()) } // Check checks the feed for incompatibilities with RFC4287. func (f *Feed) Check() error { if f.ID == nil { return errors.New("no id element of feed") } else { if err := f.ID.Check(); err != nil { return fmt.Errorf("id element of feed: %v", err) } } // 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. if f.Authors == nil { for _, e := range f.Entries { if err := e.checkAuthors(); err != nil { return fmt.Errorf("no authors set in feed %v: %v", f.ID.URI, err) } } } 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) } } } 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) } } 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) } } if f.Generator != nil { if err := f.Generator.Check(); err != nil { return fmt.Errorf("generator element of feed %v: %v", f.ID.URI, err) } } if f.Icon != nil { if err := f.Icon.Check(); err != nil { return fmt.Errorf("icon element of feed %v: %v", f.ID.URI, err) } } 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) } } if hasAlternateDuplicateLinks(f.Links) { return fmt.Errorf("links with with a rel attribute value of \"alternate\" and duplicate type and hreflang attribute values found in feed %v", f.ID.URI) } if f.Logo != nil { if err := f.Logo.Check(); err != nil { return fmt.Errorf("logo element of feed %v: %v", f.ID.URI, err) } } if f.Rights != nil { if err := f.Rights.Check(); err != nil { return fmt.Errorf("rights element of feed %v: %v", f.ID.URI, err) } } if f.Subtitle != nil { if err := f.Subtitle.Check(); err != nil { return fmt.Errorf("subtitle element of feed %v: %v", f.ID.URI, err) } } if f.Title == nil { return fmt.Errorf("no title element of feed %v", f.ID.URI) } else { if err := f.Title.Check(); err != nil { return fmt.Errorf("title element of feed %v: %v", f.ID.URI, err) } } if f.Updated == nil { return fmt.Errorf("no updated element of feed %v", f.ID) } else { if err := f.Updated.Check(); err != nil { return fmt.Errorf("updated element of feed %v: %v", f.ID.URI, err) } } 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) } } 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) } } return nil } // TODO: Create complete link or delete func (f *Feed) Standardize() { if f.Links == nil { f.Links = make([]*Link, 1) f.Links[0] = &Link{Rel: "self"} } else { selfExists := false for _, l := range f.Links { if l.Rel == "self" { selfExists = true break } } if !selfExists { f.Links = append(f.Links, &Link{Rel: "self"}) } } } // ToXML converts the feed to XML. func (f *Feed) ToXML(encoding string) (string, error) { xml, err := xml.MarshalIndent(f, "", " ") if err != nil { return "", fmt.Errorf("error xml encoding feed: %v", err) } return fmt.Sprintln(``) + string(xml), nil }