let main () =
  let open Meta_result in
  succeedf "mkdir -p %s" configuration#output_directory;
  begin match configuration#api_doc_directory with
  | Some s -> succeedf "rsync -a %s/ %s/api" s configuration#output_directory
  | None -> say "Warning, no API docs"
  end;
  let menu_md =
    sprintf "- [Home](index.html)\n"
    :: (List.map configuration#input_files ~f:(fun path ->
        match File_kind.identify_file path with
        | `Markdown m
        | `Ocaml_implementation m ->
          let base = Filename.basename m in
          let title = configuration#title ~with_prefix:false base in
          sprintf "- [%s](%s.html)\n" title base
        | other -> ""))
    @ [
      (match configuration#api_doc_directory with
       | Some _ -> sprintf "- [API Documentation](./api/index.html)\n"
       | None -> "");
      configuration#add_to_menu; ]
    |> String.concat ~sep:""
  in
  let first_pass_result : (unit, _) t =
    Markdown.to_html ~configuration menu_md
    >>= fun menu ->
    Markdown.to_html_and_toc ~configuration (read_file configuration#index_file)
    >>= fun (content, toc) ->
    let title = configuration#title "Home" in
    Template.make_page ~menu ~title ~stylesheets:configuration#stylesheets ~toc content
    >>= fun markdown_index ->
    write_file (configuration#output_directory // "index.html") ~content:markdown_index;
    List.fold ~init:(return ()) configuration#input_files ~f:begin fun prev path ->
      prev >>= fun () ->
      match File_kind.identify_file path with
      | `Markdown m ->
        let base = Filename.basename m in
        let title = configuration#title base in
        Markdown.to_html_and_toc ~configuration (read_file path)
        >>= fun (content, toc) ->
        Template.make_page ~menu ~title ~stylesheets:configuration#stylesheets ~toc content
        >>= fun content ->
        write_file (configuration#output_directory // sprintf "%s.html" base) ~content;
        return ()
      | `Ocaml_implementation impl ->
        let base = Filename.basename impl in
        let title = configuration#title base in
        Ocaml.to_html ~configuration (read_file path)
        >>= fun (content, toc) ->
        Template.make_page ~title ~menu  ~stylesheets:configuration#stylesheets ~toc content
        >>= fun content ->
        write_file (configuration#output_directory // sprintf "%s.html" base) ~content;
        return ()
      | m ->
        succeedf "cp %s %s/" (Filename.quote path) configuration#output_directory;
        return ()
    end
  in
  List.dedup first_pass_result.more_things_todo |> List.iter ~f:begin function
  | `Create_man_page cmd ->
    let actual_cmd =
      let stripped = String.strip cmd in
      List.find_map configuration#command_substitutions ~f:(fun (left, right) ->
          match String.(sub stripped ~index:0 ~length:(length left)) with
          | Some prefix when prefix = left ->
            Some (right
                  ^ String.(sub_exn stripped ~index:(length left)
                              ~length:(length stripped - length left)))
          | _ -> None) |> Option.value ~default:stripped
    in
    let output_file = configuration#output_directory // Markdown.code_url cmd in
    begin try
      let bash_cmd =
        sprintf "set -o pipefail ; %s=groff | groff -Thtml -mandoc > %s"
          actual_cmd output_file in
      succeedf "bash -c %s" (Filename.quote bash_cmd)
    with
    | e ->
      ignore (
        succeedf "(echo '```' ; %s ; echo '```') > %s" actual_cmd output_file;
        Markdown.to_html_and_toc ~configuration (read_file output_file)
        >>= fun (content, toc) ->
        Markdown.to_html ~configuration menu_md
        >>= fun menu ->
        Template.make_page ~menu ~title:cmd ~stylesheets:configuration#stylesheets ~toc:"" content
        >>= fun content ->
        write_file (output_file) ~content;
        return ()
      );
    end;
  end;
  ()