[@@@module "%/../oar-web.ml"]
[@@@module "%/../oar-utils.ml"]
[@@@module "%/../oar-init.ml"]
include Oar_init
include struct
module Textual_test = struct end end
let%include [@in (module Textual_test) [@as? extension_of]]
textual_test__extension_of srcfile =
match Filename.extension srcfile with
| ".zip" | ".pdf" | ".fig" -> Ok \`raw
| ".ml" | ".typ" | ".md" | ".org" | ".yml" -> Ok \`text
| "" when srcfile = ".firebaserc" -> Ok \`text
| "" -> Error \`no_extension
| s -> Error (\`unlisted s)
let assoc_readme_opt entries =
match List.find_opt (fun (x, _) -> "README.typ" = x) entries with None | Some (_, `dir) -> None
| Some (readme, `file) -> Some readme
let extern_linking =
let open Io in
let check_extern_pat =
let pat = Re.compile @@ Re.seq Re.[bos; str "oar-extern::"; group (any |> rep1); eos] in
Re.exec_opt pat %> Option.map (fun x -> Re.Group.get x 1) in
fun ~cwd entry_dir' path name ->
match Path.kind ~follow:false Path.(cwd / entry_dir' / (String.concat "/" (path @ [name]))) with
\`Symbolic_link ->
let target = Path.read_link Path.(cwd / entry_dir' / (String.concat "/" (path @ [name]))) in
check_extern_pat target
| _ -> None
let colophon = " This website was proudly written in <a href=\"https://ocaml.org\">OCaml</a>, scripted in <a href=\"/~kinten/oar/about.pdf\">Oar</a>, interpreted by <a href=\"/~kinten/u/utop\"><code>~/u/utop</code></a>, and hosted by <a href=\"https://firebase.google.com/docs/hosting/\">Google Firebase Hosting</a>. "
let list_of_files ?(showback = true) ~path ~entries ~cwd ~prefix ~entry_dir' () =
let open Cow in
let icon kind = match kind with
| \`dir -> Html.(span ~cls:"folder" @@ string "")
| _ -> Html.(span ~cls:"file" @@ string "") in
let href name =
match extern_linking ~cwd entry_dir' path name with Some link -> link | None ->
"/" ^ prefix ^ "/" ^ (String.concat "/" (path @ [name])) in
Html.ul ~add_li:false ~cls:"list-of-files" Html.(
( li ~cls:"header" @@ list [ div empty; div (string "Name") ] )
:: ( match path with
| [] when not showback -> empty
| [] -> li @@ list [ div empty; div (a ~href:(Uri.of_string "../") (string "(exit view)")) ]
| _ -> li @@ list [ div empty; div (a ~href:(Uri.of_string "../") (string "../")) ] )
:: List.map (fun (name, kind) ->
li @@ list [ div @@ icon kind; div @@ a ~href:(Uri.of_string @@ href name) @@ string name ]) entries
)
let%include [@in (module Oar_kit)] [@in (module Oar_kit.Firebase) [@as? fsbrowse]]
firebase_fsbrowse ~entry_dir ~prefix ~title ?doc ?ignore:(ignorelist' = []) libname =
let unit_name = mk_unit_name ~scope:!scope__current libname.libname_name in
( match StringMap.find libname.libname_name !lib_to_unit with
| v -> failwith "trying to fulfill a library that has already been taken!"
| exception Not_found ->
lib_to_unit := StringMap.add libname.libname_name unit_name !lib_to_unit;
unit_to_doc := StringMap.add unit_name (\`fsbrowse, doc) !unit_to_doc
);
match Hashtbl.find unit_ls unit_name with
| v -> failwith "duplicated unit name!"
| exception Not_found ->
Hashtbl.add unit_ls unit_name @@
\`firebase_fsbrowse (fun ?copy_public_to env ->
let cwd = Io.Stdenv.cwd env in
let public_dir = Fpath.(entry_dir / "_fsbrowse_public") in
let public_dir' = remove_cwd @@ Fpath.to_string public_dir in
let entry_dir' = remove_cwd @@ Fpath.to_string entry_dir in
let open Io in
let rec iter ~ignore ~path ~on_dir f dir =
let entries = Path.read_dir dir |> Fiber.List.filter (not % ignore) in
let entries = List.map (fun it -> (it, if Path.is_directory Path.(dir / it) then \`dir else \`file)) entries in
let () = on_dir ~path ~entries in
Fiber.List.iter (fun (entry, kind) ->
if kind = \`dir then iter ~ignore ~path:(path @ [entry]) ~on_dir f Path.(dir / entry) else
f ~path ~entries entry
) entries in
let iter ~ignore ~on_dir f dir = iter ~ignore ~path:[] ~on_dir f dir in
let ignorelist = [Re.str "_fsbrowse_public"] @ ignorelist' in
let ignore =
let ignorelist = List.map Re.compile ignorelist in
fun x -> List.exists (fun re -> Re.execp re x) ignorelist in
(* Io.traceln "%s" public_dir'; *)
copy_public_to |> Option.iter @@ fun (copy_public_root, process_mgr, fs) ->
let on_dir ~path ~entries =
let newdir = Path.(cwd / public_dir' / (String.concat "/" path)) in
let indexfile = Path.(cwd / public_dir' / (String.concat "/" path) / "index.html") in
Path.mkdirs ~exists_ok:true ~perm:0o700 newdir;
Path.save ~create:(\`Exclusive 0o700) indexfile ("
<html>
<head>
" ^ Oar_web.head_meta ^ "
<link rel=\"stylesheet\" href=\"" ^ ("/" ^ prefix ^ "/style.css") ^ "\"></head>
<title>" ^ (String.concat "/" path) ^ " - " ^ title ^ "</title>
<body class=\"fsbrowse\">
<header>
</header>
<main class=\"render\">
" ^ (
match assoc_readme_opt entries with None -> "" | Some readme ->
Path.load @@ Oar_web.Typst.html_compile ~fs ~process_mgr @@ Path.(cwd / entry_dir' / (String.concat "/" (path @ [readme])))
) ^ "
</main>
<aside>
<hgroup><button onclick=\"if (document.body.classList.contains('sidebar-collapsed')) { document.body.classList.remove('sidebar-collapsed') } else { document.body.classList.add('sidebar-collapsed') }\">Toggle</button></hgroup>
<nav>" ^ (Cow.Html.to_string @@ list_of_files ~path ~entries ~cwd ~prefix ~entry_dir' ()) ^ "</nav>
</aside>
</body>
</html>
") in
let physlink process_mgr ~link_to file =
Process.run process_mgr ["ln"; Path.native_exn link_to; Path.native_exn file] in
let on_file_raw ~path ~entries srcfile =
let newfile = Path.(cwd / public_dir' / (String.concat "/" (path @ [srcfile]))) in
let oldfile = Path.(cwd / entry_dir' / (String.concat "/" (path @ [srcfile]))) in
physlink process_mgr newfile ~link_to:oldfile in
let on_file' ~path ~entries srcfile =
let newdir = Path.(cwd / public_dir' / (String.concat "/" (path @ [srcfile]))) in
let newfile = Path.(cwd / public_dir' / (String.concat "/" (path @ [srcfile])) / "index.html") in
Path.mkdirs ~exists_ok:true ~perm:0o700 newdir;
Path.save ~create:(\`Exclusive 0o700) newfile ("
<html>
<head>
" ^ Oar_web.head_meta ^ "
<link rel=\"stylesheet\" href=\"" ^ ("/" ^ prefix ^ "/style.css") ^ "\">
<title>" ^ (String.concat "/" (path @ [srcfile])) ^ " - " ^ title ^ "</title>
</head>
<body class=\"fsbrowse\">
<header>
</header>
<main class=\"raw\">" ^ (Oar_web.Typst__codewrap.code_enter @@ Path.load @@ Oar_web.Typst__codewrap.html_compile ~fs ~process_mgr Path.(cwd / entry_dir' / (String.concat "/" (path @ [srcfile])))) ^ "</main>
<aside>
<hgroup><button onclick=\"if (document.body.classList.contains('sidebar-collapsed')) { document.body.classList.remove('sidebar-collapsed') } else { document.body.classList.add('sidebar-collapsed') }\">Toggle</button></hgroup>
<nav>" ^ (Cow.Html.to_string @@ list_of_files ~path ~entries ~cwd ~prefix ~entry_dir' ()) ^ "</nav>
</aside>
</body>
</html>
") in
let on_file ~path ~entries srcfile =
if Option.is_some @@ extern_linking ~cwd entry_dir' path srcfile then () else
match Textual_test.extension_of srcfile with
| Result.Ok \`raw -> on_file_raw ~path ~entries srcfile
| Result.Ok \`text -> on_file' ~path ~entries srcfile
| Result.Error \`no_extension -> failwith (Printf.sprintf "Firebase.fsbrowse: file '%s' has no extension" (String.concat "/" path ^ "/" ^ srcfile))
| Result.Error (`unlisted s) -> failwith (Printf.sprintf "Firebase.fsbrowse: don't know how to handle file of extension '%s'" s) in
let on_root_dir ~entries () =
let newdir = Path.(cwd / public_dir') in
Path.mkdirs ~exists_ok:true ~perm:0o700 newdir;
begin
let stylefile = Path.(cwd / public_dir' / "style.css") in
Path.save ~create:(\`Exclusive 0o700) stylefile Oar_web.style
end;
begin
let iconfile = Path.(cwd / public_dir' / "icon.file.svg") in
Path.save ~create:(\`Exclusive 0o700) iconfile Oar_web.icon__file
end;
begin
let iconfile = Path.(cwd / public_dir' / "icon.folder.svg") in
Path.save ~create:(\`Exclusive 0o700) iconfile Oar_web.icon__folder
end;
begin
let indexfile = Path.(cwd / public_dir' / "index.html") in
Path.save ~create:(\`Exclusive 0o700) indexfile ("
<html>
<head>
" ^ Oar_web.head_meta ^ "
<link rel=\"stylesheet\" href=\"" ^ ("/" ^ prefix ^ "/style.css") ^ "\">
<title>" ^ title ^ "</title>
</head>
<body class=\"fsbrowse root\">
<header class=\"first\">
</header>
<header class=\"second\">
<nav>" ^ (Cow.Html.to_string @@ list_of_files ~showback:false ~path:[] ~entries ~cwd ~prefix ~entry_dir' ()) ^ "</nav>
<aside>
<h2>" ^ title ^ "</h2>
" ^ (match doc with None -> "" | Some synopsis -> "<summary>" ^ Oar_web.pre_escape synopsis ^ "</summary>") ^ "
</aside>
</header>
<main class=\"render\">
" ^ (
match assoc_readme_opt entries with None -> "" | Some readme ->
Path.load @@ Oar_web.Typst.html_compile ~fs ~process_mgr @@ Path.(cwd / entry_dir' / readme)
) ^ "
</main>
</body>
</html>
")
end in
let copy_public_root' = remove_cwd @@ Fpath.to_string copy_public_root in
Path.rmtree ~missing_ok:true Path.(cwd / public_dir');
Path.mkdirs ~exists_ok:false ~perm:0o700 Path.(cwd / public_dir');
iter ~ignore ~on_dir:(fun ~path ~entries -> if path = [] then on_root_dir ~entries () else on_dir ~path ~entries) on_file Path.(cwd / entry_dir');
Path.mkdirs ~exists_ok:true ~perm:0o700 Path.(cwd / copy_public_root' / prefix);
Process.run process_mgr ["cp"; "-r";
Path.native_exn Path.(cwd / public_dir');
Path.native_exn Path.(cwd / copy_public_root')
];
Oar_utils.rename_lax Path.rename Path.(cwd / copy_public_root' / Filename.basename public_dir') Path.(cwd / copy_public_root' / prefix)
)