type article =
{ date : int * int * int
; title : string
; authors : string list
; tags : string list
; category : string
; content : string
; url : string
}
let alt_ergo_footer =
{|
> ### **About Alt-Ergo**
>
> Alt-Ergo is an open-source automatic solver of mathematical formulas designed for program verification. Alt-Ergo is very successful for proving formulas generated in the context of deductive program verification.
> It was originally designed and tuned to be used by the [Why playform](http://why.lri.fr). Its development started in 2006 at the Laboratoire de Recherche en Informatique (LRI) of the Université Paris Sud and is maintained, developed and distributed since 2013 by the company OCamlPro.
>
> Alt-Ergo is part of the formal method team here at OCamlPro. This work is partially funded by the research projects Soprano, BWare, Vocal and LCHIP.
> If you like Alt-Ergo, consider joining the [Alt-Ergo user’s Club](https://alt-ergo.ocamlpro.com/#club)
|}
let ocaml_pro_footer =
{|
> **About OCamlPro:**
>
> OCamlPro is a R&D lab founded in 2011, with the mission to help industrial users benefit from state-of-the art programming languages like OCaml and Rust.
>
>We design, create and implement custom ad-hoc software for our clients. We also have a long experience in developing and maintaining open-source tooling for OCaml, such as Opam, [TryOCaml](http://try.ocamlpro.com), ocp-indent, ocp-index and ocp-browser, and we contribute to the core-development of OCaml, notably with our work on the Flambda optimizer branch.
>
> Another area of expertise is that of Formal Methods, with tools such as our SMT Solver Alt-Ergo (check our [Alt-Ergo Users'](https://alt-ergo.ocamlpro.com/#club)). We also provide vocational trainings in OCaml and Rust, and we can build courses on formal methods on-demand. Do not hesitate to reach out by email: [contact@ocamlpro.com](mailto:contact@ocamlpro.com).
|}
let compare_articles a1 a2 = compare (a2.date, a2.title) (a1.date, a1.title)
let error msg =
Format.eprintf "error: %s@." msg;
Format.pp_print_flush Format.err_formatter ();
exit 1
(* The code below is used to extract a small preview from the content field of an article type with the help of Omd *)
open Omd
let handle_html html =
let c = Markup.string html |> Markup.parse_html |> Markup.signals in
let buff = Buffer.create 512 in
let fmt = Format.formatter_of_buffer buff in
Markup.iter
(fun element ->
match element with
| `End_element -> ()
| `Start_element (_, _) -> ()
| `Text ls -> List.iter (fun s -> Format.fprintf fmt "%s " s) ls
| _ -> () )
c;
Format.pp_print_flush fmt ();
Buffer.contents buff
let rec handle_inline = function
| Concat (_attr, attr_inline_list) ->
List.fold_left (fun acc i -> acc ^ handle_inline i) "" attr_inline_list
| Text (_attr, s) -> s
| Emph (_attr, attr_inline) -> handle_inline attr_inline
| Strong (_attr, attr_inline) -> handle_inline attr_inline
| Code (_attr, s) -> s
| Hard_break _attr -> " "
| Soft_break _attr -> " "
| Link (_attr, attr_link) -> handle_inline attr_link.label
| Image (_attr, attr_link) -> handle_inline attr_link.label
| Html (_attr, str) -> handle_html str
let rec handle_block = function
| Paragraph (_attr, inline) -> handle_inline inline
| List (_attr, _list_type, _list_spacing, attr_block_list_list) ->
Format.asprintf "%a"
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "@.")
(fun fmt block -> Format.fprintf fmt "%s" (handle_block block)) )
(List.flatten attr_block_list_list)
| Blockquote (_attr, attr_block_list) ->
Format.asprintf "%a"
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt "@.")
(fun fmt block -> Format.fprintf fmt "%s" (handle_block block)) )
attr_block_list
| Thematic_break _attr -> ""
| Heading (_attr, _i, attr_inline) -> handle_inline attr_inline
| Code_block (_attr, _s1, _s2) -> ""
| Html_block (_attr, str) -> handle_html str
| Definition_list (_attr, _attr_def_elt_list) -> ""
let find_preview n doc =
let str =
String.concat " "
@@ List.filter
(fun x -> x <> "")
(List.map (fun block -> handle_block block) doc)
in
try String.sub str 0 n with
| Invalid_argument _s -> str
let preview article_content =
let content = Omd.of_string article_content |> find_preview 300 in
Format.sprintf "%s..." content
(* The code above is used to extract a small preview from the content field of an article type with the help of Omd *)
(** [normalize_url target] takes a target URL and rids it of unwanted
characters, such as utf8, and spaces *)
let normalize_url target =
String.map
(function
| ' ' -> '_'
| c -> c |> Char.lowercase_ascii )
(Ubase.from_utf8 target)
(** [allowed_categories] is a list of allowed categories for any article posted
on the blog *)
let allowed_categories =
[ "Tooling"
; "Blockchains"
; "OCamlPro"
; "Formal Methods"
; "Trainings"
; "OCaml"
; "Rust"
]
(** [raw_articles] List of all raw text in all articles in /content/blog/
subdirectory *)
let raw_articles =
List.find_all
(fun file ->
(String.length file >= 5 && String.equal (String.sub file 0 5) "blog/")
&& Filename.check_suffix file ".md" )
Content.file_list
(** [get_meta_value field] extract the second field of meta_data required at the
beginning of the article *)
let get_meta_value field = List.hd (List.rev (String.split_on_char '=' field))
(** [extract_date date] convert date meta_data into a [(int * int * int)] type *)
let extract_date date =
match String.split_on_char '-' date with
| [ year; month; day ] ->
(int_of_string year, int_of_string month, int_of_string day)
| _ -> (0, 0, 0)
(** [article_footer category tags] returns a footer according to a specific type
of article, . While quite generic at the moment, it will be possible to make
it return more specific footers based on a given article's Category and Tags *)
let article_footer category tags =
match category with
| "Formal Methods" ->
if List.exists (String.equal "alt-ergo") tags then
alt_ergo_footer
else
ocaml_pro_footer
| _ -> ocaml_pro_footer
(** [article_of_string post url] convert a given raw_text article into an
[article] type *)
let article_of_string post url =
match String.split_on_char '\n' post with
| title :: author :: date :: category :: tags :: r ->
let date = extract_date (get_meta_value date) in
let title = get_meta_value title in
let authors =
let auth = get_meta_value author in
match String.split_on_char ',' auth with
| [ "" ] -> [ "Unspecified authors!" ]
| auth -> auth
in
let tags =
let tags = get_meta_value tags in
match String.split_on_char ',' tags with
| [ "" ] -> [ "Unspecified tags!" ]
| tags -> tags
in
let category =
let c = get_meta_value category in
if not @@ List.mem c allowed_categories then (
let err_msg =
Format.asprintf {|Category (%s) invalid, try: %a@.|} c
(Format.pp_print_list
~pp_sep:(fun fmt () -> Format.fprintf fmt ", ")
Format.pp_print_string )
allowed_categories
in
Format.pp_print_flush Format.str_formatter ();
error err_msg
);
c
in
let content =
String.concat "\n" r ^ {|