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  }