Files

82 lines
2.0 KiB
Go

package spc
import (
"encoding/xml"
"fmt"
"strings"
"time"
)
// RSSFeed is a minimal view of the optional SPC RSS feed.
type RSSFeed struct {
Title string
Link string
Description string
LastBuildDate *time.Time
Items []RSSItem
}
// RSSItem is a minimal view of one optional SPC RSS item.
type RSSItem struct {
Title string
Link string
Description string
PubDate string
GUID string
}
// ParseRSSFeed decodes supplemental SPC RSS metadata.
func ParseRSSFeed(raw string) (RSSFeed, error) {
var doc struct {
Channel struct {
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
LastBuildDate string `xml:"lastBuildDate"`
Items []struct {
Title string `xml:"title"`
Link string `xml:"link"`
Description string `xml:"description"`
PubDate string `xml:"pubDate"`
GUID string `xml:"guid"`
} `xml:"item"`
} `xml:"channel"`
}
if err := xml.Unmarshal([]byte(raw), &doc); err != nil {
return RSSFeed{}, fmt.Errorf("decode rss: %w", err)
}
feed := RSSFeed{
Title: strings.TrimSpace(doc.Channel.Title),
Link: strings.TrimSpace(doc.Channel.Link),
Description: strings.TrimSpace(doc.Channel.Description),
LastBuildDate: parseRSSDate(doc.Channel.LastBuildDate),
Items: make([]RSSItem, 0, len(doc.Channel.Items)),
}
for _, item := range doc.Channel.Items {
feed.Items = append(feed.Items, RSSItem{
Title: strings.TrimSpace(item.Title),
Link: strings.TrimSpace(item.Link),
Description: strings.TrimSpace(item.Description),
PubDate: strings.TrimSpace(item.PubDate),
GUID: strings.TrimSpace(item.GUID),
})
}
return feed, nil
}
func parseRSSDate(value string) *time.Time {
value = strings.TrimSpace(value)
if value == "" {
return nil
}
for _, layout := range []string{time.RFC1123Z, time.RFC1123} {
t, err := time.Parse(layout, value)
if err == nil {
tt := t.UTC()
return &tt
}
}
return nil
}