nx_rss.nx source
↩ module page · 81 lines · 2942 B
1// rss.nx -- RSS 2.0 feed builder.
2//
3// Emits valid RSS 2.0 XML (specification: Harvard Berkman Center,
4// 2009). Every feed reader, podcast app, news aggregator, and
5// many static site generators consume RSS. nishi-pages without
6// RSS is half a blog engine.
7//
8// Feed skeleton:
9// <?xml version=\"1.0\" encoding=\"utf-8\"?>
10// <rss version=\"2.0\">
11// <channel>
12// <title>...</title>
13// <link>...</link>
14// <description>...</description>
15// <language>en</language>
16// <pubDate>Wed, 22 Apr 2026 12:34:56 GMT</pubDate>
17// <item>
18// <title>...</title>
19// <link>...</link>
20// <description>...</description>
21// <pubDate>...</pubDate>
22// <guid>...</guid>
23// </item>
24// ...
25// </channel>
26// </rss>
27//
28// All dates use IMF-fixdate (http_date.nx). Text content is
29// HTML-escaped (html_escape.nx) so the feed can contain '<',
30// '>', '&' safely.
31//
32// This is a stream-builder: caller walks their posts and calls
33// rss_item for each; we don't own the post list. All output
34// appends to a caller-owned buffer with bounds checking.
35//
36// Invariants:
37// R1 Order matters: rss_begin -> rss_item* -> rss_end.
38// R2 Caller supplies HTML-unsafe text; we escape internally.
39// R3 All dates are unix seconds converted to GMT fixdate.
40// R4 Description can contain HTML; we wrap it in CDATA rather
41// than escaping, matching WordPress / Ghost convention.
42// (strict RSS says escape, but CDATA is universally
43// accepted and preserves readability.)
44
45// nx_safety_envelope:
46// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
47// sil_target: SIL1
48// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
49// verdict: NOT_YET_EVALUATED
50
51// 2026-09-16: the builder lives in nx_rss_lib.nx (importable); this file keeps the compile-only smoke main.
52import "nx_rss_lib.nx"
53const RSS_MAGIC_1777000000: i64 = 1777000000
54
55func main() -> i64 {
56 let out: *u8 = sys_mmap(RSS_MAGIC_4096)
57 var off: i64 = 0
58 off = rss_begin(out, RSS_MAGIC_4096, off,
59 "Nishi Blog", 10,
60 "https://nishifamily.com/blog", 28,
61 "Greenfield sovereign computing notes", 36,
62 RSS_MAGIC_1777000000)
63 if off < 0 { return 1 }
64
65 off = rss_item(out, RSS_MAGIC_4096, off,
66 "First post", 10,
67 "https://nishifamily.com/blog/1", 30,
68 "<p>Hello & welcome</p>", 22,
69 "https://nishifamily.com/blog/1", 30,
70 RSS_MAGIC_1777000000)
71 if off < 0 { return 2 }
72
73 off = rss_end(out, RSS_MAGIC_4096, off)
74 if off < 0 { return 3 }
75
76 // Sanity: first 5 bytes are \"<?xml\".
77 if out[0] != 0x3C { return 4 } // '<'
78 if out[1] != 0x3F { return 5 } // '?'
79 if out[2] != 0x78 { return 6 } // 'x'
80 return 0
81}