For more faithful translation of API documentation, write a custom translator from POD to rustdoc. --- generator/Rust.ml | 99 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 80 insertions(+), 19 deletions(-)
diff --git a/generator/Rust.ml b/generator/Rust.ml index 302bd4c48b..fd703f0493 100644 --- a/generator/Rust.ml +++ b/generator/Rust.ml @@ -476,27 +476,88 @@ let pr "\n" (* Print the comment for a rust function for a handle call. *) -let print_rust_handle_call_comment call = +let rec print_rust_handle_call_comment name call = (* Print comments. *) if call.shortdesc <> "" then pr "/// %s\n" (String.concat "\n/// " (String.split_on_char '\n' call.shortdesc)); if call.longdesc <> "" then ( - (* If a short comment was printed, print a blank comment line befor the - long description. *) + (* If a short comment was printed, print a blank comment line before + the long description. *) if call.shortdesc <> "" then pr "/// \n"; - (* Print all lines of the long description. Since Rust comments are - supposed to be Markdown, all indented lines will be treated as code - blocks. Hence we trim all lines. Also brackets ("[" and "]") must be - escaped. *) - List.iter - (fun line -> - let unindented = String.trim line in - let escaped = - Str.global_replace (Str.regexp {|\(\[\|\]\)|}) {|\\\1|} unindented - in - pr "/// %s\n" escaped) - (pod2text call.longdesc)) + let md = longdesc_to_markdown name call.longdesc in + List.iter (pr "/// %s\n") md + ) + +(* Convert POD to rustdoc markdown. *) +and longdesc_to_markdown name longdesc = + (* Replace any POD <> expression *) + let content = + Str.global_substitute (Str.regexp {|[A-Z]<[^>]+?>|}) + (fun s -> + let expr = Str.matched_string s in + let len = String.length expr in + let c = expr.[0] and content = String.sub expr 2 (len-3) in + match c with + | 'C' -> sprintf "`%s`" content (* C<...> becomes `...` *) + | 'B' -> sprintf "<b>%s</b>" content + | 'I' | 'F' -> sprintf "<i>%s</i>" content + | 'E' -> sprintf "&%s;" content + | 'L' -> + let len = String.length content in + if String.starts_with ~prefix:"nbd_" content then ( + let n = String.sub content 4 (len - 7) in + if n <> "get_error" && n <> "get_errno" && n <> "close" then + sprintf "[%s](Handle::%s)" n n + else + sprintf "`%s`" n + ) + else (* external manual page - how to link XXX *) + sprintf "<i>%s</i>" content + | _ -> + failwithf "rust: API documentation for %s contains '%s' which + cannot be converted to Rust markdown" name expr + ) + longdesc in + + (* Split input into lines for rest of the processing. *) + let lines = nsplit "\n" content in + + (* Surround any group of lines starting with whitespace with ```text *) + let lines = + List.map (fun line -> String.starts_with ~prefix:" " line, line) lines in + let (lines : (bool * string list) list) = group_by lines in + let lines = + List.map (function + | true (* verbatim *), lines -> [ "```text" ] @ lines @ [ "```" ] + | false, lines -> lines + ) lines in + let lines = List.flatten lines in + + (* Replace any = directives *) + filter_map ( + fun s -> + (* This is a very approximate way to translate bullet lists. *) + if String.starts_with ~prefix:"=over" s || + String.starts_with ~prefix:"=back" s then + None + else if String.starts_with ~prefix:"=item" s then ( + let len = String.length s in + let s' = String.sub s 5 (len-5) in + Some ("-" ^ s') + ) + else if String.starts_with ~prefix:"=head" s then ( + let i = int_of_string (String.make 1 s.[5]) in + let len = String.length s in + let s' = String.sub s 6 (len-6) in + Some (String.make i '#' ^ s') + ) + else if String.starts_with ~prefix:"=" s then + failwithf "rust: API documentation for %s contains '%s' which + cannot be converted to Rust markdown" name s + else + Some s + ) lines (* Print a Rust expression which converts Rust like arguments to FFI like arguments, makes a call on the raw FFI handle, and converts the return @@ -533,7 +594,7 @@ let String.concat ", " (List.map2 (sprintf "%s: %s") rust_args_names rust_args_types) in - print_rust_handle_call_comment call; + print_rust_handle_call_comment name call; (* Print visibility modifier. *) if NameSet.mem name hidden_handle_calls then ( (* If this is hidden to the public API, it might be used only if some feature @@ -653,7 +714,7 @@ let (* Print the Rust function for a synchronous handle call. *) let print_rust_sync_handle_call name call = - print_rust_handle_call_comment call; + print_rust_handle_call_comment name call; pr "pub fn %s(&self, %s) -> %s\n" name (rust_async_handle_call_args call) (rust_ret_type call); @@ -698,7 +759,7 @@ let let optargs_without_completion_cb = optargs_before_completion_cb @ optargs_after_completion_cb in - print_rust_handle_call_comment call; + print_rust_handle_call_comment name call; pr "pub async fn %s(&self, %s) -> SharedResult<()> {\n" name (rust_async_handle_call_args { call with optargs = optargs_without_completion_cb }); @@ -747,7 +808,7 @@ let let print_rust_async_handle_call_changing_state name aio_name call (predicate, value) = let value = if value then "true" else "false" in - print_rust_handle_call_comment call; + print_rust_handle_call_comment name call; pr "pub async fn %s(&self, %s) -> SharedResult<()>\n" name (rust_async_handle_call_args call); pr "{\n"; -- 2.41.0 _______________________________________________ Libguestfs mailing list Libguestfs@redhat.com https://listman.redhat.com/mailman/listinfo/libguestfs