[Libguestfs] [PATCH libnbd 4/4] rust: Build the examples

2023-10-10 Thread Richard W.M. Jones
It's worth checking that the examples actually build, so we don't get
any regressions.
---
 rust/Makefile.am | 7 ++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/rust/Makefile.am b/rust/Makefile.am
index 5c73512c87..a7700d69bd 100644
--- a/rust/Makefile.am
+++ b/rust/Makefile.am
@@ -90,7 +90,8 @@ EXTRA_DIST = \
 if HAVE_RUST
 
 all-local: libnbd-sys/libnbd_version target/debug/liblibnbd.rlib \
-   target/doc/libnbd/index.html
+   target/doc/libnbd/index.html \
+   target/debug/examples/get-size
 
 libnbd-sys/libnbd_version: Makefile
rm -f libnbd-sys/libnbd_version.t
@@ -103,6 +104,10 @@ target/debug/liblibnbd.rlib: $(source_files)
 target/doc/libnbd/index.html: $(source_files)
$(abs_top_builddir)/run $(CARGO) doc
 
+# This will actually build all the examples:
+target/debug/examples/get-size: $(source_files)
+   $(abs_top_builddir)/run $(CARGO) build --examples
+
 if HAVE_POD
 
 man_MANS = libnbd-rust.3
-- 
2.41.0

___
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs



[Libguestfs] [PATCH libnbd 3/4] rust: Write a custom translator from POD to rustdoc

2023-10-10 Thread Richard W.M. Jones
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 "%s" content
+| 'I' | 'F' -> sprintf "%s" 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 "%s" 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;
+  

[Libguestfs] [PATCH libnbd 2/4] rust: Add overview documentation

2023-10-10 Thread Richard W.M. Jones
---
 .gitignore   |  1 +
 docs/libnbd.pod  |  4 
 rust/Makefile.am | 13 +++
 rust/libnbd-rust.pod | 53 
 4 files changed, 71 insertions(+)

diff --git a/.gitignore b/.gitignore
index 36bf8b60f8..0b1cf7646a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -180,6 +180,7 @@ Makefile.in
 /python/run-python-tests
 /run
 /rust/Cargo.lock
+/rust/libnbd-rust.3
 /rust/libnbd-sys/Cargo.lock
 /rust/libnbd-sys/libnbd_version
 /rust/libnbd-sys/src/generated.rs
diff --git a/docs/libnbd.pod b/docs/libnbd.pod
index f15ea6403d..2fc78212a5 100644
--- a/docs/libnbd.pod
+++ b/docs/libnbd.pod
@@ -51,6 +51,10 @@ Using the API from OCaml.
 
 Using the API from Go.
 
+=item L
+
+Using the API from Rust.
+
 =item L
 
 Using the NBD shell (nbdsh) for command line and Python scripting.
diff --git a/rust/Makefile.am b/rust/Makefile.am
index 75738a0c30..5c73512c87 100644
--- a/rust/Makefile.am
+++ b/rust/Makefile.am
@@ -84,6 +84,7 @@ source_files = \
 
 EXTRA_DIST = \
$(source_files) \
+   libnbd-rust.pod \
$(NULL)
 
 if HAVE_RUST
@@ -102,6 +103,18 @@ target/debug/liblibnbd.rlib: $(source_files)
 target/doc/libnbd/index.html: $(source_files)
$(abs_top_builddir)/run $(CARGO) doc
 
+if HAVE_POD
+
+man_MANS = libnbd-rust.3
+CLEANFILES += $(man_MANS)
+
+libnbd-rust.3: libnbd-rust.pod $(top_builddir)/podwrapper.pl
+   $(PODWRAPPER) --section=3 --man $@ \
+   --html $(top_builddir)/html/$@.html \
+   $<
+
+endif HAVE_POD
+
 TESTS_ENVIRONMENT = \
LIBNBD_DEBUG=1 \
$(MALLOC_CHECKS) \
diff --git a/rust/libnbd-rust.pod b/rust/libnbd-rust.pod
new file mode 100644
index 00..622f0b4ac5
--- /dev/null
+++ b/rust/libnbd-rust.pod
@@ -0,0 +1,53 @@
+=head1 NAME
+
+libnbd-rust - how to use libnbd from Rust
+
+=head1 SYNOPSIS
+
+ let nbd = libnbd::Handle::new().unwrap();
+ nbd.connect_uri("nbd://localhost").unwrap();
+ let size = nbd.get_size().unwrap();
+ println!("{size} bytes");
+
+In C add:
+
+ [dependencies]
+ libnbd = VERSION | { path = "libnbd/rust" }
+
+=head1 DESCRIPTION
+
+This manual page documents how to use libnbd to access Network Block
+Device (NBD) servers from the Rust programming language.
+
+The Rust bindings work very similarly to the C bindings so you should
+start by reading L.
+
+There is also a higher level asynchronous API using Tokio.
+
+If you build libnbd from source, the main documentation can be found
+in F
+
+For the ordinary interface, start by reading the documentation for
+C.  For the higher level asynchronous API, start by reading
+C.
+
+C is a very low level wrapper around the libnbd API which
+should not be used directly.
+
+=head1 EXAMPLES
+
+This directory contains examples written in Rust:
+
+L
+
+=head1 SEE ALSO
+
+L.
+
+=head1 AUTHORS
+
+Tage Johansson
+
+=head1 COPYRIGHT
+
+Copyright Tage Johansson
-- 
2.41.0

