rss

package module
v0.0.0-...-fbf95aa Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 5 Imported by: 0

README

go-ruby-rss/rss

rss — go-ruby-rss

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's standard-library RSS module (gem rss 0.3.2, shipped with MRI 4.0.5) — it parses and generates the three feed dialects Ruby's RSS supports, without any Ruby runtime:

  • RSS 2.0 (and the historical 0.9x family), rooted at <rss>;
  • RSS 1.0, the RDF dialect rooted at <rdf:RDF>;
  • Atom (RFC 4287), rooted at <feed>.

Parse auto-detects the dialect from the document's root element, exactly like RSS::Parser.parse, and the generated XML is byte-for-byte identical to MRI's to_s on the supported element set — including MRI's two-space indentation, its always-on namespace declarations on the root element, and its date formats (RFC822 for RSS <pubDate>/<lastBuildDate>, W3CDTF/RFC3339 for <dc:date> and every Atom date).

It is built on go-ruby-rexml for the XML layer (the same way MRI's RSS sits on REXML), and is the feed backend for go-embedded-ruby — but a standalone, reusable module, a sibling of go-ruby-regexp, go-ruby-erb and go-ruby-marshal.

What it is — and isn't. Parsing and generating the feed XML for the Ruby value model (the element tree, typed accessors, and date formats) is fully deterministic and needs no interpreter, so it lives here as pure Go. It hands back a small, explicit value model (*Rss, *RDF, *AtomFeed, …) the host maps to and from its own objects.

Features

  • Parse any of the three dialects with Parse(xml), which returns a Feed whose concrete type (*Rss / *RDF / *AtomFeed) is selected by the root element. Typed accessors mirror MRI's: feed.Channel.Title, item.Guid.IsPermaLink, entry.Updated, and so on. Dates parse to *time.Time preserving their original offset (RFC822 named zones such as GMT/EST included).
  • Generate spec-valid XML with (&Rss{…}).String(), (&RDF{…}).String(), (&AtomFeed{…}).String(), matching MRI's element order, indentation, namespace block and date formatting.
  • Bundled modules MRI emits by default: Dublin Core (dc:date, dc:creator, dc:subject), content (content:encoded) and syndication (sy:updatePeriod/updateFrequency/updateBase).
  • Date formats faithful to MRI's Time#rfc822 and Time#w3cdtf: RFC822 for RSS pub/build dates, W3CDTF (RFC3339, Z for UTC, trimmed fractional seconds) for Dublin Core and Atom dates.

CGO-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) on Linux, macOS and Windows.

Install

go get github.com/go-ruby-rss/rss

Usage

Parse (auto-detect dialect)
feed, err := rss.Parse(xml)
if err != nil { /* ... */ }

switch f := feed.(type) {
case *rss.Rss: // RSS 2.0 / 0.9x
    fmt.Println(f.Channel.Title, f.Channel.Items[0].PubDate)
case *rss.RDF: // RSS 1.0
    fmt.Println(f.Channel.About, f.Items[0].DCDate)
case *rss.AtomFeed: // Atom
    fmt.Println(f.Title, f.Entries[0].Updated)
}
Generate RSS 2.0
t := time.Unix(1700000000, 0)
r := &rss.Rss{Version: "2.0", Channel: &rss.Channel{
    Title: "Example Feed", Link: "http://example.com/",
    Description: "An example.", PubDate: &t,
    Items: []*rss.Item{{
        Title: "Item One", Link: "http://example.com/1",
        Guid: &rss.Guid{Content: "http://example.com/1", IsPermaLink: true, HasPermaLink: true},
    }},
}}
fmt.Print(r.String())

This produces the same bytes as RSS::Rss.new("2.0").to_s in MRI 4.0.5, namespace block and all.

Generate Atom
f := &rss.AtomFeed{
    ID: "urn:uuid:1", Title: "Atom Example", Updated: &t,
    Authors: []*rss.AtomPerson{{Name: "Jane"}},
    Links:   []*rss.AtomLink{{Href: "http://example.com/"}},
}
fmt.Print(f.String())

Coverage boundary

This library targets the common, default-emitted element set MRI's RSS exposes, which round-trips real feeds byte-for-byte. Specifically supported:

Dialect Supported elements
RSS 2.0 <channel>: title, link, description, language, copyright, managingEditor, webMaster, pubDate, lastBuildDate, generator, docs, ttl, category, image. <item>: title, link, description, author, comments, category, pubDate, guid (+ isPermaLink).
RSS 1.0 <channel> (rdf:about), title, link, description, image/textinput resources, <items><rdf:Seq>. <image>, <item>, <textinput> resources.
Atom <feed>/<entry>: id, title, subtitle, rights, generator, updated, published, author/contributor (name/uri/email), link (href/rel/type/title), category (term/scheme/label), content, summary.
Modules Dublin Core dc:date / dc:creator / dc:subject; content content:encoded; syndication sy:updatePeriod / sy:updateFrequency / sy:updateBase.

