178 lines
5.1 KiB
Tcl
178 lines
5.1 KiB
Tcl
enum Writable(
|
|
Stdout,
|
|
File(path:Path),
|
|
)
|
|
func write(self:Writable, text:Text, newline:Bool=no)
|
|
when self is Stdout
|
|
say(text, newline)
|
|
return
|
|
is File(path)
|
|
_ := path.append(text)
|
|
if newline
|
|
_ := path.append("\n")
|
|
return
|
|
|
|
struct Option(option: Text, value: Text)
|
|
func parse(line: Text, expected: Text -> Option?)
|
|
val : Text = ""
|
|
if not line.starts_with(expected, &val)
|
|
return none
|
|
|
|
if val == ""
|
|
return none
|
|
|
|
foundequals : Bool = no
|
|
cursor := 1
|
|
while yes
|
|
if val[cursor] == " "
|
|
cursor += 1
|
|
continue
|
|
|
|
if not foundequals and val[cursor] == "="
|
|
foundequals = yes
|
|
cursor += 1
|
|
continue
|
|
|
|
if foundequals and cursor <= val.length
|
|
return Option(expected, val.slice(cursor))
|
|
|
|
return none
|
|
return none
|
|
|
|
fmjconfpath := ".fmjconf"
|
|
fmj_extension := "fmj"
|
|
|
|
description := "description"
|
|
title := "title"
|
|
link := "link"
|
|
default_author := "default_author"
|
|
author := "author"
|
|
category := "category"
|
|
pub_date := "pub_date"
|
|
|
|
default_author_value : Text? = none
|
|
|
|
func write_feed_header(out: Writable, config: Text?)
|
|
out.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?><rss version=\"2.0\"><channel>")
|
|
|
|
found_title : Bool = no
|
|
found_link : Bool = no
|
|
found_description : Bool = no
|
|
found_default_author : Bool = no
|
|
if config
|
|
for line in config.lines()
|
|
opt : Option? = none
|
|
opt = Option.parse(line, title)
|
|
if not found_title and opt
|
|
out.write("<title>$(opt!.value)</title>")
|
|
found_title = yes
|
|
continue
|
|
opt = Option.parse(line, link)
|
|
if not found_link and opt
|
|
out.write("<link>$(opt!.value)</link>")
|
|
found_link = yes
|
|
continue
|
|
opt = Option.parse(line, description)
|
|
if not found_description and opt
|
|
out.write("<description>$(opt!.value)</description>")
|
|
found_description = yes
|
|
continue
|
|
opt = Option.parse(line, default_author)
|
|
if not found_default_author and opt
|
|
default_author_value = opt!.value
|
|
found_default_author = yes
|
|
continue
|
|
|
|
if not found_title
|
|
out.write("<title>fmj generated feed</title>")
|
|
|
|
if not found_link
|
|
out.write("<link>http://example.com</link>")
|
|
|
|
if not found_description
|
|
out.write("<description>an rss feed generated by fmj</description>")
|
|
|
|
out.write("<generator>fmj</generator>")
|
|
|
|
func write_feed_footer(out : Writable)
|
|
out.write("</channel></rss>")
|
|
|
|
func write_feed_item(out : Writable, path : Path)
|
|
if not (path.extension() == fmj_extension)
|
|
return
|
|
lines := path.read()!.lines()
|
|
has_requirements : Bool = no
|
|
for line in lines
|
|
if Option.parse(line, title) or Option.parse(line, description)
|
|
has_requirements = yes
|
|
break
|
|
if not has_requirements
|
|
return
|
|
|
|
found_title : Bool = no
|
|
found_description : Bool = no
|
|
found_link : Bool = no
|
|
found_pub_date : Bool = no
|
|
found_author : Bool = no
|
|
|
|
out.write("<item>")
|
|
for line in lines
|
|
opt : Option? = none
|
|
opt = Option.parse(line, title)
|
|
if not found_title and opt
|
|
out.write("<title>$(opt!.value)</title>")
|
|
found_title = yes
|
|
continue
|
|
opt = Option.parse(line, link)
|
|
if not found_link and opt
|
|
out.write("<link>$(opt!.value)</link>")
|
|
found_link = yes
|
|
continue
|
|
opt = Option.parse(line, description)
|
|
if not found_description and opt
|
|
out.write("<description>$(opt!.value)</description>")
|
|
found_description = yes
|
|
continue
|
|
opt = Option.parse(line, author)
|
|
if not found_author and opt
|
|
out.write("<author>$(opt!.value)</description>")
|
|
found_author = yes
|
|
continue
|
|
opt = Option.parse(line, pub_date)
|
|
if not found_pub_date and opt
|
|
out.write("<pubDate>$(opt!.value)</pubDate>")
|
|
found_pub_date = yes
|
|
continue
|
|
opt = Option.parse(line, category)
|
|
if opt
|
|
out.write("<category>$(opt.value)</category>")
|
|
continue
|
|
if not found_author and default_author_value
|
|
out.write("<author>$(default_author_value)</author>")
|
|
out.write("</item>")
|
|
|
|
func main(dir: Path, output: Text = "rss.xml")
|
|
usingStdout : Bool = no
|
|
outfile : Path? = none
|
|
if output == "-"
|
|
usingStdout = yes
|
|
else
|
|
outfile = Path.from_text(output)
|
|
|
|
if not usingStdout and outfile!.exists()
|
|
_ := outfile!.remove()
|
|
|
|
out : Writable = if usingStdout
|
|
Writable.Stdout
|
|
else
|
|
Writable.File(outfile!)
|
|
|
|
config := dir.child(fmjconfpath).read()
|
|
|
|
out.write_feed_header(config)
|
|
|
|
for path in dir.children()
|
|
out.write_feed_item(path)
|
|
|
|
out.write_feed_footer()
|