diff --git a/Makefile b/Makefile index f39ff60..5ab23b4 100644 --- a/Makefile +++ b/Makefile @@ -3,19 +3,21 @@ cache := .cache www_root := _site www := $(www_root)/posts post_index := $(www_root)/posts.html +feed := $(www_root)/feed.xml post_compiler := src/webcc.ml builddir := _build/default POSTCC := $(builddir)/$(patsubst %.ml,%.exe,$(post_compiler)) INDEXCC := $(POSTCC) -i +FEEDCC := $(POSTCC) -a post-sources := $(shell find $(src)/ -type f) post-htmls := $(patsubst $(src)/%.md,$(www)/%.html,$(post-sources)) .PHONY: all serve clean $(POSTCC) -all: $(POSTCC) $(post-htmls) $(post_index) $(www_root)/static +all: $(POSTCC) $(post-htmls) $(post_index) $(feed) $(www_root)/static cp -R ~/www/* $(www_root) $(www): @@ -27,6 +29,9 @@ $(www)/%.html: $(src)/%.md $(www) $(post_index): $(post-sources) $(INDEXCC) $^ -o $@ +$(feed): $(post-sources) + $(FEEDCC) $^ -o $@ + $(www_root)/static: static cp -R static $(www_root) diff --git a/src/atom.ml b/src/atom.ml new file mode 100644 index 0000000..f3f6aba --- /dev/null +++ b/src/atom.ml @@ -0,0 +1,44 @@ +open Post + +let format_entry ~title ~date ~url ~contents ~author = + String.concat "" + [ +{| + |}; title; {| + |}; + url; + {| + + |}; author; {| + + |}; ISO8601.Permissive.string_of_datetime date;{| + +
|}; + contents; +{|
+
+|}] + +let format_feed ~title ~subtitle ~author ~feed_url ~site_url ~last_updated ~(entries: Post.t list) = + let opener = + String.concat "" [ +{| + + |}; title; {| + |}; feed_url; {| + |}; subtitle; {| + |}; ISO8601.Permissive.string_of_datetime last_updated; {| + + +|}] in + opener ^ + (String.concat "\n" @@ List.map + (fun (md,contents) -> + format_entry + ~title:md.title + ~date:md.date + ~url:(site_url ^ md.filename) + ~author + ~contents) + entries) ^ + "" diff --git a/src/dune b/src/dune index 7183ddf..48df5e0 100644 --- a/src/dune +++ b/src/dune @@ -1,3 +1,4 @@ (executable (name webcc) - (libraries omd ISO8601 str)) + (libraries omd ISO8601 str) + (modules_without_implementation post)) diff --git a/src/post.mli b/src/post.mli new file mode 100644 index 0000000..6417dd3 --- /dev/null +++ b/src/post.mli @@ -0,0 +1,5 @@ +type date = float (* repr for the ISO8601 library *) +type metadata = { title : string ; date : date ; tags : string list ; + filename : string ; + other : (string * string) list } +type t = metadata * string diff --git a/src/webcc.ml b/src/webcc.ml index f148575..8156847 100644 --- a/src/webcc.ml +++ b/src/webcc.ml @@ -1,21 +1,27 @@ +open Post + +let author = "Daniel" +let site_url = "https://groupoid.moe" +let feed_url = "https://groupoid.moe/feed.xml" +let site_title = "groupoid.moe" +let feed_title = "Recent posts" let input = ref [] let output = ref "" let gen_index = ref false +let gen_atom = ref false let spec = [ ("-o", Arg.Set_string output, " file.html Specify the output file (default is stdout)."); ("-i", Arg.Set gen_index, - " generate a post index instead of compiling individual posts."); + " generate the post index instead of compiling individual posts."); + ("-a", Arg.Set gen_atom, + " generate the atom feed."); ("--", Rest(fun s -> input := s :: !input), " Consider all remaining arguments as input file names.") ] -type post_metadata = { title : string ; date : float ; tags : string list ; - filename : string ; - other : (string * string) list } - (** Utitilies *) let with_open_in fn f = let ic = open_in fn in @@ -29,7 +35,7 @@ let with_open_out fn f = | r -> close_out oc; r | exception e -> close_out_noerr oc; raise e -let read_metadata filename ic : post_metadata = +let read_metadata filename ic : Post.metadata = let rec go lst = let line = try input_line ic @@ -46,12 +52,23 @@ let read_metadata filename ic : post_metadata = | None -> "-" in let date = ISO8601.Permissive.date (lookup "date") in + (* TODO! Fix the paths here!!!!!! *) + let target_fname = + let open Str in + if string_match (regexp {|content/\(.*\)\.md|}) filename 0 + then "/posts/" ^ matched_group 1 filename ^ ".html" + else filename + in { title = lookup "title"; date = date; tags = String.split_on_char ',' (lookup "tags"); - filename = filename; + filename = target_fname; other = md } - + +let read_post filename ic : Post.t = + let metadata = read_metadata filename ic in + let md = Omd.of_channel ic in + (metadata, Omd.to_html md) (** Functions for processing an individual post *) @@ -77,7 +94,7 @@ let format_page ~title ~contents = atom feed
-

|}; title; {|

|}; +

|}; title; {|

|}; contents; {|

@@ -97,41 +114,32 @@ let format_post ~title ~tags ~date ~contents = (* convert contents from [ic] to a post webpage, output to [oc] *) let process filename ic oc = - let metadata = read_metadata filename ic in - let md = Omd.of_channel ic in + let (metadata, contents) = read_post filename ic in let html = format_post ~title:(metadata.title) ~tags:(String.concat ", " metadata.tags) ~date:(ISO8601.Permissive.string_of_date metadata.date) - ~contents:(Omd.to_html md) + ~contents in output_string oc html +(** Compile the posts to HTML *) let compile_posts oc = if !input = [] then process "" stdin oc else begin let f filename = with_open_in filename @@ fun ic -> process filename ic oc in List.iter f !input end - -(** Generating index of posts *) + +(** Generate the index of posts *) let generate_index oc = let f filename = with_open_in filename (read_metadata filename) in let mds = List.map f !input in let mds = List.sort (fun md1 md2 -> compare md2.date md1.date) mds in - (* TODO! Fix the paths here!!!!!! *) let format_item md = - (* path to the html file *) - let fname = md.filename in - let target_fname = - let open Str in - if string_match (regexp {|content/\(.*\)\.md|}) fname 0 - then matched_group 1 fname ^ ".html" - else fname - in String.concat "" [ - {|
  • |}; + {|
  • |}; md.title; {| - |}; ISO8601.Permissive.string_of_date md.date ; {|
  • |}] in @@ -139,6 +147,28 @@ let generate_index oc = let html = format_page ~title:"Recent posts" ~contents in output_string oc html +(** Generate the atom feed *) +let generate_atom oc = + let f filename = with_open_in filename (read_post filename) in + let posts = List.map f !input in + let posts = List.sort (fun (md1,_) (md2,_) -> compare md2.date md1.date) posts in + let last_updated = + match posts with + | [] -> 0. + | (md,_)::_ -> md.date + in + let xml = Atom.format_feed + ~title:site_title + ~subtitle:feed_title + ~author + ~feed_url + ~site_url + ~last_updated + ~entries:posts + in + output_string oc xml + + (** main *) let main () = Arg.parse (Arg.align spec) @@ -150,10 +180,14 @@ let main () = else with_open_out !output f in - with_output @@ + begin + with_output @@ if !gen_index then generate_index else compile_posts + end; + if !gen_atom then with_output generate_atom + let () = try