Contents
- Install — Cargo, MSRV, source
- Quick Start — generate an RSS 2.0 feed in ten lines
- Library Usage
- Modules
- Features
- Development
- Security
- Documentation
- License
Install
As a Rust library
Or pin explicitly in Cargo.toml:
[]
= "0.0.6"
MSRV
| Surface | Minimum Supported Rust Version |
|---|---|
| Library | 1.88.0 (bumped from 1.79.0 in v0.0.6; time 0.3.51 + time-core 0.1.9 declare rust-version = "1.88", the icu_* chain via url/idna pulls 1.86, and the effective floor through current crates.io is 1.88) |
CI runs stable on every push, plus a dedicated MSRV lane that pins the 1.88.0 floor. Cross-platform matrix: Ubuntu + macOS + Windows.
Build from source
Quick Start
use ;
Library Usage
RSS generation
rss-gen covers every shipping RSS version. Pick the wire format via
RssVersion; the builder is the same for all of them.
use ;
RssVersion::{RSS0_90, RSS0_91, RSS0_92, RSS1_0, RSS2_0} are all
supported. RSS 1.0 emits the RDF wrapper (<rdf:RDF>); the others
emit a <rss> root.
Atom 1.0 generation
Atom is a sibling code path — independent types, the same ergonomics.
RFC 4287 required elements (id, title, updated) are checked at
serialise time; entries inherit feed-level authors when none are
declared per-entry, per §4.1.1.
use ;
Media enclosures (podcasts, attached video) follow Atom's typed-link model:
use ;
Parsing existing feeds
parse_rss reads any supported RSS version into the same RssData
struct used by the generator — round-tripping is symmetric.
use parse_rss;
Format auto-detection
When the caller does not know upfront whether the document is RSS or
Atom, detect_feed_format peeks the root element and returns a
classifier value. It does not parse the rest of the document.
use ;
let rss_xml = r#"<?xml version="1.0"?><rss version="2.0"><channel/></rss>"#;
let atom_xml = r#"<?xml version="1.0"?><feed xmlns="http://www.w3.org/2005/Atom"><id/></feed>"#;
assert_eq!;
assert_eq!;
The classifier returns FeedFormat::RssRdf for RSS 1.0 (<rdf:RDF>)
and FeedFormat::Unknown for documents that match neither root
element — including hand-rolled <feed> documents that omit the Atom
namespace declaration, so a misclassified payload does not silently
flow into the Atom code path.
Validation diagnostics
Every validation error is prefixed with the element that produced it, so downstream tools (static site generators, CI gates, IDE integrations) can point at the offending field rather than a bare "missing field" message.
| Context | Example error string |
|---|---|
| RSS channel | channel.title is missing, Invalid channel.link: …, Invalid channel.pub_date: … |
| RSS item | item.title is missing, item.link is missing, Invalid item.link: … |
| Atom feed | feed.id is missing, feed.updated is not a valid RFC 3339 timestamp: … |
| Atom entry | entry.0.author is missing (and feed has no feed-level author), entry.2.updated is missing |
use ;
Item-level link follows RSS 2.0 §5.7 — absolute URLs, root-relative
paths (/tags/), and bare paths (articles/foo.html) are all
accepted; whitespace, control characters, and empty strings are
rejected. Channel-level link retains absolute-URL strictness because
the spec requires it.
quick_rss helper
For one-shot generation of a minimal feed, quick_rss validates input
bounds (length caps, URL scheme) and returns the serialised XML
directly. Useful inside build.rs, snippet generators, and tests.
use quick_rss;
Constants and limits
rss-gen exposes its hard input bounds as pub const so callers can
validate ahead of time rather than discover the limit at serialise
time.
| Constant | Value | Applies to |
|---|---|---|
MAX_TITLE_LENGTH |
256 | RssData::title, RssItem::title |
MAX_LINK_LENGTH |
2 048 | RssData::link, RssItem::link |
MAX_DESCRIPTION_LENGTH |
100 000 | RssData::description, RssItem::description |
MAX_GENERAL_LENGTH |
1 024 | RssData::category and similar single-line fields |
MAX_FEED_SIZE |
1 048 576 (1 MiB) | Combined serialised feed size, enforced by RssData::validate_size |
The VERSION constant resolves to the crate's CARGO_PKG_VERSION at
compile time.
use ;
assert!;
assert_eq!;
Modules
| Module | Surface |
|---|---|
rss_gen::atom |
AtomFeed, AtomEntry, AtomPerson, AtomLink, AtomTextType, FeedFormat, generate_atom, detect_feed_format. |
rss_gen::data |
RssData, RssItem, RssVersion, plus the RssDataField / RssItemField enums used by set_field / set_item_field. |
rss_gen::error |
RssError (the crate-wide error enum) and the Result<T> = std::result::Result<T, RssError> alias. |
rss_gen::generator |
generate_rss, sanitize_content, write_element — the RSS serialisation pipeline. |
rss_gen::parser |
parse_rss, plus the ElementHandler trait for callers that need to plug in custom-element extraction. |
rss_gen::validator |
Standalone validation helpers used by the generator and exposed for callers that want to validate before constructing. |
rss_gen::macros |
Procedural shortcuts (macro_set_rss_data_fields!, macro_write_element!, …) for terse builder code. |
rss_gen::prelude |
Re-exports the surface most callers need — RssData, RssItem, RssVersion, AtomFeed, AtomEntry, generate_rss, generate_atom, parse_rss, quick_rss, detect_feed_format, and the error types. |
Features
| RSS generation | Author RSS 0.90, 0.91, 0.92, 1.0, and 2.0 feeds through a single RssData builder. RSS 2.0 emits the standard xmlns:atom declaration so feed readers can recognise the <atom:link rel="self"> element on the channel. |
| Atom 1.0 generation | AtomFeed / AtomEntry cover RFC 4287 required elements, multi-author and contributor lists, categories, xml:lang, icon/logo/rights/subtitle, plain-text and HTML payloads for <summary> / <content>, and <link rel="enclosure"> media attachments. |
| Parsing | parse_rss reads RSS 0.9x / 1.0 / 2.0 into RssData and is symmetric with the generator — parse_rss(generate_rss(x)?, None)? round-trips structurally for valid inputs. |
| Format detection | detect_feed_format classifies a document as Rss, RssRdf, Atom, or Unknown from the first start element, without parsing the body. |
| Validation | Required-element checks at both feed and item / entry level, with channel., item., feed., and entry.<idx>. context prefixes on every error message. RFC 3339 timestamp validation for Atom; relative item links for RSS 2.0 §5.7. |
| Sanitisation | sanitize_content strips invalid XML control characters and is idempotent — round-tripping through it does not double-encode entities. |
| Robust XML backend | quick-xml 0.40 with the serialize feature. Output is UTF-8, well-formed, and includes the <?xml version="1.0" encoding="utf-8"?> declaration. |
| Memory safety | #![forbid(unsafe_code)] at the crate root; no FFI, no raw-pointer dereferences. |
| Lint posture | #![deny(clippy::all, clippy::cargo, clippy::pedantic)] and #![deny(missing_docs)] — every public item is documented; every public surface passes pedantic Clippy. |
| Test posture | Unit tests, integration tests, doctest coverage, property tests (proptest, quickcheck), and structure-aware fuzzers (cargo fuzz) for parsing, generation, date parsing, content sanitisation, and URL validation. |
Development
CI runs through a reusable workflow that gates Clippy, formatting,
tests, cargo audit, cargo deny, dependency review, and CodeQL on
every push and pull request. See CONTRIBUTING.md
for signed-commit policy and PR guidelines.
Security
- No
unsafeblocks.#![forbid(unsafe_code)]is enforced at the crate root — no FFI to a C XML parser, no raw-pointer dereferences, nounsafeblocks in the generator, parser, validator, or sanitiser. - Input bounds.
MAX_TITLE_LENGTH,MAX_LINK_LENGTH,MAX_DESCRIPTION_LENGTH,MAX_GENERAL_LENGTH, andMAX_FEED_SIZEcap every text dimension;quick_rssandRssData::validate/RssData::validate_sizeenforce them. Set these before exposing feed generation to untrusted input. - Content sanitisation.
sanitize_contentremoves invalid XML control characters (everything below0x20except\n,\r,\t) and escapes&,<,>,", and'so user-supplied content cannot break feed well-formedness or smuggle injected markup. - URL hygiene. Channel-level
linkrequires an absolutehttp/httpsURL. Item-levellinkfollows RSS 2.0 §5.7 and permits relative paths, but rejects whitespace and control characters that would otherwise break feed-reader parsers. - Supply chain.
cargo auditandcargo denyrun on every push via the shared security workflow. Dependabot is wired for theminor-and-patchgroup onCargo.toml.CodeQLstatic analysis runs on every push and pull request.
Coordinated-disclosure contact and policy live in CONTRIBUTING.md.
Documentation
| Document | Covers |
|---|---|
CHANGELOG.md |
Per-release notes following Keep a Changelog 1.1.0. |
docs/MIGRATION-0.0.5-to-0.0.6.md |
Mechanical migration guide for the v0.0.6 API breaks (ValidationErrors payload, parse_date return type, removed deps). |
docs/adr/ |
Architecture Decision Records — structured ValidationError, time-only date stack, Atom as a sibling module. |
CONTRIBUTING.md |
Setup, signed-commit policy, PR guidelines. |
.github/SECURITY.md |
Coordinated-disclosure policy. |
AUTHORS.md |
Contributor roll. |
| docs.rs/rss-gen | Generated API reference for the published version (100 % coverage). |
doc.rssgen.co |
Live docs published from main via actions/deploy-pages. |
THE ARCHITECT — Sebastien Rousseau
THE ENGINE — EUXIS — Enterprise Unified Execution Intelligence System
License
Dual-licensed under Apache 2.0 or MIT, at your option.