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("")
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("$(opt!.value)")
found_title = yes
continue
opt = Option.parse(line, link)
if not found_link and opt
out.write("$(opt!.value)")
found_link = yes
continue
opt = Option.parse(line, description)
if not found_description and opt
out.write("$(opt!.value)")
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("fmj generated feed")
if not found_link
out.write("http://example.com")
if not found_description
out.write("an rss feed generated by fmj")
out.write("fmj")
func write_feed_footer(out : Writable)
out.write("")
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("")
for line in lines
opt : Option? = none
opt = Option.parse(line, title)
if not found_title and opt
out.write("$(opt!.value)")
found_title = yes
continue
opt = Option.parse(line, link)
if not found_link and opt
out.write("$(opt!.value)")
found_link = yes
continue
opt = Option.parse(line, description)
if not found_description and opt
out.write("$(opt!.value)")
found_description = yes
continue
opt = Option.parse(line, author)
if not found_author and opt
out.write("$(opt!.value)")
found_author = yes
continue
opt = Option.parse(line, pub_date)
if not found_pub_date and opt
out.write("$(opt!.value)")
found_pub_date = yes
continue
opt = Option.parse(line, category)
if opt
out.write("$(opt.value)")
continue
if not found_author and default_author_value
out.write("$(default_author_value)")
out.write("")
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()