documents¶

In [ ]:
open file_system_operators
open sm'_operators
open rust.rust_operators
open rust
In [ ]:
//// test

open testing

args¶

In [ ]:
inl get_args () =
    {
        source_dir = "source-dir", 's'
        dist_dir = "dist-dir", 'd'
        cache_dir = "cache-dir", 'c'
        hangul_spec = "hangul-spec", 'h'
    }

get_command¶

In [ ]:
let get_command () =
    ##"command"
    |> runtime.new_command
    |> runtime.command_init_arg (get_args () .source_dir) (
        runtime.arg_required true
    )
    |> runtime.command_init_arg (get_args () .dist_dir) (
        runtime.arg_required true
    )
    |> runtime.command_init_arg (get_args () .cache_dir) (
        runtime.arg_required true
    )
    |> runtime.command_init_arg (get_args () .hangul_spec) (
        runtime.arg_required true
    )

crowbook¶

In [ ]:
let crowbook hangul { ext dist_dir dist_path output_path } =
    inl exit_code, result =
        inl args =
            inl default ext =
                $'$" rendering.num_depth 6"'
                +. $'$" rendering.highlight.theme \\\\\\"Solarized (dark)\\\\\\""'
            match ext with
            | "html" =>
                $'$"--set"'
                +. $'$" html.css.add \\\\\\"\'"'
                +. $'$" body {{ color: #e8e6e3; background-color: #202324; }}"'
                +. $'$" a {{ color: #989693; }}"'
                +. $'$" pre {{ background-color: #1b1b1b; padding: 10px; }}"'
                +. $'$" \'\\\\\\""'
                +. default ext
            | "pdf" =>
                $'$"--set"'
                +. $'$" tex.paper.size a4paper"'
                +. $'$" tex.template.add \\\"\\\\pagenumbering{{gobble}}\\\""'
                +. (
                    if hangul |> not
                    then ""
                    else
                        $'$" tex.template.add \\\"\\\\usepackage{{polyglossia}}\\\""'
                        +. $'$" tex.template.add \\\"\\\\setmainlanguage{{korean}}\\\""'
                        +. $'$" tex.template.add \\\"\\\\setmainfont{{NanumGothicCoding}}\\\""'
                        +. $'$" tex.font.size 13"'
                )
                +. default ext
            | "epub" =>
                $'$"--set"'
                +. $'$" epub.version 3"'
                +. $'$" html.css.add \\\\\\"\' "'
                +. $'$" body {{ color: #e8e6e3; background-color: #202324; }} "'
                +. $'$" a {{ color: #989693; }} "'
                +. $'$" \'\\\\\\""'
                +. default ext
            | _ => ""
        runtime.execution_options fun x => { x with
            command =
                $'$"crowbook --verbose --to {!ext}"'
                +. $'$" --single \\\"{!dist_path}\\\" --output \\\"{!output_path}\\\" {!args}"'
            working_directory = dist_dir |> Some |> optionm'.box
        }
        |> runtime.execute_with_options
    if result |> sm'.contains "ERROR" |> not
    then (exit_code, result) |> Ok
    else
        trace Warning
            fun () => "documents.crowbook / result contains ERROR"
            fun () => { exit_code output_path result }
        (exit_code, result) |> Error

hangul¶

In [ ]:
let hangul workspace_root hangul_spec { ext dist_dir dist_path output_path } =
    inl lines =
        dist_path
        |> file_system.read_all_text
        |> sm'.split "\n"
        |> fun x => a x : _ int _
        |> am.map sm'.trim
    inl text =
        lines
        |> am.filter ((<>.) "")
        |> seq.of_array'
        |> sm'.concat "\n"
        |> fun x => $'$"{!x}\n\n"'
    inl exit_code, result =
        runtime.execution_options fun x => { x with
            command =
                inl hangulize_path =
                    workspace_root </> $'$"../alphabet/deps/hangulize/cmd/hangulize/hangulize{!(platform.get_executable_suffix ())}"'
                $'$"{!hangulize_path} {!hangul_spec}"'
            stdin =
                fun stdin =>
                    inl stdin =
                        stdin
                        |> threading.arc_mutex_lock
                        |> resultm.unwrap'
                    text |> runtime.stdin_write_all stdin
                |> Some |> optionm'.box
        }
        |> runtime.execute_with_options
    inl result =
        inl result =
            result
            |> sm'.split "\n"
            |> fun x => a x : _ int _
        inl result_len = result |> am'.length
        (("", (0, 0)), lines)
        ||> am.fold fun (acc, (i, n)) x =>
            if x = ""
            then $'$"{!acc}\n"', (i + 1, n + 1)
            else
                inl acc =
                    inl i = i - n
                    if i >= result_len
                    then acc
                    else
                        inl line = result |> am'.index i
                        if i = result_len - 1
                        then $'$"{!acc}{!line}"'
                        else $'$"{!acc}{!line}\n"'
                acc, (i + 1, n)
        |> fst
    result |> file_system.write_all_text output_path
    trace Info
        fun () => "documents.hangul"
        fun () => { exit_code result_len = result |> sm'.length : int; output_path }
    (exit_code, result) |> Ok
    |> fun x => x : result (int * string) (int * string)

fix_paths¶

In [ ]:
let fix_paths { cache_dir dist_path ext } =
    inl file_name = dist_path |> file_system.get_file_name
    inl cache_path = cache_dir </> file_name
    inl dist_path_no_ext =
        dist_path
        |> sm'.slice 0 ((dist_path |> sm'.last_index_of ".") - 1)
    inl cache_path_no_ext =
        cache_path
        |> sm'.slice 0 ((cache_path |> sm'.last_index_of ".") - 1)
    inl is_md = ext |> sm'.ends_with ".md"
    inl output_path : string =
        if is_md |> not
        then $'$"{!dist_path}.{!ext}"'
        else $'$"{!dist_path_no_ext}.{!ext}"'
    inl output_cache_path : string =
        if is_md |> not
        then $'$"{!cache_path}.{!ext}"'
        else $'$"{!cache_path_no_ext}.{!ext}"'
    { output_path output_cache_path }