Outside the boundary (parsed leniently — unknown elements are ignored rather than rejected): MRI's strict schema validation (e.g. raising MissingTagError for an RSS 1.0 channel without <items>), the high-level RSS::Maker builder DSL, the iTunes/taxonomy/trackback/slash/image modules (their namespaces are still declared on the root to match MRI's output), and per-element xml:lang / xml:base attributes. The namespace declarations MRI always emits on the root element are reproduced verbatim so generation matches byte-for-byte.

How rexml is used

The parse path tokenizes the document with rexml.ParseDocument and walks the resulting element tree (ChildElements, Text, Attr, QName) to populate the typed model. The generate path mirrors MRI, which builds its XML as strings directly (it does not route to_s through REXML): a small internal node builder replicates RSS::Element#tag — two-space indent, attribute wrapping, CGI.escapeHTML text/attribute escaping, and the empty-element rules — so the bytes match exactly.

Tests & coverage

go test -cover ./...

The suite is deterministic and Ruby-free (golden vectors captured from MRI 4.0.5), keeping coverage at 100% on every platform — including the cross-arch qemu lanes and the Windows lane where no ruby is present. A differential MRI oracle (oracle_test.go) additionally runs the real ruby -rrss binary where available: it parses each sample feed through MRI and through this library and asserts the reserialized XML is byte-identical, and checks the date formatters against Time#rfc822 / Time#w3cdtf. The oracle self-skips when ruby is absent.

License

BSD-3-Clause — see LICENSE. Copyright (c) the go-ruby-rss/rss authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package rss is a pure-Go (CGO=0) reimplementation of Ruby's standard library RSS module (gem rss 0.3.2, shipped with MRI 4.0.5).

It parses and generates the three feed dialects MRI's RSS supports:

  • RSS 2.0 (and the historical 0.9x family), rooted at <rss>;
  • RSS 1.0, the RDF dialect rooted at <rdf:RDF>;
  • Atom (RFC 4287), rooted at <feed>.

Parser.Parse auto-detects the dialect from the document's root element, exactly like RSS::Parser.parse. The generated XML is byte-for-byte compatible with MRI's to_s for the round-trip (parse a feed, re-serialize it) on the supported element set, including MRI's 2-space indentation, its always-on namespace declarations on the root element, and its date formats (RFC822 for RSS <pubDate>/<lastBuildDate>, W3CDTF/RFC3339 for <dc:date> and every Atom date).

The XML layer (tokenizing on parse, attribute/text escaping) is shared with github.com/go-ruby-rexml/rexml, mirroring how MRI's RSS sits on REXML.

Bundled modules: Dublin Core (dc:), content (content:encoded) and syndication (sy:) — the common modules MRI bundles and emits by default. See the package documentation in the repository README for the exact element coverage boundary.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type AtomCategory

type AtomCategory struct {
	Term   string
	Scheme string
	Label  string
}

AtomCategory mirrors RSS::Atom::*::Category.

type AtomEntry

type AtomEntry struct {
	ID         string
	Title      string
	Summary    string
	Content    string
	Updated    *time.Time
	Published  *time.Time
	Rights     string
	Authors    []*AtomPerson
	Links      []*AtomLink
	Categories []*AtomCategory
}

AtomEntry mirrors RSS::Atom::Feed::Entry.

type AtomFeed

type AtomFeed struct {
	ID         string
	Title      string
	Subtitle   string
	Updated    *time.Time // W3CDTF
	Rights     string
	Generator  string
	Authors    []*AtomPerson
	Links      []*AtomLink
	Categories []*AtomCategory
	Entries    []*AtomEntry
	// contains filtered or unexported fields
}

AtomFeed is the <feed> document of Atom (RFC 4287). It mirrors RSS::Atom::Feed.

func (*AtomFeed) FeedType

func (f *AtomFeed) FeedType() string

func (*AtomFeed) String

func (f *AtomFeed) String() string

String serializes the Atom feed to MRI-compatible XML.

MRI declares the default Atom namespace plus the Dublin Core namespace on the root <feed> (xmlns, then xmlns:dc), and emits child elements in this fixed order: author, category, contributor, generator, icon, id, link, logo, rights, subtitle, title, updated, then the entries. Atom dates use W3CDTF (RFC3339).

type AtomLink struct {
	Href  string
	Rel   string
	Type  string
	Title string
}

AtomLink mirrors RSS::Atom::*::Link.

type AtomPerson

type AtomPerson struct {
	Name  string
	URI   string
	Email string
}

AtomPerson mirrors an Atom person construct (author/contributor).

type Channel

