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