___
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs



[Libguestfs] [PATCH libnbd 1/4] rust: Annotate 'endif' with corresponding label

2023-10-10 Thread Richard W.M. Jones
Although not required, it makes automake 'Makefile.am's easier to read.
---
 rust/Makefile.am | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/rust/Makefile.am b/rust/Makefile.am
index 027097af9b..75738a0c30 100644
--- a/rust/Makefile.am
+++ b/rust/Makefile.am
@@ -117,6 +117,6 @@ clean-local:
$(CARGO) clean
$(CARGO) clean --manifest-path cargo_test/Cargo.toml
 
-endif
+endif HAVE_RUST
 
 CLEANFILES += libnbd-sys/libnbd_version
-- 
2.41.0

___
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs



[Libguestfs] [PATCH libnbd 0/4] Miscellaneous Rust cleanups

2023-10-10 Thread Richard W.M. Jones
Add an overview libnbd-rust(3) man page pointing to the real
documentation.  This is like OCaml & Golang.

When reviewing the real rustdocs I noticed they basically converted
the man pages into plain text, resulting in lots of problems such as
internal links not working, no `code` annotations, etc.  So I wrote a
simple POD to rustdoc translator.  It is by no means perfect, but it
fixes many of the issues.

Also build the examples, to make sure we don't get any regressions.

Rich.


___
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs



Re: [Libguestfs] [nbdkit] Two POSIX questions for you ...

2023-10-10 Thread Richard W.M. Jones
On Tue, Oct 10, 2023 at 09:05:43AM +0200, Laszlo Ersek wrote:
> On 10/9/23 16:51, Richard W.M. Jones wrote:
> > 
> > So one thing we could do for this test is to require (for the test)
> > that /bin/sh is bash, something like the patch below.  I was only able
> > to test this in the positive case.
> > 
> > diff --git a/tests/test-log-script-info.sh b/tests/test-log-script-info.sh
> > index fa9b2ed32..d65f6415d 100755
> > --- a/tests/test-log-script-info.sh
> > +++ b/tests/test-log-script-info.sh
> > @@ -38,6 +38,10 @@ requires_run
> >  requires_nbdinfo
> >  requires_filter log
> >  
> > +# This test implicitly assumes /bin/sh is bash, see:
> > +# https://listman.redhat.com/archives/libguestfs/2023-October/032767.html
> > +BASH_VERSION=no requires /bin/sh -c 'test "x$BASH_VERSION" != "xno"'
> > +
> >  if ! nbdinfo --help | grep -- --map ; then
> >  echo "$0: nbdinfo --map option required to run this test"
> >  exit 77
> > 
> > 
> 
> Clever!
> 
> Reviewed-by: Laszlo Ersek 
> 
> You can test it with "dash" BTW (it must be available in Fedora, because
> it is available in EPEL-9):
> 
> BASH_VERSION=no /bin/bash -c 'test "x$BASH_VERSION" != "xno"; echo $?'
> BASH_VERSION=no /bin/dash -c 'test "x$BASH_VERSION" != "xno"; echo $?'

Thanks - after testing with dash as you suggested, I pushed this
change which is similar in spirit:

https://gitlab.com/nbdkit/nbdkit/-/commit/99712c09835ae430912aed4848579fdfab01a576

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-builder quickly builds VMs from scratch
http://libguestfs.org/virt-builder.1.html
___
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs



Re: [Libguestfs] [nbdkit] Two POSIX questions for you ...

2023-10-10 Thread Laszlo Ersek
On 10/9/23 16:51, Richard W.M. Jones wrote:
> 
> So one thing we could do for this test is to require (for the test)
> that /bin/sh is bash, something like the patch below.  I was only able
> to test this in the positive case.
> 
> diff --git a/tests/test-log-script-info.sh b/tests/test-log-script-info.sh
> index fa9b2ed32..d65f6415d 100755
> --- a/tests/test-log-script-info.sh
> +++ b/tests/test-log-script-info.sh
> @@ -38,6 +38,10 @@ requires_run
>  requires_nbdinfo
>  requires_filter log
>  
> +# This test implicitly assumes /bin/sh is bash, see:
> +# https://listman.redhat.com/archives/libguestfs/2023-October/032767.html
> +BASH_VERSION=no requires /bin/sh -c 'test "x$BASH_VERSION" != "xno"'
> +
>  if ! nbdinfo --help | grep -- --map ; then
>  echo "$0: nbdinfo --map option required to run this test"
>  exit 77
> 
> 

Clever!

Reviewed-by: Laszlo Ersek 

You can test it with "dash" BTW (it must be available in Fedora, because
it is available in EPEL-9):

BASH_VERSION=no /bin/bash -c 'test "x$BASH_VERSION" != "xno"; echo $?'
BASH_VERSION=no /bin/dash -c 'test "x$BASH_VERSION" != "xno"; echo $?'

Laszlo
___
Libguestfs mailing list
Libguestfs@redhat.com
https://listman.redhat.com/mailman/listinfo/libguestfs