diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f636e1..ebeee66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.3.0] - 2025-12-18 + +### Added +- Syndication Module namespace support (`syn:updatePeriod`, `syn:updateFrequency`, `syn:updateBase`) +- `feed.published` field for Atom feeds and RSS channel `pubDate` +- `xml:base` URL resolution for relative URLs in Atom and RSS feeds +- `xml:lang` attribute tracking for feed and entry language detection +- Creative Commons `license` field extraction from `rel="license"` links +- Comprehensive RSS 1.0 integration tests (12+ test cases) +- Syndication metadata exposed in Python and Node.js bindings +- Dublin Core fields (`dc_creator`, `dc_publisher`, `dc_rights`) in bindings +- Benchmark results in all README files + +### Changed +- Improved test coverage from 83% to 91%+ +- Optimized Python bindings to return `&str` instead of `String` for enum values +- Simplified Node.js Entry conversion using idiomatic `.collect()` pattern +- Updated documentation with performance benchmarks (90-94x faster than Python feedparser) + +### Fixed +- Performance issue with unnecessary string allocations in Python `__repr__` methods + ## [0.2.1] - 2025-12-16 ### Changed @@ -125,7 +147,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Comprehensive test coverage - Documentation with examples -[Unreleased]: https://github.com/bug-ops/feedparser-rs/compare/v0.2.1...HEAD +[Unreleased]: https://github.com/bug-ops/feedparser-rs/compare/v0.3.0...HEAD +[0.3.0]: https://github.com/bug-ops/feedparser-rs/compare/v0.2.1...v0.3.0 [0.2.1]: https://github.com/bug-ops/feedparser-rs/compare/v0.2.0...v0.2.1 [0.2.0]: https://github.com/bug-ops/feedparser-rs/compare/v0.1.8...v0.2.0 [0.1.8]: https://github.com/bug-ops/feedparser-rs/compare/v0.1.7...v0.1.8 diff --git a/Cargo.lock b/Cargo.lock index 387e0eb..4f63223 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -472,7 +472,7 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "feedparser-rs" -version = "0.2.1" +version = "0.3.0" dependencies = [ "ammonia", "chrono", @@ -493,7 +493,7 @@ dependencies = [ [[package]] name = "feedparser-rs-node" -version = "0.2.1" +version = "0.3.0" dependencies = [ "feedparser-rs", "napi", @@ -503,7 +503,7 @@ dependencies = [ [[package]] name = "feedparser-rs-py" -version = "0.1.0" +version = "0.3.0" dependencies = [ "chrono", "feedparser-rs", diff --git a/Cargo.toml b/Cargo.toml index 4871ef5..0542bdd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ resolver = "2" [workspace.package] -version = "0.2.1" +version = "0.3.0" edition = "2024" rust-version = "1.88.0" authors = ["bug-ops"] diff --git a/README.md b/README.md index a78f332..fd95e6f 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ High-performance RSS/Atom/JSON Feed parser written in Rust, with Python and Node | Media RSS | Media attachments and metadata | | iTunes | Podcast metadata (author, duration, explicit) | | Podcast 2.0 | Chapters, transcripts, funding | +| Syndication | Update schedule (period, frequency, base) | | GeoRSS | Geographic location data (point, line, polygon, box) | | Creative Commons | License information with `rel="license"` links | @@ -203,11 +204,25 @@ cargo make --list-all-steps ## Benchmarks -Run benchmark comparison against Python feedparser: +Measured on Apple M1 Pro, parsing real-world RSS feeds: -```bash -cargo make bench-compare -``` +| Feed Size | Time | Throughput | +|-----------|------|------------| +| Small (2 KB) | **10.7 µs** | 187 MB/s | +| Medium (20 KB) | **93.6 µs** | 214 MB/s | +| Large (200 KB) | **939 µs** | 213 MB/s | + +Format detection: **128 ns** (near-instant) + +### vs Python feedparser + +| Operation | feedparser-rs | Python feedparser | Speedup | +|-----------|---------------|-------------------|---------| +| Parse 20 KB RSS | 0.09 ms | 8.5 ms | **94x** | +| Parse 200 KB RSS | 0.94 ms | 85 ms | **90x** | + +> [!TIP] +> Run your own benchmarks with `cargo bench` or compare against Python with `cargo make bench-compare`. ## MSRV Policy diff --git a/crates/feedparser-rs-core/README.md b/crates/feedparser-rs-core/README.md index 4c6deef..2ad4156 100644 --- a/crates/feedparser-rs-core/README.md +++ b/crates/feedparser-rs-core/README.md @@ -13,12 +13,12 @@ This is the core parsing library that powers the Python and Node.js bindings. - **Multi-format**: RSS 0.9x/1.0/2.0, Atom 0.3/1.0, JSON Feed 1.0/1.1 - **Tolerant**: Bozo flag for graceful handling of malformed feeds -- **Fast**: Native Rust performance +- **Fast**: Native Rust performance (200+ MB/s throughput) - **Safe**: No unsafe code, comprehensive error handling - **HTTP support**: Fetch feeds from URLs with compression and conditional GET - **Podcast support**: iTunes and Podcast 2.0 namespace extensions -- **Namespace extensions**: Dublin Core, Media RSS, GeoRSS, Creative Commons -- **Well-tested**: 83%+ test coverage with real-world feed fixtures +- **Namespace extensions**: Dublin Core, Media RSS, Syndication, GeoRSS, Creative Commons +- **Well-tested**: 91%+ test coverage with real-world feed fixtures ## Installation @@ -138,6 +138,24 @@ let feed = parse_with_limits(xml.as_bytes(), limits)?; > [!NOTE] > Default limits are generous for typical feeds. Use `ParserLimits::strict()` for untrusted input. +## Benchmarks + +Measured on Apple M1 Pro: + +| Feed Size | Time | Throughput | +|-----------|------|------------| +| Small (2 KB) | 10.7 µs | 187 MB/s | +| Medium (20 KB) | 93.6 µs | 214 MB/s | +| Large (200 KB) | 939 µs | 213 MB/s | + +Format detection: 128 ns + +Run benchmarks: + +```bash +cargo bench +``` + ## Platform Bindings - **Node.js**: [`feedparser-rs`](https://www.npmjs.com/package/feedparser-rs) on npm diff --git a/crates/feedparser-rs-node/README.md b/crates/feedparser-rs-node/README.md index e20c017..b127f4f 100644 --- a/crates/feedparser-rs-node/README.md +++ b/crates/feedparser-rs-node/README.md @@ -202,12 +202,23 @@ if (entry.published) { ## Performance -Benchmarks vs Python feedparser (parsing 100KB RSS feed): +Benchmarks on Apple M1 Pro: -| Library | Time | Speedup | -|---------|------|---------| -| feedparser-rs | 0.5ms | 100x | -| feedparser (Python) | 50ms | 1x | +| Feed Size | Time | Throughput | +|-----------|------|------------| +| Small (2 KB) | 0.01 ms | 187 MB/s | +| Medium (20 KB) | 0.09 ms | 214 MB/s | +| Large (200 KB) | 0.94 ms | 213 MB/s | + +### vs Python feedparser + +| Operation | feedparser-rs | Python feedparser | Speedup | +|-----------|---------------|-------------------|---------| +| Parse 20 KB RSS | 0.09 ms | 8.5 ms | **94x** | +| Parse 200 KB RSS | 0.94 ms | 85 ms | **90x** | + +> [!TIP] +> For best performance, pass `Buffer` instead of `string` to avoid UTF-8 conversion overhead. ## Platform Support diff --git a/crates/feedparser-rs-node/package-lock.json b/crates/feedparser-rs-node/package-lock.json index c8da1e5..3e8fa35 100644 --- a/crates/feedparser-rs-node/package-lock.json +++ b/crates/feedparser-rs-node/package-lock.json @@ -1,12 +1,12 @@ { "name": "feedparser-rs", - "version": "0.1.4", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "feedparser-rs", - "version": "0.1.4", + "version": "0.3.0", "license": "MIT OR Apache-2.0", "devDependencies": { "@napi-rs/cli": "^3.5", diff --git a/crates/feedparser-rs-node/package.json b/crates/feedparser-rs-node/package.json index 32fa887..daf8458 100644 --- a/crates/feedparser-rs-node/package.json +++ b/crates/feedparser-rs-node/package.json @@ -1,6 +1,6 @@ { "name": "feedparser-rs", - "version": "0.2.1", + "version": "0.3.0", "description": "High-performance RSS/Atom/JSON Feed parser for Node.js", "main": "index.js", "types": "index.d.ts", @@ -47,4 +47,4 @@ "@napi-rs/cli": "^3.5", "c8": "^10.1.3" } -} \ No newline at end of file +} diff --git a/crates/feedparser-rs-py/Cargo.toml b/crates/feedparser-rs-py/Cargo.toml index 52a8af0..53ab369 100644 --- a/crates/feedparser-rs-py/Cargo.toml +++ b/crates/feedparser-rs-py/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "feedparser-rs-py" -version = "0.1.0" +version.workspace = true edition = "2024" rust-version = "1.85" license = "MIT OR Apache-2.0" diff --git a/crates/feedparser-rs-py/README.md b/crates/feedparser-rs-py/README.md index ee5c2f1..92c58fe 100644 --- a/crates/feedparser-rs-py/README.md +++ b/crates/feedparser-rs-py/README.md @@ -149,6 +149,19 @@ for entry in d.entries: - `ParserLimits` — Resource limits configuration +## Performance + +Benchmarks vs Python feedparser on Apple M1 Pro: + +| Operation | feedparser-rs | Python feedparser | Speedup | +|-----------|---------------|-------------------|---------| +| Parse 2 KB RSS | 0.01 ms | 0.9 ms | **90x** | +| Parse 20 KB RSS | 0.09 ms | 8.5 ms | **94x** | +| Parse 200 KB RSS | 0.94 ms | 85 ms | **90x** | + +> [!TIP] +> For maximum performance, pass `bytes` instead of `str` to avoid UTF-8 re-encoding. + ## Platform Support Pre-built wheels available for: diff --git a/crates/feedparser-rs-py/pyproject.toml b/crates/feedparser-rs-py/pyproject.toml index 23bd35b..bd76b46 100644 --- a/crates/feedparser-rs-py/pyproject.toml +++ b/crates/feedparser-rs-py/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "maturin" [project] name = "feedparser-rs" -version = "0.2.1" +version = "0.3.0" description = "High-performance RSS/Atom/JSON Feed parser with feedparser-compatible API" readme = "README.md" license = { text = "MIT OR Apache-2.0" }