RSS 2.0, Atom 1.0 and JSON Feed 1.1 parsing and normalization for MoonBit.
| 格式 / 能力 | 当前状态 |
|---|---|
| JSON Feed 1.1 | supported / initial stable |
| RSS 2.0 | supported / initial stable |
| RSS Podcast extensions | Apple Podcasts/iTunes core metadata supported |
| Atom 1.0 | Feed/Entry 文档入口与统一模型入口;完整兼容状态见下方矩阵 |
| Auto detection | supported |
| Date normalization | supported;保留原始字符串并提供 UTC Unix 秒 |
| CLI | supported |
| Mooncakes 发布 | joanna/moonfeed@0.1.2(当前 registry 最新版;0.2.0 待本轮闭环) |
moon add joanna/moonfeed@0.1.2git clone https://github.com/jojochensmart-lab/moonfeed.git
cd moonfeed
moon --version
moonc -v
moon check
moon test
moon build
moon run examples/basicmoon run cli --target js -- detect fixtures/rss/full.xml
moon run cli --target js -- inspect fixtures/atom/full.xml
moon run cli --target js -- normalize fixtures/jsonfeed/full.jsonFormat: RSS 2.0
Title: Example Feed
Items: 2import { "joanna/moonfeed" }let feed = @moonfeed.parse(input)test {
let feed = @moonfeed.parse_json_feed(
#|{"version":"https://jsonfeed.org/version/1.1","title":"News","items":[]}
)
assert_eq(feed.title, "News")
}test {
let feed = @moonfeed.parse_rss(
#|<rss version="2.0"><channel><title>News</title><link>https://example.org/</link><description>Updates</description></channel></rss>
)
assert_eq(feed.title, "News")
assert_eq(feed.home_page_url, Some("https://example.org/"))
}let podcast = @moonfeed.parse_podcast_rss(input)
let show_author = podcast.show.author
let categories = podcast.show.categories
let first_episode = podcast.episodes[0]
let duration = first_episode.duration_in_secondstest {
let document = @moonfeed.parse_atom_document(
#|<entry xmlns="http://www.w3.org/2005/Atom" xml:base="https://example.org/news/"><id>urn:entry:demo</id><title>MoonFeed</title><updated>2026-10-20T12:00:00Z</updated><author><name>MoonBit</name></author><link href="../article"/><content>Hello</content></entry>
)
match document {
@moonfeed.AtomDocument::EntryDocument(entry) => {
assert_eq(entry.metadata.id, Some("urn:entry:demo"))
assert_eq(entry.metadata.links[0].href.raw, "../article")
assert_eq(entry.metadata.links[0].href.resolved, "https://example.org/article")
assert_eq(entry.content.unwrap().value, "Hello")
}
_ => fail("expected Entry Document")
}
}test {
let feed = @moonfeed.parse_atom(
#|<feed xmlns="http://www.w3.org/2005/Atom"><id>urn:news</id><title>News</title><updated>2026-10-01T12:00:00Z</updated><entry><id>post-1</id><title>First post</title><updated>2026-10-01T12:00:00Z</updated><summary>Hello</summary></entry></feed>
)
assert_eq(feed.items[0].id, "post-1")
}let date = @moonfeed.parse_rss_datetime("Wed, 02 Oct 2002 13:00:00 GMT")
assert_eq(date.raw, "Wed, 02 Oct 2002 13:00:00 GMT")
assert_eq(date.unix_seconds, 1033563600L)moon info
moon fmt
moon check
moon test
moon buildpub(all) suberror ParseError {
Detect(DetectError)
JsonFeed(ParseError)
Rss(ParseError)
Atom(ParseError)
} derive(Eq, Debug)fn parse_atom_document_with_base(input : String, document_uri : String) -> AtomDocument raise ParseErrorInstall
Download zipRSS 2.0, Atom 1.0 and JSON Feed 1.1 parsing and normalization for MoonBit.