files_fn¶

In [ ]:
inl files_fn { dist_dir cache_dir } fn dist_path ext =
    inl { output_path output_cache_path } = fix_paths { cache_dir dist_path ext }
    inl equal =
        if (output_path |> file_system.file_exists |> not)
            || (output_cache_path |> file_system.file_exists |> not)
        then false
        else
            inl output_hash =
                output_path
                |> crypto.get_file_hash
                |> resultm.map_error' sm'.format'
                |> resultm.unbox'
                |> resultm.get
            inl output_cache_hash =
                output_cache_path
                |> crypto.get_file_hash
                |> resultm.map_error' sm'.format'
                |> resultm.unbox'
                |> resultm.get
            output_hash = output_cache_hash
    if equal
    then None
    else
        match fn { ext dist_dir dist_path output_path } with
        | Ok (exit_code, result) when exit_code <>. 0 =>
            trace Info
                fun () => "documents.files_fn / error"
                fun () => { exit_code result }
            new_pair output_path result |> Error |> resultm.box |> Some
        | Error (exit_code, result) =>
            new_pair output_path result |> Error |> resultm.box |> Some
        | _ =>
            if output_path |> file_system.file_exists
            then output_path |> file_system.file_copy output_cache_path
            else failwith $'$"documents.files_fn / {!output_path} should exist"'
            output_path |> Ok |> resultm.box |> Some

run¶

