1package feeds
2
3import (
4	"encoding/json"
5	"encoding/xml"
6	"io"
7	"sort"
8	"time"
9)
10
11type Link struct {
12	Href, Rel, Type, Length string
13}
14
15type Author struct {
16	Name, Email string
17}
18
19type Image struct {
20	Url, Title, Link string
21	Width, Height    int
22}
23
24type Enclosure struct {
25	Url, Length, Type string
26}
27
28type Item struct {
29	Title       string
30	Link        *Link
31	Source      *Link
32	Author      *Author
33	Description string // used as description in rss, summary in atom
34	Id          string // used as guid in rss, id in atom
35	Updated     time.Time
36	Created     time.Time
37	Enclosure   *Enclosure
38	Content     string
39}
40
41type Feed struct {
42	Title       string
43	Link        *Link
44	Description string
45	Author      *Author
46	Updated     time.Time
47	Created     time.Time
48	Id          string
49	Subtitle    string
50	Items       []*Item
51	Copyright   string
52	Image       *Image
53}
54
55// add a new Item to a Feed
56func (f *Feed) Add(item *Item) {
57	f.Items = append(f.Items, item)
58}
59
60// returns the first non-zero time formatted as a string or ""
61func anyTimeFormat(format string, times ...time.Time) string {
62	for _, t := range times {
63		if !t.IsZero() {
64			return t.Format(format)
65		}
66	}
67	return ""
68}
69
70// interface used by ToXML to get a object suitable for exporting XML.
71type XmlFeed interface {
72	FeedXml() interface{}
73}
74
75// turn a feed object (either a Feed, AtomFeed, or RssFeed) into xml
76// returns an error if xml marshaling fails
77func ToXML(feed XmlFeed) (string, error) {
78	x := feed.FeedXml()
79	data, err := xml.MarshalIndent(x, "", "  ")
80	if err != nil {
81		return "", err
82	}
83	// strip empty line from default xml header
84	s := xml.Header[:len(xml.Header)-1] + string(data)
85	return s, nil
86}
87
88// WriteXML writes a feed object (either a Feed, AtomFeed, or RssFeed) as XML into
89// the writer. Returns an error if XML marshaling fails.
90func WriteXML(feed XmlFeed, w io.Writer) error {
91	x := feed.FeedXml()
92	// write default xml header, without the newline
93	if _, err := w.Write([]byte(xml.Header[:len(xml.Header)-1])); err != nil {
94		return err
95	}
96	e := xml.NewEncoder(w)
97	e.Indent("", "  ")
98	return e.Encode(x)
99}
100
101// creates an Atom representation of this feed
102func (f *Feed) ToAtom() (string, error) {
103	a := &Atom{f}
104	return ToXML(a)
105}
106
107// WriteAtom writes an Atom representation of this feed to the writer.
108func (f *Feed) WriteAtom(w io.Writer) error {
109	return WriteXML(&Atom{f}, w)
110}
111
112// creates an Rss representation of this feed
113func (f *Feed) ToRss() (string, error) {
114	r := &Rss{f}
115	return ToXML(r)
116}
117
118// WriteRss writes an RSS representation of this feed to the writer.
119func (f *Feed) WriteRss(w io.Writer) error {
120	return WriteXML(&Rss{f}, w)
121}
122
123// ToJSON creates a JSON Feed representation of this feed
124func (f *Feed) ToJSON() (string, error) {
125	j := &JSON{f}
126	return j.ToJSON()
127}
128
129// WriteJSON writes an JSON representation of this feed to the writer.
130func (f *Feed) WriteJSON(w io.Writer) error {
131	j := &JSON{f}
132	feed := j.JSONFeed()
133
134	e := json.NewEncoder(w)
135	e.SetIndent("", "  ")
136	return e.Encode(feed)
137}
138
139// Sort sorts the Items in the feed with the given less function.
140func (f *Feed) Sort(less func(a, b *Item) bool) {
141	lessFunc := func(i, j int) bool {
142		return less(f.Items[i], f.Items[j])
143	}
144	sort.SliceStable(f.Items, lessFunc)
145}
146