type Channel struct {
	Title             string
	Link              string
	Description       string
	Language          string
	Copyright         string
	ManagingEditor    string
	WebMaster         string
	PubDate           *time.Time // <pubDate>, RFC822
	LastBuildDate     *time.Time // <lastBuildDate>, RFC822
	Generator         string
	Docs              string
	TTL               string
	Image             *Image
	Categories        []string
	DCDate            *time.Time // dc:date, W3CDTF
	SyUpdatePeriod    string     // sy:updatePeriod
	SyUpdateFrequency string     // sy:updateFrequency
	SyUpdateBase      string     // sy:updateBase
	Items             []*Item
}

Channel mirrors RSS::Rss::Channel.

type Feed

type Feed interface {
	// FeedType is "rss", "rss" (for 1.0 too, via RDF) or "atom" — see the
	// concrete types for the exact value. Use the type switch for dispatch.
	FeedType() string
	// String serializes the feed to MRI-compatible XML.
	String() string
}

Feed is the common interface returned by Parser.Parse. The concrete type is *Rss (RSS 0.9x/2.0), *RDF (RSS 1.0) or *AtomFeed (Atom). FeedType reports the dialect, mirroring RSS feed#feed_type.

func Parse

func Parse(xml string) (Feed, error)

Parse parses an RSS/Atom document and auto-detects the dialect from the root element, mirroring RSS::Parser.parse:

  • <rss> → *Rss (RSS 0.9x / 2.0)
  • <rdf:RDF> → *RDF (RSS 1.0)
  • <feed> → *AtomFeed (Atom)

It returns an error for malformed XML or an unrecognized root element.

type Guid

type Guid struct {
	Content      string
	IsPermaLink  bool
	HasPermaLink bool
}

Guid mirrors RSS::Rss::Channel::Item::Guid. IsPermaLink is a tri-state in MRI (nil/true/false); HasPermaLink reports whether the attribute is set.

type Image

type Image struct {
	URL    string
	Title  string
	Link   string
	Width  string
	Height string
}

Image mirrors RSS::Rss::Channel::Image.

type Item

type Item struct {
	Title          string
	Link           string
	Description    string
	Author         string
	Comments       string
	PubDate        *time.Time // <pubDate>, RFC822
	Guid           *Guid
	Categories     []string
	DCDate         *time.Time // dc:date, W3CDTF
	DCCreator      string     // dc:creator
	DCSubject      string     // dc:subject
	ContentEncoded string     // content:encoded
}

Item mirrors RSS::Rss::Channel::Item.

type RDF

type RDF struct {
	Channel   *RDFChannel
	Image     *RDFImage
	Items     []*RDFItem
	Textinput *RDFTextinput
	// contains filtered or unexported fields
}

RDF is the <rdf:RDF> document of RSS 1.0. It mirrors RSS::RDF.

func (*RDF) FeedType

func (r *RDF) FeedType() string

func (*RDF) String

func (r *RDF) String() string

String serializes the RSS 1.0 RDF document to MRI-compatible XML.

MRI declares, on the root <rdf:RDF>, the default RSS 1.0 namespace and the rdf: namespace first, then the bundled-module namespaces (content, dc, image, slash, sy, taxo, trackback) in that fixed order, regardless of use. Children appear as <channel>, then <image>, then the <item> sequence, then <textinput>. The channel carries an <items><rdf:Seq><rdf:li resource=…/> table of item URIs.

type RDFChannel

type RDFChannel struct {
	About             string
	Title             string
	Link              string
	Description       string
	DCDate            *time.Time
	DCCreator         string
	SyUpdatePeriod    string
	SyUpdateFrequency string
	SyUpdateBase      string
	ImageResource     string // <image rdf:resource="..."/>, when an image is present
	// ItemResources is the <items><rdf:Seq><rdf:li resource="..."/> sequence.
	ItemResources     []string
	TextinputResource string
}

RDFChannel mirrors RSS::RDF::Channel. About is the rdf:about attribute.

type RDFImage

type RDFImage struct {
	About string
	Title string
	URL   string
	Link  string
}

RDFImage mirrors RSS::RDF::Image.

type RDFItem

type RDFItem struct {
	About          string
	Title          string
	Link           string
	Description    string
	DCDate         *time.Time
	DCCreator      string
	DCSubject      string
	ContentEncoded string
}

RDFItem mirrors RSS::RDF::Item.

type RDFTextinput

type RDFTextinput struct {
	About       string
	Title       string
	Description string
	Name        string
	Link        string
}

RDFTextinput mirrors RSS::RDF::Textinput.

type Rss

type Rss struct {
	Version string // e.g. "2.0"
	Channel *Channel
	// contains filtered or unexported fields
}

Rss is the <rss> document of RSS 0.91/0.92/2.0. It mirrors RSS::Rss.

func (*Rss) FeedType

func (r *Rss) FeedType() string

func (*Rss) String

func (r *Rss) String() string

String serializes the RSS 2.0/0.9x document to MRI-compatible XML.

MRI declares the bundled-module namespaces on the root <rss> element unconditionally (content, dc, itunes, trackback), in that fixed order, regardless of whether the modules are used; this method reproduces that.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL