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