Documentation
¶
Overview ¶
Package rssnip fetches and parses RSS, Atom, RDF, and JSON Feed documents and normalizes their entries to the JSON Feed 1.1 item shape.
Fetch retrieves a single feed URL over HTTP and returns its items. Parse does the same for a document the caller already holds, without any network access. Both accept the same Option values, and both return items in feed order with dates formatted as RFC3339. Run implements the rssnip command and is not part of the library API for programmatic use.
Fetch accepts a feed URL or an HTML page advertising one. Discovery inspects only the first retrieved document, uses the first advertised link, and can be turned off with WithDiscovery. Pagination follows Atom rel="next" links, JSON Feed next_url, and WordPress paged feeds, stops at WithMaxPages feed pages or at a repeated URL, and drops items whose ID was already seen. An initial HTML discovery document is not counted as a feed page. With WithSince, pagination may also stop early once a feed is observed to be ordered by date and to have run past the requested boundary.
WithSince and WithUntil select the half-open interval [since, until) using each item's published date, or its updated date under WithPreferUpdated. The other date is used when the preferred one is missing or unparseable; items with no usable date are dropped whenever either bound is set.
Each document is limited to MaxFeedSize bytes, and requests made by the default client are serialized per host, spaced one second apart, and time out after 30 seconds. That pacing state is created per Fetch call; pass WithHTTPClient to control it. Parse never closes the reader it is given and never fetches anything, so cancellation and timeouts belong to the caller.
Unparseable documents match ErrNotFeed, documents that advertise no feed link match ErrNoFeedFound, and non-2xx responses return a *StatusError, except that a 404 or 410 response to a guessed WordPress pagination page ends pagination and keeps the items collected so far. Errors carry no partial items and report URLs with any userinfo removed. Use github.com/Songmu/rssnip/feediscovery to list the feed links an HTML page advertises without fetching them.
Index ¶
- Constants
- Variables
- func Run(ctx context.Context, argv []string, outStream, errStream io.Writer) (runErr error)
- type Attachment
- type Author
- type FeedInfo
- type Item
- type Option
- func WithDiscovery(discovery bool) Option
- func WithHTTPClient(client *http.Client) Option
- func WithMaxPages(maxPages int) Option
- func WithPreferUpdated(preferUpdated bool) Option
- func WithSince(since time.Time) Option
- func WithUntil(until time.Time) Option
- func WithUserAgent(userAgent string) Option
- type StatusError
Examples ¶
Constants ¶
const MaxFeedSize = 32 << 20
MaxFeedSize is the maximum size in bytes of a single feed document.
Variables ¶
var ErrNoFeedFound = errors.New("rssnip: no feed found for document")
ErrNoFeedFound indicates that a document is not a feed and advertises no feed link that could be followed.
var ErrNotFeed = errors.New("rssnip: document is not a supported feed")
ErrNotFeed indicates that a document could not be parsed as a supported feed.
Functions ¶
Types ¶
type Attachment ¶
type Item ¶
type Item struct {
ID string `json:"id"`
URL string `json:"url,omitempty"`
ExternalURL string `json:"external_url,omitempty"`
Title string `json:"title,omitempty"`
ContentHTML string `json:"content_html,omitempty"`
ContentText string `json:"content_text,omitempty"`
Summary string `json:"summary,omitempty"`
Image string `json:"image,omitempty"`
BannerImage string `json:"banner_image,omitempty"`
DatePublished string `json:"date_published,omitempty"`
DateModified string `json:"date_modified,omitempty"`
Authors []Author `json:"authors,omitempty"`
Tags []string `json:"tags,omitempty"`
Attachments []Attachment `json:"attachments,omitempty"`
Feed FeedInfo `json:"_feed"`
}
Item is a feed entry normalized to the JSON Feed 1.1 item shape.
func Fetch ¶ added in v0.0.2
Fetch retrieves feedURL and returns its items in feed order, normalized to the JSON Feed 1.1 item shape.
feedURL must be an absolute HTTP or HTTPS URL without userinfo. It may point at a feed or, unless WithDiscovery(false) is set, at an HTML page advertising one; the first advertised link is then fetched instead. Fetch follows Atom rel="next" links, JSON Feed next_url, and WordPress paged feeds up to WithMaxPages feed pages, skipping items whose ID was already seen. The initial HTML discovery document, if any, is not counted. Each retrieved document is limited to MaxFeedSize bytes.
A non-2xx response returns a *StatusError, except that a 404 or 410 response to a guessed WordPress pagination page ends pagination and returns the items collected so far. A document that is neither a feed nor a page advertising one matches ErrNoFeedFound, and every unparseable document matches ErrNotFeed. Errors never carry partial items, and URLs in error messages have any userinfo removed.
Example ¶
package main
import (
"context"
"fmt"
"log"
"net/http"
"net/http/httptest"
"github.com/Songmu/rssnip"
)
func main() {
document := `<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"><channel>
<title>Example</title>
<item>
<title>Hello</title>
<link>https://example.com/posts/1</link>
<pubDate>Fri, 02 Jan 2026 03:04:05 GMT</pubDate>
</item>
</channel></rss>`
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/rss+xml")
fmt.Fprint(w, document)
}))
defer server.Close()
items, err := rssnip.Fetch(
context.Background(),
server.URL+"/feed.xml",
rssnip.WithHTTPClient(server.Client()),
rssnip.WithMaxPages(1),
)
if err != nil {
log.Fatal(err)
}
for _, item := range items {
fmt.Printf("%s %s %s\n", item.Title, item.URL, item.DatePublished)
}
}
Output: Hello https://example.com/posts/1 2026-01-02T03:04:05Z
func Parse ¶ added in v0.0.2
Parse reads a single feed document from r and returns its items, normalized to the JSON Feed 1.1 item shape.
Parse performs no network access and follows no pagination links, so WithHTTPClient, WithUserAgent, WithMaxPages, and WithDiscovery have no effect. sourceURL must be an absolute HTTP or HTTPS URL without userinfo. It resolves relative URLs in XML feeds and becomes each item's _feed.feed_url; JSON Feed URL fields are preserved as written. contentType is the document's HTTP Content-Type header and may be empty when unknown; RSS, Atom, RDF, and JSON Feed documents are recognized from their contents, and the character encoding of XML feeds comes from their byte order mark or XML declaration.
Parse reads at most MaxFeedSize+1 bytes from r and fails without partial items when the document is larger. The caller owns r, including closing it. An HTML page is not a feed and matches ErrNotFeed; use feediscovery.FindAll to obtain the feed links it advertises.
Example ¶
package main
import (
"fmt"
"log"
"strings"
"github.com/Songmu/rssnip"
)
func main() {
document := `{
"version": "https://jsonfeed.org/version/1.1",
"title": "Example",
"items": [
{"id": "1", "url": "https://example.com/posts/1", "title": "Hello", "date_published": "2026-01-02T03:04:05Z"}
]
}`
items, err := rssnip.Parse(
strings.NewReader(document),
"https://example.com/feed.json",
"application/feed+json",
)
if err != nil {
log.Fatal(err)
}
for _, item := range items {
fmt.Printf("%s %s %s\n", item.Feed.Title, item.Title, item.DatePublished)
}
}
Output: Example Hello 2026-01-02T03:04:05Z
type Option ¶ added in v0.0.2
type Option func(*options) error
Option configures Fetch and Parse. Invalid values are reported by Fetch and Parse rather than when the option is created.
func WithDiscovery ¶ added in v0.0.2
WithDiscovery enables or disables feed discovery for the first document that Fetch retrieves. It is enabled by default. Parse never discovers feeds.
func WithHTTPClient ¶ added in v0.0.2
WithHTTPClient sets the HTTP client used by Fetch.
By default Fetch creates a client whose transport serializes requests per host, waits one second between them, and applies a 30 second timeout to each request. That state is not shared between calls. A supplied client replaces this behavior, so polite pacing, timeouts, and redirect policy become the caller's responsibility.
func WithMaxPages ¶ added in v0.0.2
WithMaxPages limits how many pages Fetch retrieves from a single feed. It defaults to 10 and must be at least 1; 1 disables pagination. Parse ignores it because Parse never follows pagination links.
func WithPreferUpdated ¶ added in v0.0.2
WithPreferUpdated compares the updated date instead of the published date when filtering by WithSince or WithUntil. The other date is used when the preferred one is missing or unparseable.
func WithSince ¶ added in v0.0.2
WithSince keeps only items dated at or after since. Fetch also uses it to stop paginating once a feed is observed to run past the boundary.
func WithUntil ¶ added in v0.0.2
WithUntil keeps only items dated strictly before until. Items are selected from the half-open interval [since, until).
func WithUserAgent ¶ added in v0.0.2
WithUserAgent sets the User-Agent header sent by Fetch. It defaults to "rssnip/<version>" and must not be empty.
type StatusError ¶ added in v0.0.2
type StatusError struct {
// URL is the requested feed URL with any userinfo removed.
URL string
// Status is the HTTP status line, such as "404 Not Found".
Status string
// StatusCode is the HTTP status code, such as 404.
StatusCode int
}
StatusError reports a feed response with a non-2xx HTTP status.
func (*StatusError) Error ¶ added in v0.0.2
func (err *StatusError) Error() string
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
rssnip
command
|
|
|
Package feediscovery discovers advertised feed links in HTML and XHTML documents without fetching their URLs or parsing feed contents.
|
Package feediscovery discovers advertised feed links in HTML and XHTML documents without fetching their URLs or parsing feed contents. |