diff options
| author | Christine Dodrill <me@christine.website> | 2017-03-29 00:26:50 -0700 |
|---|---|---|
| committer | Christine Dodrill <me@christine.website> | 2017-03-29 00:26:50 -0700 |
| commit | b89387f6bbb010907dfa85ee0c0bab0cf8b34dfb (patch) | |
| tree | 91deef87b78892c30839a99088362b70bf78eee4 /vendor/github.com | |
| parent | a59e48d9485f6c80f397df3bbcbe9b09ac54a435 (diff) | |
| download | xesite-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.go | 66 | ||||
| -rw-r--r-- | vendor/github.com/Xe/ln/formatter.go | 100 | ||||
| -rw-r--r-- | vendor/github.com/Xe/ln/logger.go | 141 | ||||
| -rw-r--r-- | vendor/github.com/Xe/ln/stack.go | 44 | ||||
| -rw-r--r-- | vendor/github.com/gorilla/feeds/atom.go | 163 | ||||
| -rw-r--r-- | vendor/github.com/gorilla/feeds/doc.go | 70 | ||||
| -rw-r--r-- | vendor/github.com/gorilla/feeds/feed.go | 106 | ||||
| -rw-r--r-- | vendor/github.com/gorilla/feeds/rss.go | 146 | ||||
| -rw-r--r-- | vendor/github.com/gorilla/feeds/uuid.go | 27 | ||||
| -rw-r--r-- | vendor/github.com/pkg/errors/errors.go | 269 | ||||
| -rw-r--r-- | vendor/github.com/pkg/errors/stack.go | 178 |
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 |