In [ ]:
let run { source_dir dist_dir cache_dir hangul_spec }
    : async.future_pin (
        resultm.result'
            (am'.vec (
                resultm.result'
                    (pair string (am'.vec (optionm'.option' (resultm.result' string (pair string string)))))
                    sm'.std_string
            ))
            sm'.std_string
    )
    =
    inl workspace_root = file_system.get_workspace_root ()
    inl source_dir = source_dir |> file_system.get_full_path
    inl dist_dir = dist_dir |> file_system.get_full_path
    inl cache_dir = cache_dir |> file_system.get_full_path
    trace Debug
        fun () => "documents.run"
        fun () => { source_dir dist_dir cache_dir hangul_spec }
    fun () =>
        inl files =
            dist_dir
            |> file_system.new_walk_dir
            |> file_system.walk_dir_filter fun entry => async.new_future_move_send fun () =>
                entry
                |> file_system.dir_entry_file_type
                |> async.await_send
                |> resultm.map_error' sm'.format'
                |> resultm.unbox
                |> function
                    | Ok file_type when file_type |> file_system.file_type_is_dir =>
                        file_system.Ignore
                    | _ =>
                        inl path =
                            entry
                            |> file_system.dir_entry_path
                            |> file_system.path_buf_display
                            |> sm'.format'
                            |> sm'.from_std_string
                        if (path |> sm'.ends_with ".md" |> not) || (path |> sm'.ends_with ".hangul.md")
                        then file_system.Ignore
                        else file_system.Continue
            |> async.stream_filter_map_futures fun (entry : _ _ file_system.async_walkdir_error) =>
                match entry |> resultm.map_error' sm'.format' |> resultm.unbox with
                | Ok entry =>
                    entry
                    |> file_system.dir_entry_path
                    |> file_system.path_buf_display
                    |> sm'.format'
                    |> sm'.from_std_string
                    |> Some
                | Error error =>
                    trace Critical
                        fun () => "documents.run / stream_filter_map"
                        fun () => { error }
                    None
                |> optionm'.box
            |> async.stream_collect_futures
            |> async.await
        trace Debug
            fun () => "documents.run"
            fun () => { files_len = files |> am'.vec_len }
        files
        |> async.into_par_iter
        |> async.par_map fun file =>
            inl file = file |> file_system.get_full_path
            inl relative_path =
                file
                |> sm'.to_std_string
                |> file_system.new_path_buf
                |> file_system.path_buf_display
                |> sm'.format'
                |> sm'.from_std_string
                |> sm'.replace dist_dir (join "")
                |> sm'.replace "\\" "/"
                |> fun s => $'$".{!s}"'
            inl file = file |> file_system.normalize_path
            inl real_path = source_dir </> relative_path |> file_system.standardize_path
            inl origin_hash_exit_code, origin_hash =
                runtime.execution_options fun x => { x with
                    command =
                        $'$"git ls-tree --format=\'%%(objectname)\' origin/gh-pages \\"{!real_path}\\""'
                    working_directory = source_dir |> Some |> optionm'.box
                }
                |> runtime.execute_with_options
            inl dist_path = dist_dir </> relative_path |> file_system.standardize_path
            inl local_git_hash_exit_code, local_git_hash =
                runtime.execution_options fun x => { x with
                    command = $'$"git hash-object \\"{!dist_path}\\""'
                    working_directory = dist_dir |> Some |> optionm'.box
                }
                |> runtime.execute_with_options
            inl cache_path = cache_dir </> relative_path |> file_system.standardize_path
            inl files =
                inl files_fn = files_fn { dist_dir cache_dir }
                inl files = [
                    "hangul.md", dist_path, hangul workspace_root hangul_spec |> files_fn
                ]
                inl { output_path } = fix_paths { dist_path cache_dir ext = "hangul.md" }
                inl files' = [
                    "html", dist_path, crowbook false |> files_fn
                    "pdf", dist_path, crowbook false |> files_fn
                    "epub", dist_path, crowbook false |> files_fn
                    "html", output_path, crowbook true |> files_fn
                    "pdf", output_path, crowbook true |> files_fn
                    "epub", output_path, crowbook true |> files_fn
                ]
                [ files; files' ]
            inl files' =
                if origin_hash |> sm'.contains local_git_hash
                then []
                else
                    inl hash1 =
                        dist_path
                        |> crypto.get_file_hash
                        |> resultm.map_error' sm'.format'
                        |> resultm.unbox'
                        |> resultm.get
                    inl hash2 =
                        if cache_path |> file_system.file_exists |> not
                        then None
                        else
                            cache_path
                            |> crypto.get_file_hash
                            |> resultm.map_error' sm'.format'
                            |> resultm.unbox'
                            |> resultm.ok
                    match hash2 with
                    | Some hash2 when hash1 = hash2 => []
                    | _ =>
                        trace Info
                            fun () =>
                                "documents.run / par_map"
                                +. " / origin_hash |> sm\'.contains local_git_hash |> not"
                                +. " / Some hash2 when hash1 = hash2"
                            fun () => {
                                file
                                real_path
                                relative_path
                                origin_hash_exit_code
                                origin_hash
                                local_git_hash_exit_code
                                local_git_hash
                                hash1
                                hash2
                                dist_path
                                cache_path
                            }
                        dist_path |> file_system.file_copy cache_path
                        files
            inl files' =
                if files' <> []
                then files'
                else
                    files
                    |> listm.map fun files =>
                        files
                        |> listm'.filter fun ext, path, fn =>
                            inl { output_path output_cache_path } =
                                fix_paths { cache_dir dist_path = path; ext }
                            if output_path |> file_system.file_exists
                            then true
                            elif output_cache_path |> file_system.file_exists |> not
                            then true
                            else
                                trace Info
                                    fun () => "documents.run / par_map / files\' = [] / listm.iter"
                                    fun () => { output_path output_cache_path }
                                output_cache_path |> file_system.file_copy output_path
                                false
            files'
            |> listm'.box
            |> listm'.to_array'
            |> am'.to_vec
            |> am'.vec_collect fun files =>
                files
                |> listm'.box
                |> listm'.to_array'
                |> am'.to_vec
                |> async.into_par_iter
                |> async.par_map fun ext, path, fn =>
                    fn path ext |> optionm'.box
                |> async.par_collect
            |> new_pair file
            |> Ok
            |> resultm.box
        |> async.par_collect
        |> Ok
        |> resultm.box
    |> async.new_future_move
In [ ]:
//// test
///! rust -d async-walkdir encoding_rs encoding_rs_io futures futures-lite rayon regex sha2

inl workspace_root = file_system.get_workspace_root ()
inl source_dir = workspace_root </> "../alphabet/target/documents"
inl dist_dir = source_dir </> "dist"
inl cache_dir = source_dir </> "cache"
inl file_name_no_ext = "test"
inl file_name = join $'$"{!file_name_no_ext}.md"'

source_dir |> file_system.directory_delete true
dist_dir |> file_system.directory_delete true
cache_dir |> file_system.directory_delete true

source_dir |> file_system.create_dir |> ignore
dist_dir |> file_system.create_dir |> ignore
cache_dir |> file_system.create_dir |> ignore

inl text = "# a\n\n## b\n\n---\n\nabc\nabc\n\nabc\n\nabc\n"
text |> file_system.write_all_text (source_dir </> file_name)
text |> file_system.write_all_text (dist_dir </> file_name)

inl html_path = dist_dir </> $'$"{!file_name}.html"' |> file_system.absolute_path
inl pdf_path = dist_dir </> $'$"{!file_name}.pdf"' |> file_system.absolute_path
inl epub_path = dist_dir </> $'$"{!file_name}.epub"' |> file_system.absolute_path
inl hangul_path = dist_dir </> $'$"{!file_name_no_ext}.hangul.md"' |> file_system.absolute_path
inl hangul_html_path = dist_dir </> $'$"{!file_name_no_ext}.hangul.md.html"' |> file_system.absolute_path
inl hangul_pdf_path = dist_dir </> $'$"{!file_name_no_ext}.hangul.md.pdf"' |> file_system.absolute_path
inl hangul_epub_path = dist_dir </> $'$"{!file_name_no_ext}.hangul.md.epub"' |> file_system.absolute_path

run {
    source_dir
    dist_dir
    cache_dir
    hangul_spec = "por-br"
}
|> async.block_on_futures
|> resultm.unwrap'
|> sm'.format_debug'
|> sm'.from_std_string
|> _assert_eq (
    ;[
        Ok (
            (dist_dir </> file_name |> file_system.absolute_path),
            ;[
                hangul_path
                html_path
                pdf_path
                epub_path
                hangul_html_path
                hangul_pdf_path
                hangul_epub_path
            ]
            |> am'.to_vec |> am'.vec_map (Ok >> resultm.box >> Some >> optionm'.box)
            |> fun x => x : _ (_ (_ _ sm'.std_string))
        )
    ]
    |> am'.to_vec
    |> am'.vec_map resultm.box
    |> fun x => x : _ (_ _ sm'.std_string)
    |> sm'.format_debug'
    |> sm'.from_std_string
)

dist_dir |> file_system.directory_delete true
dist_dir |> file_system.create_dir |> ignore

text |> file_system.write_all_text (dist_dir </> file_name)

run {
    source_dir = source_dir
    dist_dir = dist_dir
    cache_dir = cache_dir
    hangul_spec = "por-br"
}
|> async.block_on_futures
|> resultm.unwrap'
|> sm'.format_debug'
|> sm'.from_std_string
|> _assert_eq (
    ;[
        Ok (
            (dist_dir </> file_name |> file_system.absolute_path),
            ;[]
            |> am'.to_vec |> am'.vec_map ((optionm.map resultm.box) >> optionm'.box)
        )
    ]
    |> am'.to_vec
    |> am'.vec_map resultm.box
    |> fun x =>
        x : am'.vec (
            resultm.result'
                (string * am'.vec (optionm'.option' (resultm.result' string string)))
                sm'.std_string
        )
    |> sm'.format_debug'
    |> sm'.from_std_string
)

run {
    source_dir = source_dir
    dist_dir = dist_dir
    cache_dir = cache_dir
    hangul_spec = "por-br"
}
|> async.block_on_futures
|> resultm.unwrap'
|> sm'.format_debug'
|> sm'.from_std_string
|> _assert_eq (
    ;[
        Ok (
            (dist_dir </> file_name |> file_system.absolute_path),
            ;[ None; None; None; None; None; None; None ]
            |> am'.to_vec |> am'.vec_map ((optionm.map resultm.box) >> optionm'.box)
        )
    ]
    |> am'.to_vec
    |> am'.vec_map resultm.box
    |> fun x =>
        x : am'.vec (
            resultm.result'
                (string * am'.vec (optionm'.option' (resultm.result' string string)))
                sm'.std_string
        )
    |> sm'.format_debug'
    |> sm'.from_std_string
)

hangul_path
|> file_system.read_all_text
|> _assert_eq "# 아\n\n## 브\n\n---\n\n아브크\n아브크\n\n아브크\n\n아브크\n"

html_path
|> file_system.read_all_text
|> sm'.contains "<p class = \"rule\">***</p>\n<p id = \"para-1\">abc abc</p>"
|> _assert_eq true

pdf_path
|> file_system.read_all_bytes
|> sm'.slice_contains "%PDF-1.5"
|> _assert_eq true

epub_path
|> file_system.read_all_bytes
|> sm'.slice_contains "application/epub+zip"
|> _assert_eq true

hangul_html_path
|> file_system.read_all_text
|> sm'.contains "<p class = \"rule\">***</p>\n<p id = \"para-1\">아브크 아브크</p>"
|> _assert_eq true

hangul_pdf_path
|> file_system.read_all_bytes
|> sm'.slice_contains "%PDF-1.5"
|> _assert_eq true

hangul_epub_path
|> file_system.read_all_bytes
|> sm'.slice_contains "application/epub+zip"
|> _assert_eq true
00:00:00 v #1 file_system.create_dir / { dir = /home/runner/work/alphabet/polyglot/../alphabet/target/documents }
00:00:00 v #2 file_system.create_dir / { dir = /home/runner/work/alphabet/polyglot/../alphabet/target/documents/dist }
00:00:00 v #3 file_system.create_dir / { dir = /home/runner/work/alphabet/polyglot/../alphabet/target/documents/cache }
00:00:00 d #4 documents.run / { source_dir = /home/runner/work/alphabet/alphabet/target/documents; dist_dir = /home/runner/work/alphabet/alphabet/target/documents/dist; cache_dir = /home/runner/work/alphabet/alphabet/target/documents/cache; hangul_spec = por-br }
00:00:00 d #5 documents.run / { files_len = 1 }
00:00:00 d #6 runtime.execute_with_options / { file_name = git; arguments = ["ls-tree", "--format='%(objectname)'", "origin/gh-pages", "/home/runner/work/alphabet/alphabet/target/documents/test.md"]; options = { command = git ls-tree --format='%(objectname)' origin/gh-pages "/home/runner/work/alphabet/alphabet/target/documents/test.md"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents",
) } }
00:00:00 v #7 ! fatal: Not a valid object name origin/gh-pages
00:00:00 v #8 runtime.execute_with_options / result / { exit_code = 128; std_trace_length = 56 }
00:00:00 d #9 runtime.execute_with_options / { file_name = git; arguments = ["hash-object", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md"]; options = { command = git hash-object "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents/dist",
) } }
00:00:00 v #10 > ba0ba7eb68b2a508ff0525bcbb91bd5ebc95e71b
00:00:00 v #11 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 40 }
00:00:00 i #12 documents.run / par_map / origin_hash |> sm'.contains local_git_hash |> not / Some hash2 when hash1 = hash2 / { file = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md; real_path = /home/runner/work/alphabet/alphabet/target/documents/test.md; relative_path = ./test.md; origin_hash_exit_code = 128; origin_hash = fatal: Not a valid object name origin/gh-pages; local_git_hash_exit_code = 0; local_git_hash = ba0ba7eb68b2a508ff0525bcbb91bd5ebc95e71b; hash1 = f87ee09ec1dadc95e06e23bef39aa3185381d285bb9ca41a8a112061a6f504ec; hash2 = US0_1; dist_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md; cache_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md }
00:00:00 d #13 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md; new_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md; result = 34 }
00:00:00 d #14 runtime.execute_with_options / { file_name = /home/runner/work/alphabet/polyglot/../alphabet/deps/hangulize/cmd/hangulize/hangulize; arguments = ["por-br"]; options = { command = /home/runner/work/alphabet/polyglot/../alphabet/deps/hangulize/cmd/hangulize/hangulize por-br; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = Some(
    fable_library_rust::module_3bd9ae6a::FuncType::Func1<alloc::sync::Arc<std::sync::mutex::Mutex<std::process::ChildStdin>>, ()>,
); trace = true; working_directory = None } }
00:00:00 v #15 > # 아
00:00:00 v #16 > ## 브
00:00:00 v #17 > ---
00:00:00 v #18 > 아브크
00:00:00 v #19 > 아브크
00:00:00 v #20 > 아브크
00:00:00 v #21 > 아브크
00:00:00 v #22 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 28 }
00:00:00 i #23 documents.hangul / { exit_code = 0; result_len = 34; output_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md }
00:00:00 d #24 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md; new_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md; result = 62 }
00:00:00 d #25 runtime.execute_with_options / { file_name = crowbook; arguments = ["--verbose", "--to", "html", "--single", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md", "--output", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.html", "--set", "html.css.add", "' body { color: #e8e6e3; background-color: #202324; } a { color: #989693; } pre { background-color: #1b1b1b; padding: 10px; } '"]; options = { command = crowbook --verbose --to html --single "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md" --output "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.html" --set html.css.add \"' body { color: #e8e6e3; background-color: #202324; } a { color: #989693; } pre { background-color: #1b1b1b; padding: 10px; } '\" rendering.num_depth 6 rendering.highlight.theme \"Solarized (dark)\"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents/dist",
) } }
00:00:00 d #26 runtime.execute_with_options / { file_name = crowbook; arguments = ["--verbose", "--to", "pdf", "--single", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md", "--output", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.pdf", "--set", "tex.paper.size", "a4paper", "tex.template.add", "\\pagenumbering{gobble}", "rendering.num_depth", "6", "rendering.highlight.theme", "Solarized (dark)"]; options = { command = crowbook --verbose --to pdf --single "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md" --output "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.pdf" --set tex.paper.size a4paper tex.template.add "\pagenumbering{gobble}" rendering.num_depth 6 rendering.highlight.theme \"Solarized (dark)\"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents/dist",
) } }
00:00:00 d #27 runtime.execute_with_options / { file_name = crowbook; arguments = ["--verbose", "--to", "html", "--single", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md", "--output", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.html", "--set", "html.css.add", "' body { color: #e8e6e3; background-color: #202324; } a { color: #989693; } pre { background-color: #1b1b1b; padding: 10px; } '"]; options = { command = crowbook --verbose --to html --single "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md" --output "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.html" --set html.css.add \"' body { color: #e8e6e3; background-color: #202324; } a { color: #989693; } pre { background-color: #1b1b1b; padding: 10px; } '\" rendering.num_depth 6 rendering.highlight.theme \"Solarized (dark)\"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents/dist",
) } }
00:00:00 v #28 ! CROWBOOK 0.17.0
00:00:00 v #29 ! 15:44:15 [DEBUG] (1) crowbook::book: Ignoring YAML block:
00:00:00 v #30 ! CROWBOOK 0.17.0
00:00:00 v #31 ! 15:44:15 [DEBUG] (1) crowbook::book: Ignoring YAML block:
00:00:00 v #32 !
00:00:00 v #32 !
00:00:00 v #33 ! 15:44:15 [DEBUG] (1) crowbook::book: Attempting to generate html...
00:00:00 v #34 ! CROWBOOK 0.17.0
00:00:00 v #35 ! 15:44:15 [DEBUG] (1) crowbook::book: Attempting to generate html...
00:00:00 v #36 ! 15:44:15 [DEBUG] (1) crowbook::book: Ignoring YAML block:
00:00:00 v #37 ! 15:44:15 [DEBUG] (1) epub_builder::toc: rendered elem: "<li><a href=\"#link-1\">아</a></li>"
00:00:00 v #38 !
00:00:00 v #39 ! 15:44:15 [DEBUG] (1) crowbook::book: Attempting to generate pdf...
00:00:00 v #40 ! 15:44:15 [DEBUG] (1) crowbook::latex: Attempting to run LaTeX on generated file
00:00:00 v #41 ! 15:44:15 [DEBUG] (1) epub_builder::toc: rendered elem: "<li><a href=\"#link-1\">a</a></li>"
00:00:00 v #42 ! 15:44:15 [INFO] crowbook::book: Succesfully generated HTML (standalone page): test.hangul.md.html
00:00:00 v #43 ! 15:44:15 [INFO] crowbook::book: Succesfully generated HTML (standalone page): test.md.html
00:00:00 v #44 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 392 }
00:00:00 d #45 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.html; new_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md.html; result = 11612 }
00:00:00 v #46 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 385 }
00:00:00 d #47 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md.html; new_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md.html; result = 11586 }
00:00:00 d #48 runtime.execute_with_options / { file_name = crowbook; arguments = ["--verbose", "--to", "epub", "--single", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md", "--output", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.epub", "--set", "epub.version", "3", "html.css.add", "'  body { color: #e8e6e3; background-color: #202324; }  a { color: #989693; }  '"]; options = { command = crowbook --verbose --to epub --single "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md" --output "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.epub" --set epub.version 3 html.css.add \"'  body { color: #e8e6e3; background-color: #202324; }  a { color: #989693; }  '\" rendering.num_depth 6 rendering.highlight.theme \"Solarized (dark)\"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents/dist",
) } }
00:00:00 d #49 runtime.execute_with_options / { file_name = crowbook; arguments = ["--verbose", "--to", "epub", "--single", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md", "--output", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.epub", "--set", "epub.version", "3", "html.css.add", "'  body { color: #e8e6e3; background-color: #202324; }  a { color: #989693; }  '"]; options = { command = crowbook --verbose --to epub --single "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md" --output "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.epub" --set epub.version 3 html.css.add \"'  body { color: #e8e6e3; background-color: #202324; }  a { color: #989693; }  '\" rendering.num_depth 6 rendering.highlight.theme \"Solarized (dark)\"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents/dist",
) } }
00:00:00 v #50 ! CROWBOOK 0.17.0
00:00:00 v #51 ! 15:44:15 [DEBUG] (1) crowbook::book: Ignoring YAML block:
00:00:00 v #52 !
00:00:00 v #53 ! 15:44:15 [DEBUG] (1) crowbook::book: Attempting to generate epub...
00:00:00 v #54 ! CROWBOOK 0.17.0
00:00:00 v #55 ! 15:44:15 [DEBUG] (1) crowbook::book: Ignoring YAML block:
00:00:00 v #56 !
00:00:00 v #57 ! 15:44:15 [DEBUG] (1) crowbook::book: Attempting to generate epub...
00:00:00 d #58 runtime.execute_with_options / { file_name = crowbook; arguments = ["--verbose", "--to", "pdf", "--single", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md", "--output", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.pdf", "--set", "tex.paper.size", "a4paper", "tex.template.add", "\\pagenumbering{gobble}", "tex.template.add", "\\usepackage{polyglossia}", "tex.template.add", "\\setmainlanguage{korean}", "tex.template.add", "\\setmainfont{NanumGothicCoding}", "tex.font.size", "13", "rendering.num_depth", "6", "rendering.highlight.theme", "Solarized (dark)"]; options = { command = crowbook --verbose --to pdf --single "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md" --output "/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.pdf" --set tex.paper.size a4paper tex.template.add "\pagenumbering{gobble}" tex.template.add "\usepackage{polyglossia}" tex.template.add "\setmainlanguage{korean}" tex.template.add "\setmainfont{NanumGothicCoding}" tex.font.size 13 rendering.num_depth 6 rendering.highlight.theme \"Solarized (dark)\"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents/dist",
) } }
00:00:00 v #59 ! CROWBOOK 0.17.0
00:00:00 v #60 ! 15:44:15 [DEBUG] (1) crowbook::book: Ignoring YAML block:
00:00:00 v #61 !
00:00:00 v #62 ! 15:44:15 [DEBUG] (1) crowbook::book: Attempting to generate pdf...
00:00:00 v #63 ! 15:44:15 [DEBUG] (1) crowbook::latex: Attempting to run LaTeX on generated file
00:00:00 v #64 ! 15:44:15 [DEBUG] (1) epub_builder::epub: Add resource: "stylesheet.css"
00:00:00 v #65 ! 15:44:15 [DEBUG] (1) epub_builder::epub: render_opf...
00:00:00 v #66 ! 15:44:15 [DEBUG] (1) epub_builder::epub: id="title_page.xhtml", mime="application/xhtml+xml"
00:00:00 v #67 ! 15:44:15 [DEBUG] (1) epub_builder::epub: content = Content { file: "title_page.xhtml", mime: "application/xhtml+xml", itemref: true, cover: false, reftype: Some(TitlePage), title: "Title" }
00:00:00 v #68 ! 15:44:15 [DEBUG] (1) epub_builder::epub: id="chapter_000.xhtml", mime="application/xhtml+xml"
00:00:00 v #69 ! 15:44:15 [DEBUG] (1) epub_builder::epub: content = Content { file: "chapter_000.xhtml", mime: "application/xhtml+xml", itemref: true, cover: false, reftype: Some(Text), title: "a" }
00:00:00 v #70 ! 15:44:15 [DEBUG] (1) epub_builder::epub: id="stylesheet.css", mime="text/css"
00:00:00 v #71 ! 15:44:15 [DEBUG] (1) epub_builder::toc: rendered elem: "<li><a href=\"title_page.xhtml\">Title</a></li>"
00:00:00 v #72 ! 15:44:15 [DEBUG] (1) epub_builder::toc: rendered elem: "<li><a href=\"chapter_000.xhtml\">a</a></li>"
00:00:00 v #64 ! 15:44:15 [DEBUG] (1) epub_builder::epub: Add resource: "stylesheet.css"
00:00:00 v #73 ! 15:44:15 [DEBUG] (1) epub_builder::epub: render_opf...
00:00:00 v #74 ! 15:44:15 [DEBUG] (1) epub_builder::epub: id="title_page.xhtml", mime="application/xhtml+xml"
00:00:00 v #75 ! 15:44:15 [DEBUG] (1) epub_builder::epub: content = Content { file: "title_page.xhtml", mime: "application/xhtml+xml", itemref: true, cover: false, reftype: Some(TitlePage), title: "Title" }
00:00:00 v #76 ! 15:44:15 [DEBUG] (1) epub_builder::epub: id="chapter_000.xhtml", mime="application/xhtml+xml"
00:00:00 v #77 ! 15:44:15 [DEBUG] (1) epub_builder::epub: content = Content { file: "chapter_000.xhtml", mime: "application/xhtml+xml", itemref: true, cover: false, reftype: Some(Text), title: "아" }
00:00:00 v #78 ! 15:44:15 [DEBUG] (1) epub_builder::epub: id="stylesheet.css", mime="text/css"
00:00:00 v #79 ! 15:44:15 [DEBUG] (1) epub_builder::toc: rendered elem: "<li><a href=\"title_page.xhtml\">Title</a></li>"
00:00:00 v #80 ! 15:44:15 [DEBUG] (1) epub_builder::toc: rendered elem: "<li><a href=\"chapter_000.xhtml\">아</a></li>"
00:00:00 v #81 ! 15:44:15 [INFO] crowbook::book: Succesfully generated EPUB: test.md.epub
00:00:00 v #82 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 1326 }
00:00:00 d #83 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md.epub; new_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md.epub; result = 4882 }
00:00:00 v #84 ! 15:44:15 [INFO] crowbook::book: Succesfully generated EPUB: test.hangul.md.epub
00:00:00 v #85 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 1333 }
00:00:00 d #86 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.epub; new_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md.epub; result = 4933 }
00:00:02 v #87 ! 15:44:18 [INFO] crowbook::book: Succesfully generated PDF: test.md.pdf
00:00:02 v #88 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 352 }
00:00:02 d #89 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md.pdf; new_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md.pdf; result = 4011 }
00:00:02 v #90 ! 15:44:18 [INFO] crowbook::book: Succesfully generated PDF: test.hangul.md.pdf
00:00:02 v #91 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 359 }
00:00:02 d #92 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.pdf; new_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md.pdf; result = 8033 }
__assert_eq / actual: "[Ok(("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md", [Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.html")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.pdf")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.epub")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.html")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.pdf")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.epub"))]))]" / expected: "[Ok(("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md", [Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.html")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.pdf")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md.epub")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.html")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.pdf")), Some(Ok("/home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.epub"))]))]"
00:00:02 v #93 file_system.create_dir / { dir = /home/runner/work/alphabet/polyglot/../alphabet/target/documents/dist }
00:00:02 d #94 documents.run / { source_dir = /home/runner/work/alphabet/alphabet/target/documents; dist_dir = /home/runner/work/alphabet/alphabet/target/documents/dist; cache_dir = /home/runner/work/alphabet/alphabet/target/documents/cache; hangul_spec = por-br }
00:00:02 d #95 documents.run / { files_len = 1 }
00:00:03 d #96 runtime.execute_with_options / { file_name = git; arguments = ["ls-tree", "--format='%(objectname)'", "origin/gh-pages", "/home/runner/work/alphabet/alphabet/target/documents/test.md"]; options = { command = git ls-tree --format='%(objectname)' origin/gh-pages "/home/runner/work/alphabet/alphabet/target/documents/test.md"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents",
) } }
00:00:03 v #97 ! fatal: Not a valid object name origin/gh-pages
00:00:03 v #98 runtime.execute_with_options / result / { exit_code = 128; std_trace_length = 56 }
00:00:03 d #99 runtime.execute_with_options / { file_name = git; arguments = ["hash-object", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md"]; options = { command = git hash-object "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents/dist",
) } }
00:00:03 v #100 > ba0ba7eb68b2a508ff0525bcbb91bd5ebc95e71b
00:00:03 v #101 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 40 }
00:00:03 i #102 documents.run / par_map / files' = [] / listm.iter / { output_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.epub; output_cache_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md.epub }
00:00:03 d #103 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md.epub; new_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.epub; result = 4933 }
00:00:03 i #104 documents.run / par_map / files' = [] / listm.iter / { output_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.pdf; output_cache_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md.pdf }
00:00:03 d #105 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md.pdf; new_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.pdf; result = 8033 }
00:00:03 i #106 documents.run / par_map / files' = [] / listm.iter / { output_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.html; output_cache_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md.html }
00:00:03 d #107 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md.html; new_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md.html; result = 11612 }
00:00:03 i #108 documents.run / par_map / files' = [] / listm.iter / { output_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md.epub; output_cache_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md.epub }
00:00:03 d #109 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md.epub; new_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md.epub; result = 4882 }
00:00:03 i #110 documents.run / par_map / files' = [] / listm.iter / { output_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md.pdf; output_cache_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md.pdf }
00:00:03 d #111 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md.pdf; new_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md.pdf; result = 4011 }
00:00:03 i #112 documents.run / par_map / files' = [] / listm.iter / { output_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md.html; output_cache_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md.html }
00:00:03 d #113 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.md.html; new_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.md.html; result = 11586 }
00:00:03 i #114 documents.run / par_map / files' = [] / listm.iter / { output_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md; output_cache_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md }
00:00:03 d #115 file_system.file_copy / { old_path = /home/runner/work/alphabet/alphabet/target/documents/cache/test.hangul.md; new_path = /home/runner/work/alphabet/alphabet/target/documents/dist/test.hangul.md; result = 62 }
__assert_eq / actual: "[Ok(("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md", []))]" / expected: "[Ok(("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md", []))]"
00:00:03 d #116 documents.run / { source_dir = /home/runner/work/alphabet/alphabet/target/documents; dist_dir = /home/runner/work/alphabet/alphabet/target/documents/dist; cache_dir = /home/runner/work/alphabet/alphabet/target/documents/cache; hangul_spec = por-br }
00:00:03 d #117 documents.run / { files_len = 1 }
00:00:03 d #118 runtime.execute_with_options / { file_name = git; arguments = ["ls-tree", "--format='%(objectname)'", "origin/gh-pages", "/home/runner/work/alphabet/alphabet/target/documents/test.md"]; options = { command = git ls-tree --format='%(objectname)' origin/gh-pages "/home/runner/work/alphabet/alphabet/target/documents/test.md"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents",
) } }
00:00:03 v #119 ! fatal: Not a valid object name origin/gh-pages
00:00:03 v #120 runtime.execute_with_options / result / { exit_code = 128; std_trace_length = 56 }
00:00:03 d #121 runtime.execute_with_options / { file_name = git; arguments = ["hash-object", "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md"]; options = { command = git hash-object "/home/runner/work/alphabet/alphabet/target/documents/dist/test.md"; cancellation_token = None; environment_variables = Array(MutCell([])); on_line = None; stdin = None; trace = true; working_directory = Some(
    "/home/runner/work/alphabet/alphabet/target/documents/dist",
) } }
00:00:03 v #122 > ba0ba7eb68b2a508ff0525bcbb91bd5ebc95e71b
00:00:03 v #123 runtime.execute_with_options / result / { exit_code = 0; std_trace_length = 40 }
__assert_eq / actual: "[Ok(("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md", [None, None, None, None, None, None, None]))]" / expected: "[Ok(("/home/runner/work/alphabet/alphabet/target/documents/dist/test.md", [None, None, None, None, None, None, None]))]"
__assert_eq / actual: "# 아

