yao/rss/build_atom.go

156 lines
3.6 KiB
Go

package rss
import (
"encoding/xml"
"strings"
"time"
)
// Atom namespace
const atomNS = "http://www.w3.org/2005/Atom"
// buildAtomXML generates an Atom 1.0 XML document from a Feed struct.
// Podcast/iTunes extensions are not included in Atom output (they are RSS-specific).
func buildAtomXML(feed *Feed) (string, error) {
doc := atomBuildFeed{
NS: atomNS,
}
doc.Title = feed.Title
if feed.Description != "" {
doc.Subtitle = feed.Description
}
if feed.Language != "" {
doc.Lang = feed.Language
}
// Links
if feed.Link != "" {
doc.Links = append(doc.Links, atomBuildLink{
Href: feed.Link,
Rel: "alternate",
Type: "text/html",
})
}
// Updated
if feed.Updated != "" {
doc.Updated = feed.Updated
} else {
doc.Updated = time.Now().UTC().Format(time.RFC3339)
}
// Entries
for _, item := range feed.Items {
entry := atomBuildEntry{
Title: item.Title,
ID: item.GUID,
}
if entry.ID == "" {
entry.ID = item.Link
}
if item.Link != "" {
entry.Links = append(entry.Links, atomBuildLink{
Href: item.Link,
Rel: "alternate",
Type: "text/html",
})
}
if item.Description != "" {
entry.Summary = &atomBuildText{
Type: "html",
Value: item.Description,
}
}
if item.Content != "" {
entry.Content = &atomBuildText{
Type: "html",
Value: item.Content,
}
}
// Authors
if item.Author != "" {
// Split on ", " to handle multiple authors
names := strings.Split(item.Author, ", ")
for _, name := range names {
name = strings.TrimSpace(name)
if name != "" {
entry.Authors = append(entry.Authors, atomBuildPerson{Name: name})
}
}
}
entry.Published = item.Published
entry.Updated = item.Updated
if entry.Updated == "" {
entry.Updated = item.Published
}
// Categories
for _, cat := range item.Categories {
entry.Categories = append(entry.Categories, atomBuildCategory{Term: cat, Label: cat})
}
doc.Entries = append(doc.Entries, entry)
}
output, err := xml.MarshalIndent(doc, "", " ")
if err != nil {
return "", err
}
return xml.Header + string(output), nil
}
// --- Build XML structs for Atom 1.0 output ---
type atomBuildFeed struct {
XMLName xml.Name `xml:"feed"`
NS string `xml:"xmlns,attr"`
Lang string `xml:"xml:lang,attr,omitempty"`
Title string `xml:"title"`
Subtitle string `xml:"subtitle,omitempty"`
Links []atomBuildLink `xml:"link"`
Updated string `xml:"updated"`
Entries []atomBuildEntry `xml:"entry"`
}
type atomBuildLink struct {
XMLName xml.Name `xml:"link"`
Href string `xml:"href,attr"`
Rel string `xml:"rel,attr,omitempty"`
Type string `xml:"type,attr,omitempty"`
}
type atomBuildEntry struct {
Title string `xml:"title"`
Links []atomBuildLink `xml:"link"`
ID string `xml:"id"`
Published string `xml:"published,omitempty"`
Updated string `xml:"updated,omitempty"`
Summary *atomBuildText `xml:"summary,omitempty"`
Content *atomBuildText `xml:"content,omitempty"`
Authors []atomBuildPerson `xml:"author,omitempty"`
Categories []atomBuildCategory `xml:"category,omitempty"`
}
type atomBuildText struct {
Type string `xml:"type,attr,omitempty"`
Value string `xml:",chardata"`
}
type atomBuildPerson struct {
XMLName xml.Name `xml:"author"`
Name string `xml:"name"`
}
type atomBuildCategory struct {
XMLName xml.Name `xml:"category"`
Term string `xml:"term,attr"`
Label string `xml:"label,attr,omitempty"`
}