aboutsummaryrefslogtreecommitdiff
path: root/vendor/github.com
diff options
context:
space:
mode:
authorChristine Dodrill <me@christine.website>2017-03-29 00:26:50 -0700
committerChristine Dodrill <me@christine.website>2017-03-29 00:26:50 -0700
commitb89387f6bbb010907dfa85ee0c0bab0cf8b34dfb (patch)
tree91deef87b78892c30839a99088362b70bf78eee4 /vendor/github.com
parenta59e48d9485f6c80f397df3bbcbe9b09ac54a435 (diff)
downloadxesite-b89387f6bbb010907dfa85ee0c0bab0cf8b34dfb.tar.xz
xesite-b89387f6bbb010907dfa85ee0c0bab0cf8b34dfb.zip
support RSS feed generation
Diffstat (limited to 'vendor/github.com')
-rw-r--r--vendor/github.com/Xe/ln/filter.go66
-rw-r--r--vendor/github.com/Xe/ln/formatter.go100
-rw-r--r--vendor/github.com/Xe/ln/logger.go141
-rw-r--r--vendor/github.com/Xe/ln/stack.go44
-rw-r--r--vendor/github.com/gorilla/feeds/atom.go163
-rw-r--r--vendor/github.com/gorilla/feeds/doc.go70
-rw-r--r--vendor/github.com/gorilla/feeds/feed.go106
-rw-r--r--vendor/github.com/gorilla/feeds/rss.go146
-rw-r--r--vendor/github.com/gorilla/feeds/uuid.go27
-rw-r--r--vendor/github.com/pkg/errors/errors.go269
-rw-r--r--vendor/github.com/pkg/errors/stack.go178
11 files changed, 1310 insertions, 0 deletions
diff --git a/vendor/github.com/Xe/ln/filter.go b/vendor/github.com/Xe/ln/filter.go
new file mode 100644
index 0000000..586efef
--- /dev/null
+++ b/vendor/github.com/Xe/ln/filter.go
@@ -0,0 +1,66 @@
+package ln
+
+import (
+ "io"
+ "sync"
+)
+
+// Filter interface for defining chain filters
+type Filter interface {
+ Apply(Event) bool
+ Run()
+ Close()
+}
+
+// FilterFunc allows simple functions to implement the Filter interface
+type FilterFunc func(e Event) bool
+
+// Apply implements the Filter interface
+func (ff FilterFunc) Apply(e Event) bool {
+ return ff(e)
+}
+
+// Run implements the Filter interface
+func (ff FilterFunc) Run() {}
+
+// Close implements the Filter interface
+func (ff FilterFunc) Close() {}
+
+// WriterFilter implements a filter, which arbitrarily writes to an io.Writer
+type WriterFilter struct {
+ sync.Mutex
+ Out io.Writer
+ Formatter Formatter
+}
+
+// NewWriterFilter creates a filter to add to the chain
+func NewWriterFilter(out io.Writer, format Formatter) *WriterFilter {
+ if format == nil {
+ format = DefaultFormatter
+ }
+ return &WriterFilter{
+ Out: out,
+ Formatter: format,
+ }
+}
+
+// Apply implements the Filter interface
+func (w *WriterFilter) Apply(e Event) bool {
+ output, err := w.Formatter.Format(e)
+ if err == nil {
+ w.Lock()
+ w.Out.Write(output)
+ w.Unlock()
+ }
+
+ return true
+}
+
+// Run implements the Filter interface
+func (w *WriterFilter) Run() {}
+
+// Close implements the Filter interface
+func (w *WriterFilter) Close() {}
+
+// NilFilter is safe to return as a Filter, but does nothing
+var NilFilter = FilterFunc(func(e Event) bool { return true })
diff --git a/vendor/github.com/Xe/ln/formatter.go b/vendor/github.com/Xe/ln/formatter.go
new file mode 100644
index 0000000..9d67139
--- /dev/null
+++ b/vendor/github.com/Xe/ln/formatter.go
@@ -0,0 +1,100 @@
+package ln
+
+import (
+ "bytes"
+ "fmt"
+ "time"
+)
+
+var (
+ // DefaultTimeFormat represents the way in which time will be formatted by default
+ DefaultTimeFormat = time.RFC3339
+)
+
+// Formatter defines the formatting of events
+type Formatter interface {
+ Format(Event) ([]byte, error)
+}
+
+// DefaultFormatter is the default way in which to format events
+var DefaultFormatter Formatter
+
+func init() {
+ DefaultFormatter = NewTextFormatter()
+}
+
+// TextFormatter formats events as key value pairs.
+// Any remaining text not wrapped in an instance of `F` will be
+// placed at the end.
+type TextFormatter struct {
+ TimeFormat string
+}
+
+// NewTextFormatter returns a Formatter that outputs as text.
+func NewTextFormatter() Formatter {
+ return &TextFormatter{TimeFormat: DefaultTimeFormat}
+}
+
+// Format implements the Formatter interface
+func (t *TextFormatter) Format(e Event) ([]byte, error) {
+ var writer bytes.Buffer
+
+ writer.WriteString("time=\"")
+ writer.WriteString(e.Time.Format(t.TimeFormat))
+ writer.WriteString("\"")
+
+ for k, v := range e.Data {
+ writer.WriteByte(' ')
+ if shouldQuote(k) {
+ writer.WriteString(fmt.Sprintf("%q", k))
+ } else {
+ writer.WriteString(k)
+ }
+
+ writer.WriteByte('=')
+
+ switch v.(type) {
+ case string:
+ vs, _ := v.(string)
+ if shouldQuote(vs) {
+ fmt.Fprintf(&writer, "%q", vs)
+ } else {
+ writer.WriteString(vs)
+ }
+ case error:
+ tmperr, _ := v.(error)
+ es := tmperr.Error()
+
+ if shouldQuote(es) {
+ fmt.Fprintf(&writer, "%q", es)
+ } else {
+ writer.WriteString(es)
+ }
+ case time.Time:
+ tmptime, _ := v.(time.Time)
+ writer.WriteString(tmptime.Format(time.RFC3339))
+ default:
+ fmt.Fprint(&writer, v)
+ }
+ }
+
+ if len(e.Message) > 0 {
+ fmt.Fprintf(&writer, " _msg=%q", e.Message)
+ }
+
+ writer.WriteByte('\n')
+ return writer.Bytes(), nil
+}
+
+func shouldQuote(s string) bool {
+ for _, b := range s {
+ if !((b >= 'A' && b <= 'Z') ||
+ (b >= 'a' && b <= 'z') ||
+ (b >= '0' && b <= '9') ||
+ (b == '-' || b == '.' || b == '#' ||
+ b == '/' || b == '_')) {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/Xe/ln/logger.go b/vendor/github.com/Xe/ln/logger.go
new file mode 100644
index 0000000..cdfe89e
--- /dev/null
+++ b/vendor/github.com/Xe/ln/logger.go
@@ -0,0 +1,141 @@
+package ln
+
+import (
+ "fmt"
+ "os"
+ "time"
+
+ "github.com/pkg/errors"
+)
+
+// Logger holds the current priority and list of filters
+type Logger struct {
+ Filters []Filter
+}
+
+// DefaultLogger is the default implementation of Logger
+var DefaultLogger *Logger
+
+func init() {
+ var defaultFilters []Filter
+
+ // Default to STDOUT for logging, but allow LN_OUT to change it.
+ out := os.Stdout
+ if os.Getenv("LN_OUT") == "<stderr>" {
+ out = os.Stderr
+ }
+
+ defaultFilters = append(defaultFilters, NewWriterFilter(out, nil))
+
+ DefaultLogger = &Logger{
+ Filters: defaultFilters,
+ }
+
+}
+
+// F is a key-value mapping for structured data.
+type F map[string]interface{}
+
+type Fer interface {
+ F() map[string]interface{}
+}
+
+// Event represents an event
+type Event struct {
+ Time time.Time
+ Data F
+ Message string
+}
+
+// Log is the generic logging method.
+func (l *Logger) Log(xs ...interface{}) {
+ var bits []interface{}
+ event := Event{Time: time.Now()}
+
+ addF := func(bf F) {
+ if event.Data == nil {
+ event.Data = bf
+ } else {
+ for k, v := range bf {
+ event.Data[k] = v
+ }
+ }
+ }
+
+ // Assemble the event
+ for _, b := range xs {
+ if bf, ok := b.(F); ok {
+ addF(bf)
+ } else if fer, ok := b.(Fer); ok {
+ addF(F(fer.F()))
+ } else {
+ bits = append(bits, b)
+ }
+ }
+
+ event.Message = fmt.Sprint(bits...)
+
+ if os.Getenv("LN_DEBUG_ALL_EVENTS") == "1" {
+ frame := callersFrame()
+ if event.Data == nil {
+ event.Data = make(F)
+ }
+ event.Data["_lineno"] = frame.lineno
+ event.Data["_function"] = frame.function
+ event.Data["_filename"] = frame.filename
+ }
+
+ l.filter(event)
+}
+
+func (l *Logger) filter(e Event) {
+ for _, f := range l.Filters {
+ if !f.Apply(e) {
+ return
+ }
+ }
+}
+
+// Error logs an error and information about the context of said error.
+func (l *Logger) Error(err error, xs ...interface{}) {
+ data := F{}
+ frame := callersFrame()
+
+ data["_lineno"] = frame.lineno
+ data["_function"] = frame.function
+ data["_filename"] = frame.filename
+ data["err"] = err
+
+ cause := errors.Cause(err)
+ if cause != nil {
+ data["cause"] = cause.Error()
+ }
+
+ xs = append(xs, data)
+
+ l.Log(xs...)
+}
+
+// Fatal logs this set of values, then exits with status code 1.
+func (l *Logger) Fatal(xs ...interface{}) {
+ l.Log(xs...)
+
+ os.Exit(1)
+}
+
+// Default Implementation
+
+// Log is the generic logging method.
+func Log(xs ...interface{}) {
+ DefaultLogger.Log(xs...)
+}
+
+// Error logs an error and information about the context of said error.
+func Error(err error, xs ...interface{}) {
+ DefaultLogger.Error(err, xs...)
+}
+
+// Fatal logs this set of values, then exits with status code 1.
+func Fatal(xs ...interface{}) {
+ DefaultLogger.Fatal(xs...)
+}
diff --git a/vendor/github.com/Xe/ln/stack.go b/vendor/github.com/Xe/ln/stack.go
new file mode 100644
index 0000000..1cf1e7a
--- /dev/null
+++ b/vendor/github.com/Xe/ln/stack.go
@@ -0,0 +1,44 @@
+package ln
+
+import (
+ "os"
+ "runtime"
+ "strings"
+)
+
+type frame struct {
+ filename string
+ function string
+ lineno int
+}
+
+// skips 2 frames, since Caller returns the current frame, and we need
+// the caller's caller.
+func callersFrame() frame {
+ var out frame
+ pc, file, line, ok := runtime.Caller(3)
+ if !ok {
+ return out
+ }
+ srcLoc := strings.LastIndex(file, "/src/")
+ if srcLoc >= 0 {
+ file = file[srcLoc+5:]
+ }
+ out.filename = file
+ out.function = functionName(pc)
+ out.lineno = line
+
+ return out
+}
+
+func functionName(pc uintptr) string {
+ fn := runtime.FuncForPC(pc)
+ if fn == nil {
+ return "???"
+ }
+ name := fn.Name()
+ beg := strings.LastIndex(name, string(os.PathSeparator))
+ return name[beg+1:]
+ // end := strings.LastIndex(name, string(os.PathSeparator))
+ // return name[end+1 : len(name)]
+}
diff --git a/vendor/github.com/gorilla/feeds/atom.go b/vendor/github.com/gorilla/feeds/atom.go
new file mode 100644
index 0000000..512976b
--- /dev/null
+++ b/vendor/github.com/gorilla/feeds/atom.go
@@ -0,0 +1,163 @@
+package feeds
+
+import (
+ "encoding/xml"
+ "fmt"
+ "net/url"
+ "strconv"
+ "time"
+)
+
+// Generates Atom feed as XML
+
+const ns = "http://www.w3.org/2005/Atom"
+
+type AtomPerson struct {
+ Name string `xml:"name,omitempty"`
+ Uri string `xml:"uri,omitempty"`
+ Email string `xml:"email,omitempty"`
+}
+
+type AtomSummary struct {
+ XMLName xml.Name `xml:"summary"`
+ Content string `xml:",chardata"`
+ Type string `xml:"type,attr"`
+}
+
+type AtomContent struct {
+ XMLName xml.Name `xml:"content"`
+ Content string `xml:",chardata"`
+ Type string `xml:"type,attr"`
+}
+
+type AtomAuthor struct {
+ XMLName xml.Name `xml:"author"`
+ AtomPerson
+}
+
+type AtomContributor struct {
+ XMLName xml.Name `xml:"contributor"`
+ AtomPerson
+}
+
+type AtomEntry struct {
+ XMLName xml.Name `xml:"entry"`
+ Xmlns string `xml:"xmlns,attr,omitempty"`
+ Title string `xml:"title"` // required
+ Updated string `xml:"updated"` // required
+ Id string `xml:"id"` // required
+ Category string `xml:"category,omitempty"`
+ Content *AtomContent
+ Rights string `xml:"rights,omitempty"`
+ Source string `xml:"source,omitempty"`
+ Published string `xml:"published,omitempty"`
+ Contributor *AtomContributor
+ Link *AtomLink // required if no child 'content' elements
+ Summary *AtomSummary // required if content has src or content is base64
+ Author *AtomAuthor // required if feed lacks an author
+}
+
+type AtomLink struct {
+ //Atom 1.0 <link rel="enclosure" type="audio/mpeg" title="MP3" href="http://www.example.org/myaudiofile.mp3" length="1234" />
+ XMLName xml.Name `xml:"link"`
+ Href string `xml:"href,attr"`
+ Rel string `xml:"rel,attr,omitempty"`
+ Type string `xml:"type,attr,omitempty"`
+ Length string `xml:"length,attr,omitempty"`
+}
+
+type AtomFeed struct {
+ XMLName xml.Name `xml:"feed"`
+ Xmlns string `xml:"xmlns,attr"`
+ Title string `xml:"title"` // required
+ Id string `xml:"id"` // required
+ Updated string `xml:"updated"` // required
+ Category string `xml:"category,omitempty"`
+ Icon string `xml:"icon,omitempty"`
+ Logo string `xml:"logo,omitempty"`
+ Rights string `xml:"rights,omitempty"` // copyright used
+ Subtitle string `xml:"subtitle,omitempty"`
+ Link *AtomLink
+ Author *AtomAuthor `xml:"author,omitempty"`
+ Contributor *AtomContributor
+ Entries []*AtomEntry
+}
+
+type Atom struct {
+ *Feed
+}
+
+func newAtomEntry(i *Item) *AtomEntry {
+ id := i.Id
+ // assume the description is html
+ c := &AtomContent{Content: i.Description, Type: "html"}
+
+ if len(id) == 0 {
+ // if there's no id set, try to create one, either from data or just a uuid
+ if len(i.Link.Href) > 0 && (!i.Created.IsZero() || !i.Updated.IsZero()) {
+ dateStr := anyTimeFormat("2006-01-02", i.Updated, i.Created)
+ host, path := i.Link.Href, "/invalid.html"
+ if url, err := url.Parse(i.Link.Href); err == nil {
+ host, path = url.Host, url.Path
+ }
+ id = fmt.Sprintf("tag:%s,%s:%s", host, dateStr, path)
+ } else {
+ id = "urn:uuid:" + NewUUID().String()
+ }
+ }
+ var name, email string
+ if i.Author != nil {
+ name, email = i.Author.Name, i.Author.Email
+ }
+
+ x := &AtomEntry{
+ Title: i.Title,
+ Link: &AtomLink{Href: i.Link.Href, Rel: i.Link.Rel, Type: i.Link.Type},
+ Content: c,
+ Id: id,
+ Updated: anyTimeFormat(time.RFC3339, i.Updated, i.Created),
+ }
+
+ intLength, err := strconv.ParseInt(i.Link.Length, 10, 64)
+
+ if err == nil && (intLength > 0 || i.Link.Type != "") {
+ i.Link.Rel = "enclosure"
+ x.Link = &AtomLink{Href: i.Link.Href, Rel: i.Link.Rel, Type: i.Link.Type, Length: i.Link.Length}
+ }
+
+ if len(name) > 0 || len(email) > 0 {
+ x.Author = &AtomAuthor{AtomPerson: AtomPerson{Name: name, Email: email}}
+ }
+ return x
+}
+
+// create a new AtomFeed with a generic Feed struct's data
+func (a *Atom) AtomFeed() *AtomFeed {
+ updated := anyTimeFormat(time.RFC3339, a.Updated, a.Created)
+ feed := &AtomFeed{
+ Xmlns: ns,
+ Title: a.Title,
+ Link: &AtomLink{Href: a.Link.Href, Rel: a.Link.Rel},
+ Subtitle: a.Description,
+ Id: a.Link.Href,
+ Updated: updated,
+ Rights: a.Copyright,
+ }
+ if a.Author != nil {
+ feed.Author = &AtomAuthor{AtomPerson: AtomPerson{Name: a.Author.Name, Email: a.Author.Email}}
+ }
+ for _, e := range a.Items {
+ feed.Entries = append(feed.Entries, newAtomEntry(e))
+ }
+ return feed
+}
+
+// return an XML-Ready object for an Atom object
+func (a *Atom) FeedXml() interface{} {
+ return a.AtomFeed()
+}
+
+// return an XML-ready object for an AtomFeed object
+func (a *AtomFeed) FeedXml() interface{} {
+ return a
+}
diff --git a/vendor/github.com/gorilla/feeds/doc.go b/vendor/github.com/gorilla/feeds/doc.go
new file mode 100644
index 0000000..5b005ea
--- /dev/null
+++ b/vendor/github.com/gorilla/feeds/doc.go
@@ -0,0 +1,70 @@
+/*
+Syndication (feed) generator library for golang.
+
+Installing
+
+ go get github.com/gorilla/feeds
+
+Feeds provides a simple, generic Feed interface with a generic Item object as well as RSS and Atom specific RssFeed and AtomFeed objects which allow access to all of each spec's defined elements.
+
+Examples
+
+Create a Feed and some Items in that feed using the generic interfaces:
+
+ import (
+ "time"
+ . "github.com/gorilla/feeds
+ )
+
+ now = time.Now()
+
+ feed := &Feed{
+ Title: "jmoiron.net blog",
+ Link: &Link{Href: "http://jmoiron.net/blog"},
+ Description: "discussion about tech, footie, photos",
+ Author: &Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"},
+ Created: now,
+ Copyright: "This work is copyright © Benjamin Button",
+ }
+
+ feed.Items = []*Item{
+ &Item{
+ Title: "Limiting Concurrency in Go",
+ Link: &Link{Href: "http://jmoiron.net/blog/limiting-concurrency-in-go/"},
+ Description: "A discussion on controlled parallelism in golang",
+ Author: &Author{Name: "Jason Moiron", Email: "jmoiron@jmoiron.net"},
+ Created: now,
+ },
+ &Item{
+ Title: "Logic-less Template Redux",
+ Link: &Link{Href: "http://jmoiron.net/blog/logicless-template-redux/"},
+ Description: "More thoughts on logicless templates",
+ Created: now,
+ },
+ &Item{
+ Title: "Idiomatic Code Reuse in Go",
+ Link: &Link{Href: "http://jmoiron.net/blog/idiomatic-code-reuse-in-go/"},
+ Description: "How to use interfaces <em>effectively</em>",
+ Created: now,
+ },
+ }
+
+From here, you can output Atom or RSS versions of this feed easily
+
+ atom, err := feed.ToAtom()
+ rss, err := feed.ToRss()
+
+You can also get access to the underlying objects that feeds uses to export its XML
+
+ atomFeed := &Atom{feed}.AtomFeed()
+ rssFeed := &Rss{feed}.RssFeed()
+
+From here, you can modify or add each syndication's specific fields before outputting
+
+ atomFeed.Subtitle = "plays the blues"
+ atom, err := ToXML(atomFeed)
+ rssFeed.Generator = "gorilla/feeds v1.0 (github.com/gorilla/feeds)"
+ rss, err := ToXML(rssFeed)
+
+*/
+package feeds
diff --git a/vendor/github.com/gorilla/feeds/feed.go b/vendor/github.com/gorilla/feeds/feed.go
new file mode 100644
index 0000000..fe4833e
--- /dev/null
+++ b/vendor/github.com/gorilla/feeds/feed.go
@@ -0,0 +1,106 @@
+package feeds
+
+import (
+ "encoding/xml"
+ "io"
+ "time"
+)
+
+type Link struct {
+ Href, Rel, Type, Length string
+}
+
+type Author struct {
+ Name, Email string
+}
+
+type Item struct {
+ Title string
+ Link *Link
+ Author *Author
+ Description string // used as description in rss, summary in atom
+ Id string // used as guid in rss, id in atom
+ Updated time.Time
+ Created time.Time
+}
+
+type Feed struct {
+ Title string
+ Link *Link
+ Description string
+ Author *Author
+ Updated time.Time
+ Created time.Time
+ Id string
+ Subtitle string
+ Items []*Item
+ Copyright string
+}
+
+// add a new Item to a Feed
+func (f *Feed) Add(item *Item) {
+ f.Items = append(f.Items, item)
+}
+
+// returns the first non-zero time formatted as a string or ""
+func anyTimeFormat(format string, times ...time.Time) string {
+ for _, t := range times {
+ if !t.IsZero() {
+ return t.Format(format)
+ }
+ }
+ return ""
+}
+
+// interface used by ToXML to get a object suitable for exporting XML.
+type XmlFeed interface {
+ FeedXml() interface{}
+}
+
+// turn a feed object (either a Feed, AtomFeed, or RssFeed) into xml
+// returns an error if xml marshaling fails
+func ToXML(feed XmlFeed) (string, error) {
+ x := feed.FeedXml()
+ data, err := xml.MarshalIndent(x, "", " ")
+ if err != nil {
+ return "", err
+ }
+ // strip empty line from default xml header
+ s := xml.Header[:len(xml.Header)-1] + string(data)
+ return s, nil
+}
+
+// Write a feed object (either a Feed, AtomFeed, or RssFeed) as XML into
+// the writer. Returns an error if XML marshaling fails.
+func WriteXML(feed XmlFeed, w io.Writer) error {
+ x := feed.FeedXml()
+ // write default xml header, without the newline
+ if _, err := w.Write([]byte(xml.Header[:len(xml.Header)-1])); err != nil {
+ return err
+ }
+ e := xml.NewEncoder(w)
+ e.Indent("", " ")
+ return e.Encode(x)
+}
+
+// creates an Atom representation of this feed
+func (f *Feed) ToAtom() (string, error) {
+ a := &Atom{f}
+ return ToXML(a)
+}
+
+// Writes an Atom representation of this feed to the writer.
+func (f *Feed) WriteAtom(w io.Writer) error {
+ return WriteXML(&Atom{f}, w)
+}
+
+// creates an Rss representation of this feed
+func (f *Feed) ToRss() (string, error) {
+ r := &Rss{f}
+ return ToXML(r)
+}
+
+// Writes an RSS representation of this feed to the writer.
+func (f *Feed) WriteRss(w io.Writer) error {
+ return WriteXML(&Rss{f}, w)
+}
diff --git a/vendor/github.com/gorilla/feeds/rss.go b/vendor/github.com/gorilla/feeds/rss.go
new file mode 100644
index 0000000..3381f74
--- /dev/null
+++ b/vendor/github.com/gorilla/feeds/rss.go
@@ -0,0 +1,146 @@
+package feeds
+
+// rss support
+// validation done according to spec here:
+// http://cyber.law.harvard.edu/rss/rss.html
+
+import (
+ "encoding/xml"
+ "fmt"
+ "strconv"
+ "time"
+)
+
+// private wrapper around the RssFeed which gives us the <rss>..</rss> xml
+type rssFeedXml struct {
+ XMLName xml.Name `xml:"rss"`
+ Version string `xml:"version,attr"`
+ Channel *RssFeed
+}
+
+type RssImage struct {
+ XMLName xml.Name `xml:"image"`
+ Url string `xml:"url"`
+ Title string `xml:"title"`
+ Link string `xml:"link"`
+ Width int `xml:"width,omitempty"`
+ Height int `xml:"height,omitempty"`
+}
+
+type RssTextInput struct {
+ XMLName xml.Name `xml:"textInput"`
+ Title string `xml:"title"`
+ Description string `xml:"description"`
+ Name string `xml:"name"`
+ Link string `xml:"link"`
+}
+
+type RssFeed struct {
+ XMLName xml.Name `xml:"channel"`
+ Title string `xml:"title"` // required
+ Link string `xml:"link"` // required
+ Description string `xml:"description"` // required
+ Language string `xml:"language,omitempty"`
+ Copyright string `xml:"copyright,omitempty"`
+ ManagingEditor string `xml:"managingEditor,omitempty"` // Author used
+ WebMaster string `xml:"webMaster,omitempty"`
+ PubDate string `xml:"pubDate,omitempty"` // created or updated
+ LastBuildDate string `xml:"lastBuildDate,omitempty"` // updated used
+ Category string `xml:"category,omitempty"`
+ Generator string `xml:"generator,omitempty"`
+ Docs string `xml:"docs,omitempty"`
+ Cloud string `xml:"cloud,omitempty"`
+ Ttl int `xml:"ttl,omitempty"`
+ Rating string `xml:"rating,omitempty"`
+ SkipHours string `xml:"skipHours,omitempty"`
+ SkipDays string `xml:"skipDays,omitempty"`
+ Image *RssImage
+ TextInput *RssTextInput
+ Items []*RssItem
+}
+
+type RssItem struct {
+ XMLName xml.Name `xml:"item"`
+ Title string `xml:"title"` // required
+ Link string `xml:"link"` // required
+ Description string `xml:"description"` // required
+ Author string `xml:"author,omitempty"`
+ Category string `xml:"category,omitempty"`
+ Comments string `xml:"comments,omitempty"`
+ Enclosure *RssEnclosure
+ Guid string `xml:"guid,omitempty"` // Id used
+ PubDate string `xml:"pubDate,omitempty"` // created or updated
+ Source string `xml:"source,omitempty"`
+}
+
+type RssEnclosure struct {
+ //RSS 2.0 <enclosure url="http://example.com/file.mp3" length="123456789" type="audio/mpeg" />
+ XMLName xml.Name `xml:"enclosure"`
+ Url string `xml:"url,attr"`
+ Length string `xml:"length,attr"`
+ Type string `xml:"type,attr"`
+}
+
+type Rss struct {
+ *Feed
+}
+
+// create a new RssItem with a generic Item struct's data
+func newRssItem(i *Item) *RssItem {
+ item := &RssItem{
+ Title: i.Title,
+ Link: i.Link.Href,
+ Description: i.Description,
+ Guid: i.Id,
+ PubDate: anyTimeFormat(time.RFC1123Z, i.Created, i.Updated),
+ }
+
+ intLength, err := strconv.ParseInt(i.Link.Length, 10, 64)
+
+ if err == nil && (intLength > 0 || i.Link.Type != "") {
+ item.Enclosure = &RssEnclosure{Url: i.Link.Href, Type: i.Link.Type, Length: i.Link.Length}
+ }
+ if i.Author != nil {
+ item.Author = i.Author.Name
+ }
+ return item
+}
+
+// create a new RssFeed with a generic Feed struct's data
+func (r *Rss) RssFeed() *RssFeed {
+ pub := anyTimeFormat(time.RFC1123Z, r.Created, r.Updated)
+ build := anyTimeFormat(time.RFC1123Z, r.Updated)
+ author := ""
+ if r.Author != nil {
+ author = r.Author.Email
+ if len(r.Author.Name) > 0 {
+ author = fmt.Sprintf("%s (%s)", r.Author.Email, r.Author.Name)
+ }
+ }
+
+ channel := &RssFeed{
+ Title: r.Title,
+ Link: r.Link.Href,
+ Description: r.Description,
+ ManagingEditor: author,
+ PubDate: pub,
+ LastBuildDate: build,
+ Copyright: r.Copyright,
+ }
+ for _, i := range r.Items {
+ channel.Items = append(channel.Items, newRssItem(i))
+ }
+ return channel
+}
+
+// return an XML-Ready object for an Rss object
+func (r *Rss) FeedXml() interface{} {
+ // only generate version 2.0 feeds for now
+ return r.RssFeed().FeedXml()
+
+}
+
+// return an XML-ready object for an RssFeed object
+func (r *RssFeed) FeedXml() interface{} {
+ return &rssFeedXml{Version: "2.0", Channel: r}
+}
diff --git a/vendor/github.com/gorilla/feeds/uuid.go b/vendor/github.com/gorilla/feeds/uuid.go
new file mode 100644
index 0000000..51bbafe
--- /dev/null
+++ b/vendor/github.com/gorilla/feeds/uuid.go
@@ -0,0 +1,27 @@
+package feeds
+
+// relevant bits from https://github.com/abneptis/GoUUID/blob/master/uuid.go
+
+import (
+ "crypto/rand"
+ "fmt"
+)
+
+type UUID [16]byte
+
+// create a new uuid v4
+func NewUUID() *UUID {
+ u := &UUID{}
+ _, err := rand.Read(u[:16])
+ if err != nil {
+ panic(err)
+ }
+
+ u[8] = (u[8] | 0x80) & 0xBf
+ u[6] = (u[6] | 0x40) & 0x4f
+ return u
+}
+
+func (u *UUID) String() string {
+ return fmt.Sprintf("%x-%x-%x-%x-%x", u[:4], u[4:6], u[6:8], u[8:10], u[10:])
+}
diff --git a/vendor/github.com/pkg/errors/errors.go b/vendor/github.com/pkg/errors/errors.go
new file mode 100644
index 0000000..842ee80
--- /dev/null
+++ b/vendor/github.com/pkg/errors/errors.go
@@ -0,0 +1,269 @@
+// Package errors provides simple error handling primitives.
+//
+// The traditional error handling idiom in Go is roughly akin to
+//
+// if err != nil {
+// return err
+// }
+//
+// which applied recursively up the call stack results in error reports
+// without context or debugging information. The errors package allows
+// programmers to add context to the failure path in their code in a way
+// that does not destroy the original value of the error.
+//
+// Adding context to an error
+//
+// The errors.Wrap function returns a new error that adds context to the
+// original error by recording a stack trace at the point Wrap is called,
+// and the supplied message. For example
+//
+// _, err := ioutil.ReadAll(r)
+// if err != nil {
+// return errors.Wrap(err, "read failed")
+// }
+//
+// If additional control is required the errors.WithStack and errors.WithMessage
+// functions destructure errors.Wrap into its component operations of annotating
+// an error with a stack trace and an a message, respectively.
+//
+// Retrieving the cause of an error
+//
+// Using errors.Wrap constructs a stack of errors, adding context to the
+// preceding error. Depending on the nature of the error it may be necessary
+// to reverse the operation of errors.Wrap to retrieve the original error
+// for inspection. Any error value which implements this interface
+//
+// type causer interface {
+// Cause() error
+// }
+//
+// can be inspected by errors.Cause. errors.Cause will recursively retrieve
+// the topmost error which does not implement causer, which is assumed to be
+// the original cause. For example:
+//
+// switch err := errors.Cause(err).(type) {
+// case *MyError:
+// // handle specifically
+// default:
+// // unknown error
+// }
+//
+// causer interface is not exported by this package, but is considered a part
+// of stable public API.
+//
+// Formatted printing of errors
+//
+// All error values returned from this package implement fmt.Formatter and can
+// be formatted by the fmt package. The following verbs are supported
+//
+// %s print the error. If the error has a Cause it will be
+// printed recursively
+// %v see %s
+// %+v extended format. Each Frame of the error's StackTrace will
+// be printed in detail.
+//
+// Retrieving the stack trace of an error or wrapper
+//
+// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
+// invoked. This information can be retrieved with the following interface.
+//
+// type stackTracer interface {
+// StackTrace() errors.StackTrace
+// }
+//
+// Where errors.StackTrace is defined as
+//
+// type StackTrace []Frame
+//
+// The Frame type represents a call site in the stack trace. Frame supports
+// the fmt.Formatter interface that can be used for printing information about
+// the stack trace of this error. For example:
+//
+// if err, ok := err.(stackTracer); ok {
+// for _, f := range err.StackTrace() {
+// fmt.Printf("%+s:%d", f)
+// }
+// }
+//
+// stackTracer interface is not exported by this package, but is considered a part
+// of stable public API.
+//
+// See the documentation for Frame.Format for more details.
+package errors
+
+import (
+ "fmt"
+ "io"
+)
+
+// New returns an error with the supplied message.
+// New also records the stack trace at the point it was called.
+func New(message string) error {
+ return &fundamental{
+ msg: message,
+ stack: callers(),
+ }
+}
+
+// Errorf formats according to a format specifier and returns the string
+// as a value that satisfies error.
+// Errorf also records the stack trace at the point it was called.
+func Errorf(format string, args ...interface{}) error {
+ return &fundamental{
+ msg: fmt.Sprintf(format, args...),
+ stack: callers(),
+ }
+}
+
+// fundamental is an error that has a message and a stack, but no caller.
+type fundamental struct {
+ msg string
+ *stack
+}
+
+func (f *fundamental) Error() string { return f.msg }
+
+func (f *fundamental) Format(s fmt.State, verb rune) {
+ switch verb {
+ case 'v':
+ if s.Flag('+') {
+ io.WriteString(s, f.msg)
+ f.stack.Format(s, verb)
+ return
+ }
+ fallthrough
+ case 's':
+ io.WriteString(s, f.msg)
+ case 'q':
+ fmt.Fprintf(s, "%q", f.msg)
+ }
+}
+
+// WithStack annotates err with a stack trace at the point WithStack was called.
+// If err is nil, WithStack returns nil.
+func WithStack(err error) error {
+ if err == nil {
+ return nil
+ }
+ return &withStack{
+ err,
+ ca