## 브

---

아브크
아브크

아브크

아브크
" / expected: "# 아

## 브

---

아브크
아브크

아브크

아브크
"
__assert_eq / actual: true / expected: true
__assert_eq / actual: true / expected: true
__assert_eq / actual: true / expected: true
__assert_eq / actual: true / expected: true
__assert_eq / actual: true / expected: true
__assert_eq / actual: true / expected: true

tests¶

In [ ]:
inl tests () =
    testing.run_tests {
        verify_app = get_command >> runtime.command_debug_assert
    }

main¶

In [ ]:
///! _

inl main (args : array_base string) =
    Info |> Some |> init_trace_state
    trace Info
        fun () => "documents.main"
        fun () => { args }
    inl command = get_command ()
    inl arg_matches = command |> runtime.command_get_matches
    inl source_dir =
        arg_matches
        |> runtime.matches_get_one (get_args () .source_dir |> fst)
        |> optionm'.unbox
        |> optionm.value
        |> sm'.from_std_string
    inl dist_dir =
        arg_matches
        |> runtime.matches_get_one (get_args () .dist_dir |> fst)
        |> optionm'.unbox
        |> optionm.value
        |> sm'.from_std_string
    inl cache_dir =
        arg_matches
        |> runtime.matches_get_one (get_args () .cache_dir |> fst)
        |> optionm'.unbox
        |> optionm.value
        |> sm'.from_std_string
    inl hangul_spec =
        arg_matches
        |> runtime.matches_get_one (get_args () .hangul_spec |> fst)
        |> optionm'.unbox
        |> optionm.value
        |> sm'.from_std_string
    inl result =
        run { source_dir dist_dir cache_dir hangul_spec }
        |> async.block_on_futures
        |> resultm.unbox
    match result with
    | Ok result =>
        trace Info
            fun () => "documents.main"
            fun () => { result_len = result |> am'.vec_len }
        0i32
    | Error error =>
        trace Critical
            fun () => "documents.main"
            fun () => { error }
        1i32
inl main () =
    $'let tests () = !tests ()' : ()
    $'let main args = !main args' : ()