11package main
22
33import (
4+ "encoding/xml"
45 "flag"
56 "fmt"
67 "log"
@@ -25,6 +26,28 @@ type PlanContext struct {
2526 Template string // Custom template content
2627}
2728
29+ type Rss struct {
30+ XMLName xml.Name `xml:"rss"`
31+ Version string `xml:"version,attr"`
32+ Channel Channel `xml:"channel"`
33+ }
34+
35+ type Channel struct {
36+ Title string `xml:"title"`
37+ Link string `xml:"link"`
38+ Description string `xml:"description"`
39+ Items []Item `xml:"item"`
40+ }
41+
42+ type Item struct {
43+ Title string `xml:"title"`
44+ Link string `xml:"link"`
45+ Description string `xml:"description"`
46+ Content string `xml:"content:encoded"`
47+ PubDate string `xml:"pubDate"`
48+ Guid string `xml:"guid"`
49+ }
50+
2851func main () {
2952 // Define flags
3053 var inputPath string
@@ -185,22 +208,70 @@ func build(ctx *PlanContext) {
185208 log .Fatalf ("Failed to build current page: %v" , err )
186209 }
187210
188- if err := buildHistory (ctx ); err != nil {
211+ // Prepare RSS items
212+ var rssItems []Item
213+
214+ // Add current post
215+ r := render .New (& ctx .Config , ctx .Template )
216+ bodyBytes , err := r .RenderBody (content )
217+ if err == nil {
218+ link := ctx .Config .BaseURL + "/"
219+ item := Item {
220+ Title : ctx .Config .Title ,
221+ Link : link ,
222+ Description : string (bodyBytes ),
223+ Content : string (bodyBytes ),
224+ PubDate : info .ModTime ().Format (time .RFC1123Z ),
225+ Guid : link ,
226+ }
227+ rssItems = append (rssItems , item )
228+ } else {
229+ log .Printf ("Warning: Failed to render current content for RSS: %v" , err )
230+ }
231+
232+ historyItems , err := buildHistory (ctx )
233+ if err != nil {
189234 log .Printf ("Warning: Failed to build history (is this a git repo?): %v" , err )
190235 }
236+ rssItems = append (rssItems , historyItems ... )
237+
238+ // Generate RSS Feed
239+ rss := Rss {
240+ Version : "2.0" ,
241+ Channel : Channel {
242+ Title : ctx .Config .Title ,
243+ Link : ctx .Config .BaseURL ,
244+ Description : fmt .Sprintf ("Updates for %s" , ctx .Config .Title ),
245+ Items : rssItems ,
246+ },
247+ }
248+
249+ rssFile := filepath .Join (ctx .OutputDir , "rss.xml" )
250+ f , err := os .Create (rssFile )
251+ if err != nil {
252+ log .Fatalf ("Failed to create RSS file: %v" , err )
253+ }
254+ defer f .Close ()
255+
256+ f .WriteString (xml .Header )
257+ enc := xml .NewEncoder (f )
258+ enc .Indent ("" , " " )
259+ if err := enc .Encode (rss ); err != nil {
260+ log .Fatalf ("Failed to encode RSS: %v" , err )
261+ }
191262
192263 fmt .Println ("Build complete." )
193264}
194265
195266// buildHistory reconstructs the past versions of the plan file using git history.
196267// It iterates through unique dates in the git log, retrieves the file content for that date,
197268// and generates static pages for each day, as well as year and month index pages.
198- func buildHistory (ctx * PlanContext ) error {
269+ func buildHistory (ctx * PlanContext ) ([] Item , error ) {
199270 fmt .Println ("Building history..." )
200271
201272 history , err := getGitHistory (ctx .PlanDir , ctx .PlanFile )
202273 if err != nil {
203- return err
274+ return nil , err
204275 }
205276
206277 var dates []string
@@ -215,6 +286,10 @@ func buildHistory(ctx *PlanContext) error {
215286 Path string
216287 }
217288 tree := make (map [string ]map [string ][]dayEntry )
289+ var rssItems []Item
290+
291+ // Reuse renderer if config doesn't change per file
292+ r := render .New (& ctx .Config , ctx .Template )
218293
219294 for _ , dateStr := range dates {
220295 hash := history [dateStr ]
@@ -236,15 +311,34 @@ func buildHistory(ctx *PlanContext) error {
236311 outPath := filepath .Join (outDir , "index.html" )
237312
238313 if err := renderAndWrite (ctx , content , t , outPath ); err != nil {
239- return err
314+ return nil , err
315+ }
316+
317+ // Add to RSS
318+ relPath := fmt .Sprintf ("/%s/%s/%s" , year , month , day )
319+ link := ctx .Config .BaseURL + relPath
320+
321+ // We need the body content. renderAndWrite does it but doesn't return it.
322+ // We'll just re-render body here.
323+ bodyBytes , err := r .RenderBody (content )
324+ if err == nil {
325+ item := Item {
326+ Title : dateStr ,
327+ Link : link ,
328+ Description : string (bodyBytes ),
329+ Content : string (bodyBytes ),
330+ PubDate : t .Format (time .RFC1123Z ),
331+ Guid : link ,
332+ }
333+ rssItems = append (rssItems , item )
240334 }
241335
242336 if tree [year ] == nil {
243337 tree [year ] = make (map [string ][]dayEntry )
244338 }
245339 tree [year ][month ] = append (tree [year ][month ], dayEntry {
246340 DateStr : dateStr ,
247- Path : fmt . Sprintf ( "/%s/%s/%s" , year , month , day ) ,
341+ Path : relPath ,
248342 })
249343 }
250344
@@ -263,7 +357,7 @@ func buildHistory(ctx *PlanContext) error {
263357 yearContent += fmt .Sprintf ("- [%s](%s)\n " , link .DateStr , link .Path )
264358 }
265359 if err := renderAndWrite (ctx , []byte (yearContent ), time .Now (), filepath .Join (ctx .OutputDir , year , "index.html" )); err != nil {
266- return err
360+ return nil , err
267361 }
268362
269363 for month , days := range months {
@@ -279,11 +373,11 @@ func buildHistory(ctx *PlanContext) error {
279373 monthContent += fmt .Sprintf ("- [%s](%s)\n " , link .DateStr , link .Path )
280374 }
281375 if err := renderAndWrite (ctx , []byte (monthContent ), time .Now (), filepath .Join (ctx .OutputDir , year , month , "index.html" )); err != nil {
282- return err
376+ return nil , err
283377 }
284378 }
285379 }
286- return nil
380+ return rssItems , nil
287381}
288382
289383func renderAndWrite (ctx * PlanContext , content []byte , modTime time.Time , outPath string ) error {
@@ -431,7 +525,7 @@ func edit(ctx *PlanContext) {
431525 fullCmd := fmt .Sprintf ("%s %q" , editor , file )
432526 cmd = exec .Command ("sh" , "-c" , fullCmd )
433527 }
434-
528+
435529 cmd .Stdin = os .Stdin
436530 cmd .Stdout = os .Stdout
437531 cmd .Stderr = os .Stderr
0 commit comments