Hiroyuki Katsura
2019-Aug-05 06:57 UTC
Re: [Libguestfs] [PATCH] Rust bindings: Implement Event features
I fixed based on comments. I'll send these two patches to this mailing list. - Fix Handle -> Handle<'a> - Add events Regards, Hiroyuki 2019年8月1日(木) 0:01 Pino Toscano <ptoscano@redhat.com>:> Hi Hiroyuki, > > On Tuesday, 30 July 2019 07:51:37 CEST Hiroyuki Katsura wrote: > > This patch includes: > > > > - Event callback handlers > > - Tests related to events(410-430) > > --- > > Would it be possible to split the Handle -> Handle<'a> change in an own > small patch? This way it can be documented why it was changed. > > > +pub fn event_to_string(events: &[guestfs::Event]) -> Result<String, > error::Error> { > > + let bitmask = events_to_bitmask(events); > > + > > + let r = unsafe { guestfs_event_to_string(bitmask) }; > > + if r.is_null() { > > + Err(error::unix_error("event_to_string")) > > + } else { > > + let s = unsafe { ffi::CStr::from_ptr(r) }; > > + let s = s.to_str()?.to_string(); > > These two look like utils::char_ptr_to_string(). > > > diff --git a/rust/tests/410_close_event.rs b/rust/tests/ > 410_close_event.rs > > new file mode 100644 > > index 000000000..308471098 > > --- /dev/null > > +++ b/rust/tests/410_close_event.rs > > @@ -0,0 +1,38 @@ > > +/* libguestfs Rust bindings > > + * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com > > > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License as published by > > + * the Free Software Foundation; either version 2 of the License, or > > + * (at your option) any later version. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + * > > + * You should have received a copy of the GNU General Public License > > + * along with this program; if not, write to the Free Software > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA > 02110-1301 USA. > > + */ > > + > > +extern crate guestfs; > > + > > +use std::sync::{Arc, Mutex}; > > + > > +#[test] > > +fn close_event() { > > + let close_invoked = Arc::new(Mutex::new(0)); > > Maybe a thread-safe Arc is not needed for this test -- it's just a > single-threaded test with just one handle. > > > + { > > + let mut g = guestfs::Handle::create().expect("create"); > > + g.set_event_callback( > > + |_, _, _, _| { > > + let mut data = (&close_invoked).lock().unwrap(); > > + *data += 1; > > + }, > > + &[guestfs::Event::Close], > > + ) > > + .unwrap(); > > Check that the close_invoked count is still 0 before the block ending. > > > diff --git a/rust/tests/420_log_messages.rs b/rust/tests/ > 420_log_messages.rs > > new file mode 100644 > > index 000000000..1e9627ca7 > > --- /dev/null > > +++ b/rust/tests/420_log_messages.rs > > @@ -0,0 +1,60 @@ > > +/* libguestfs Rust bindings > > + * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com > > > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License as published by > > + * the Free Software Foundation; either version 2 of the License, or > > + * (at your option) any later version. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + * > > + * You should have received a copy of the GNU General Public License > > + * along with this program; if not, write to the Free Software > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA > 02110-1301 USA. > > + */ > > + > > +extern crate guestfs; > > + > > +use std::str; > > +use std::sync::{Arc, Mutex}; > > + > > +#[test] > > +fn log_messages() { > > + let close_invoked = Arc::new(Mutex::new(0)); > > Ditto as in 410_close_event.rs. > > > diff --git a/rust/tests/430_progress_messages.rs b/rust/tests/ > 430_progress_messages.rs > > new file mode 100644 > > index 000000000..a1d33aff7 > > --- /dev/null > > +++ b/rust/tests/430_progress_messages.rs > > @@ -0,0 +1,61 @@ > > +/* libguestfs Rust bindings > > + * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com > > > > + * > > + * This program is free software; you can redistribute it and/or modify > > + * it under the terms of the GNU General Public License as published by > > + * the Free Software Foundation; either version 2 of the License, or > > + * (at your option) any later version. > > + * > > + * This program is distributed in the hope that it will be useful, > > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > > + * GNU General Public License for more details. > > + * > > + * You should have received a copy of the GNU General Public License > > + * along with this program; if not, write to the Free Software > > + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA > 02110-1301 USA. > > + */ > > + > > +extern crate guestfs; > > + > > +use std::default::Default; > > +use std::sync::{Arc, Mutex}; > > + > > +#[test] > > +fn progress_messages() { > > + let callback_invoked = Arc::new(Mutex::new(0)); > > Ditto as in 410_close_event.rs. > > > + { > > + let mut g = guestfs::Handle::create().expect("create"); > > + g.add_drive("/dev/null", Default::default()).unwrap(); > > + g.launch().unwrap(); > > + > > + let eh = g > > + .set_event_callback( > > + |_, _, _, _| { > > + let mut data = (&callback_invoked).lock().unwrap(); > > + *data += 1; > > + }, > > + &[guestfs::Event::Progress], > > + ) > > + .unwrap(); > > + assert_eq!("ok", g.debug("progress", &["5"]).unwrap()); > > + assert!(*(&callback_invoked).lock().unwrap() > 0); > > + > > + *(&callback_invoked).lock().unwrap() = 0; > > + g.delete_event_callback(eh).unwrap(); > > + assert_eq!("ok", g.debug("progress", &["5"]).unwrap()); > > + assert_eq!(*(&callback_invoked).lock().unwrap(), 0); > > + > > + g.set_event_callback( > > + |_, _, _, _| { > > + let mut data = (&callback_invoked).lock().unwrap(); > > + *data += 1; > > + }, > > + &[guestfs::Event::Progress], > > + ) > > + .unwrap(); > > + assert_eq!("ok", g.debug("progress", &["5"]).unwrap()); > > + assert!(*(&callback_invoked).lock().unwrap() > 0); > > + } > > + assert!(*(&callback_invoked).lock().unwrap() > 0); > > This assert is not needed, and most probably the whole scope here can > be removed. > > -- > Pino Toscano
Hiroyuki Katsura
2019-Aug-05 06:59 UTC
[Libguestfs] [PATCH 1/2] Rust bindings: Add Event structs, Clarify Handle lifetime
Without clarifying handle's lifetime, it is unable to see how long the callbacks which the handle owns will live. Then, Rust compiler will infer that the callbacks have 'static lifetime. It is not convenient for users. --- generator/rust.ml | 38 ++++++++++++++++++++++++++++++- rust/src/base.rs | 23 +++++++++++++------ rust/src/error.rs | 2 +- rust/src/event.rs | 4 ++++ rust/src/lib.rs | 2 ++ rust/tests/040_create_multiple.rs | 2 +- 6 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 rust/src/event.rs diff --git a/generator/rust.ml b/generator/rust.ml index a1735602c..1f5cefa62 100644 --- a/generator/rust.ml +++ b/generator/rust.ml @@ -72,6 +72,42 @@ extern \"C\" { } "; + (* event enum *) + pr "\n"; + pr "pub enum Event {\n"; + List.iter ( + fun (name, _) -> + pr " %s,\n" (snake2caml name) + ) events; + pr "}\n\n"; + + pr "impl Event {\n"; + pr " pub fn to_u64(&self) -> u64 {\n"; + pr " match self {\n"; + List.iter ( + fun (name, i) -> + pr " Event::%s => %d,\n" (snake2caml name) i + ) events; + pr " }\n"; + pr " }\n"; + pr " pub fn from_bitmask(bitmask: u64) -> Option<Event> {\n"; + pr " match bitmask {\n"; + List.iter ( + fun (name, i) -> + pr " %d => Some(Event::%s),\n" i (snake2caml name) + ) events; + pr " _ => None,\n"; + pr " }\n"; + pr " }\n"; + pr "}\n\n"; + + pr "pub const EVENT_ALL: [Event; %d] = [\n" (List.length events); + List.iter ( + fun (name, _) -> + pr " Event::%s,\n" (snake2caml name) + ) events; + pr "];\n"; + List.iter ( fun { s_camel_name = name; s_name = c_name; s_cols = cols } -> pr "\n"; @@ -356,7 +392,7 @@ extern \"C\" { pr "}\n"; - pr "impl Handle {\n"; + pr "impl<'a> Handle<'a> {\n"; List.iter ( fun ({ name = name; shortdesc = shortdesc; longdesc = longdesc; style = (ret, args, optargs) } as f) -> diff --git a/rust/src/base.rs b/rust/src/base.rs index 02ad33535..c17607cb3 100644 --- a/rust/src/base.rs +++ b/rust/src/base.rs @@ -17,6 +17,9 @@ */ use crate::error; +use crate::event; +use crate::guestfs; +use std::collections; #[allow(non_camel_case_types)] #[repr(C)] @@ -34,31 +37,37 @@ extern "C" { const GUESTFS_CREATE_NO_ENVIRONMENT: i64 = 1; const GUESTFS_CREATE_NO_CLOSE_ON_EXIT: i64 = 2; -pub struct Handle { +pub struct Handle<'a> { pub(crate) g: *mut guestfs_h, + pub(crate) callbacks: collections::HashMap< + event::EventHandle, + Box<Box<dyn Fn(guestfs::Event, event::EventHandle, &[u8], &[u64]) + 'a>>, + >, } -impl Handle { - pub fn create() -> Result<Handle, error::Error> { +impl<'a> Handle<'a> { + pub fn create() -> Result<Handle<'a>, error::Error> { let g = unsafe { guestfs_create() }; if g.is_null() { Err(error::Error::Create) } else { - Ok(Handle { g }) + let callbacks = collections::HashMap::new(); + Ok(Handle { g, callbacks }) } } - pub fn create_flags(flags: CreateFlags) -> Result<Handle, error::Error> { + pub fn create_flags(flags: CreateFlags) -> Result<Handle<'a>, error::Error> { let g = unsafe { guestfs_create_flags(flags.to_libc_int()) }; if g.is_null() { Err(error::Error::Create) } else { - Ok(Handle { g }) + let callbacks = collections::HashMap::new(); + Ok(Handle { g, callbacks }) } } } -impl Drop for Handle { +impl<'a> Drop for Handle<'a> { fn drop(&mut self) { unsafe { guestfs_close(self.g) } } diff --git a/rust/src/error.rs b/rust/src/error.rs index 705ee1735..e526121e8 100644 --- a/rust/src/error.rs +++ b/rust/src/error.rs @@ -56,7 +56,7 @@ impl convert::From<str::Utf8Error> for Error { } } -impl base::Handle { +impl<'a> base::Handle<'a> { pub(crate) fn get_error_from_handle(&self, operation: &'static str) -> Error { let c_msg = unsafe { guestfs_last_error(self.g) }; let message = unsafe { utils::char_ptr_to_string(c_msg).unwrap() }; diff --git a/rust/src/event.rs b/rust/src/event.rs new file mode 100644 index 000000000..c363e913a --- /dev/null +++ b/rust/src/event.rs @@ -0,0 +1,4 @@ +#[derive(Hash, PartialEq, Eq)] +pub struct EventHandle { + eh: i32, +} diff --git a/rust/src/lib.rs b/rust/src/lib.rs index cc41a99f8..81adef2a3 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -18,9 +18,11 @@ mod base; mod error; +mod event; mod guestfs; mod utils; pub use crate::base::*; pub use crate::error::*; +pub use crate::event::*; pub use crate::guestfs::*; diff --git a/rust/tests/040_create_multiple.rs b/rust/tests/040_create_multiple.rs index cc93554a3..970c988af 100644 --- a/rust/tests/040_create_multiple.rs +++ b/rust/tests/040_create_multiple.rs @@ -18,7 +18,7 @@ extern crate guestfs; -fn create() -> guestfs::Handle { +fn create<'a>() -> guestfs::Handle<'a> { match guestfs::Handle::create() { Ok(g) => g, Err(e) => panic!("fail: {:?}", e), -- 2.20.1 (Apple Git-117)
Hiroyuki Katsura
2019-Aug-05 06:59 UTC
[Libguestfs] [PATCH 2/2] Rust bindings: Implement callback handlers
This patch includes: - Event callback handlers - Tests related to events(410-430) src/bin/event.rs and src/bin/event_leak.rs are the PoCs that Boxes related to callbacks are not leaked. --- rust/src/bin/.gitkeep | 0 rust/src/bin/event.rs | 29 ++++++ rust/src/bin/event_leak.rs | 30 ++++++ rust/src/error.rs | 6 ++ rust/src/event.rs | 146 ++++++++++++++++++++++++++++ rust/tests/410_close_event.rs | 39 ++++++++ rust/tests/420_log_messages.rs | 60 ++++++++++++ rust/tests/430_progress_messages.rs | 59 +++++++++++ 8 files changed, 369 insertions(+) delete mode 100644 rust/src/bin/.gitkeep create mode 100644 rust/src/bin/event.rs create mode 100644 rust/src/bin/event_leak.rs create mode 100644 rust/tests/410_close_event.rs create mode 100644 rust/tests/420_log_messages.rs create mode 100644 rust/tests/430_progress_messages.rs diff --git a/rust/src/bin/.gitkeep b/rust/src/bin/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/rust/src/bin/event.rs b/rust/src/bin/event.rs new file mode 100644 index 000000000..3a3aa7d7c --- /dev/null +++ b/rust/src/bin/event.rs @@ -0,0 +1,29 @@ +extern crate guestfs; +use guestfs::*; + +fn main() { + for _ in 0..256 { + let mut g = match Handle::create() { + Ok(g) => g, + Err(e) => panic!(format!(" could not create handle {:?}", e)), + }; + g.set_event_callback( + |e, _, _, _| match e { + Event::Close => print!("c"), + _ => print!("o"), + }, + &EVENT_ALL, + ) + .unwrap(); + let eh = g + .set_event_callback(|_, _, _, _| print!("n"), &EVENT_ALL) + .unwrap(); + g.set_trace(true).unwrap(); + g.delete_event_callback(eh).unwrap(); + g.set_trace(false).unwrap(); + } + let _v = vec![0; 1024 * 1024]; + // no leak + // mem::forget(v); + println!() +} diff --git a/rust/src/bin/event_leak.rs b/rust/src/bin/event_leak.rs new file mode 100644 index 000000000..176de3c9a --- /dev/null +++ b/rust/src/bin/event_leak.rs @@ -0,0 +1,30 @@ +extern crate guestfs; +use guestfs::*; +use std::mem; + +fn main() { + for _ in 0..256 { + let mut g = match Handle::create() { + Ok(g) => g, + Err(e) => panic!(format!(" could not create handle {:?}", e)), + }; + g.set_event_callback( + |e, _, _, _| match e { + Event::Close => print!("c"), + _ => print!("o"), + }, + &EVENT_ALL, + ) + .unwrap(); + let eh = g + .set_event_callback(|_, _, _, _| print!("n"), &EVENT_ALL) + .unwrap(); + g.set_trace(true).unwrap(); + g.delete_event_callback(eh).unwrap(); + g.set_trace(false).unwrap(); + } + let v = vec![0; 1024 * 1024]; + // leak + mem::forget(v); + println!() +} diff --git a/rust/src/error.rs b/rust/src/error.rs index e526121e8..ce444e199 100644 --- a/rust/src/error.rs +++ b/rust/src/error.rs @@ -20,6 +20,7 @@ use crate::base; use crate::utils; use std::convert; use std::ffi; +use std::io; use std::os::raw::{c_char, c_int}; use std::str; @@ -41,6 +42,7 @@ pub enum Error { API(APIError), IllegalString(ffi::NulError), Utf8Error(str::Utf8Error), + UnixError(io::Error, &'static str), Create, } @@ -56,6 +58,10 @@ impl convert::From<str::Utf8Error> for Error { } } +pub(crate) fn unix_error(operation: &'static str) -> Error { + Error::UnixError(io::Error::last_os_error(), operation) +} + impl<'a> base::Handle<'a> { pub(crate) fn get_error_from_handle(&self, operation: &'static str) -> Error { let c_msg = unsafe { guestfs_last_error(self.g) }; diff --git a/rust/src/event.rs b/rust/src/event.rs index c363e913a..752e73610 100644 --- a/rust/src/event.rs +++ b/rust/src/event.rs @@ -1,4 +1,150 @@ +/* libguestfs Rust bindings + * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +use crate::base; +use crate::error; +use crate::guestfs; +use crate::utils; +use std::os::raw::{c_char, c_void}; +use std::slice; + +type GuestfsEventCallback = extern "C" fn( + *const base::guestfs_h, + *const c_void, + u64, + i32, + i32, + *const i8, + usize, + *const u64, + usize, +); + +#[link(name = "guestfs")] +extern "C" { + fn guestfs_set_event_callback( + g: *const base::guestfs_h, + cb: GuestfsEventCallback, + event_bitmask: u64, + flags: i32, + opaque: *const c_void, + ) -> i32; + fn guestfs_delete_event_callback(g: *const base::guestfs_h, eh: i32); + fn guestfs_event_to_string(bitmask: u64) -> *const c_char; + fn free(buf: *const c_void); +} + #[derive(Hash, PartialEq, Eq)] pub struct EventHandle { eh: i32, } + +fn events_to_bitmask(v: &[guestfs::Event]) -> u64 { + let mut r = 0u64; + for x in v.iter() { + r |= x.to_u64(); + } + r +} + +pub fn event_to_string(events: &[guestfs::Event]) -> Result<String, error::Error> { + let bitmask = events_to_bitmask(events); + + let r = unsafe { guestfs_event_to_string(bitmask) }; + if r.is_null() { + Err(error::unix_error("event_to_string")) + } else { + let s = unsafe { utils::char_ptr_to_string(r) }; + unsafe { free(r as *const c_void) }; + Ok(s?) + } +} + +impl<'a> base::Handle<'a> { + pub fn set_event_callback<C: 'a>( + &mut self, + callback: C, + events: &[guestfs::Event], + ) -> Result<EventHandle, error::Error> + where + C: Fn(guestfs::Event, EventHandle, &[u8], &[u64]) + 'a, + { + extern "C" fn trampoline<C>( + _g: *const base::guestfs_h, + opaque: *const c_void, + event: u64, + event_handle: i32, + _flags: i32, + buf: *const c_char, + buf_len: usize, + array: *const u64, + array_len: usize, + ) where + C: Fn(guestfs::Event, EventHandle, &[u8], &[u64]), + { + // trampoline function + // c.f. https://s3.amazonaws.com/temp.michaelfbryan.com/callbacks/index.html + + let event = match guestfs::Event::from_bitmask(event) { + Some(x) => x, + None => panic!("Failed to parse bitmask: {}", event), + }; + let eh = EventHandle { eh: event_handle }; + let buf = unsafe { slice::from_raw_parts(buf as *const u8, buf_len) }; + let array = unsafe { slice::from_raw_parts(array, array_len) }; + + let callback: &Box<dyn Fn(guestfs::Event, EventHandle, &[u8], &[u64])> + Box::leak(unsafe { Box::from_raw(opaque as *mut _) }); + callback(event, eh, buf, array) + } + + // Because trait pointer is fat pointer, in order to pass it to API, + // double Box is used. + let callback: Box<Box<dyn Fn(guestfs::Event, EventHandle, &[u8], &[u64]) + 'a>> + Box::new(Box::new(callback)); + let ptr = Box::into_raw(callback); + let callback = unsafe { Box::from_raw(ptr) }; + let event_bitmask = events_to_bitmask(events); + + let eh = { + unsafe { + guestfs_set_event_callback( + self.g, + trampoline::<C>, + event_bitmask, + 0, + ptr as *const c_void, + ) + } + }; + if eh == -1 { + return Err(self.get_error_from_handle("set_event_callback")); + } + self.callbacks.insert(EventHandle { eh }, callback); + + Ok(EventHandle { eh }) + } + + pub fn delete_event_callback(&mut self, eh: EventHandle) -> Result<(), error::Error> { + unsafe { + guestfs_delete_event_callback(self.g, eh.eh); + } + self.callbacks.remove(&eh); + Ok(()) + } +} diff --git a/rust/tests/410_close_event.rs b/rust/tests/410_close_event.rs new file mode 100644 index 000000000..e22474d36 --- /dev/null +++ b/rust/tests/410_close_event.rs @@ -0,0 +1,39 @@ +/* libguestfs Rust bindings + * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +extern crate guestfs; + +use std::cell::RefCell; +use std::rc::Rc; + +#[test] +fn close_event() { + let close_invoked = Rc::new(RefCell::new(0)); + { + let mut g = guestfs::Handle::create().expect("create"); + g.set_event_callback( + |_, _, _, _| { + *close_invoked.borrow_mut() += 1; + }, + &[guestfs::Event::Close], + ) + .unwrap(); + assert_eq!(*close_invoked.borrow(), 0); + } + assert_eq!(*close_invoked.borrow(), 1); +} diff --git a/rust/tests/420_log_messages.rs b/rust/tests/420_log_messages.rs new file mode 100644 index 000000000..78365995a --- /dev/null +++ b/rust/tests/420_log_messages.rs @@ -0,0 +1,60 @@ +/* libguestfs Rust bindings + * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +extern crate guestfs; + +use std::cell::RefCell; +use std::rc::Rc; +use std::str; + +#[test] +fn log_messages() { + let callback_invoked = Rc::new(RefCell::new(0)); + { + let mut g = guestfs::Handle::create().expect("create"); + g.set_event_callback( + |ev, _, buf, array| { + *callback_invoked.borrow_mut() += 1; + + let event = guestfs::event_to_string(&[ev]).unwrap(); + + let buf = str::from_utf8(buf).unwrap(); + let array = array + .into_iter() + .map(|x| format!("{}", x)) + .collect::<Vec<String>>() + .join(","); + + eprintln!("event logged: event={} buf={} array={}", event, buf, array) + }, + &[ + guestfs::Event::Appliance, + guestfs::Event::Library, + guestfs::Event::Warning, + guestfs::Event::Trace, + ], + ) + .unwrap(); + + g.set_trace(true).unwrap(); + g.set_verbose(true).unwrap(); + g.add_drive_ro("/dev/null").unwrap(); + g.set_autosync(true).unwrap(); + } + assert!(*callback_invoked.borrow() > 0); +} diff --git a/rust/tests/430_progress_messages.rs b/rust/tests/430_progress_messages.rs new file mode 100644 index 000000000..08d8b59e7 --- /dev/null +++ b/rust/tests/430_progress_messages.rs @@ -0,0 +1,59 @@ +/* libguestfs Rust bindings + * Copyright (C) 2019 Hiroyuki Katsura <hiroyuki.katsura.0513@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +extern crate guestfs; + +use std::cell::RefCell; +use std::default::Default; +use std::rc::Rc; + +#[test] +fn progress_messages() { + let callback_invoked = Rc::new(RefCell::new(0)); + { + let mut g = guestfs::Handle::create().expect("create"); + g.add_drive("/dev/null", Default::default()).unwrap(); + g.launch().unwrap(); + + let eh = g + .set_event_callback( + |_, _, _, _| { + *callback_invoked.borrow_mut() += 1; + }, + &[guestfs::Event::Progress], + ) + .unwrap(); + assert_eq!("ok", g.debug("progress", &["5"]).unwrap()); + assert!(*callback_invoked.borrow() > 0); + + *callback_invoked.borrow_mut() = 0; + g.delete_event_callback(eh).unwrap(); + assert_eq!("ok", g.debug("progress", &["5"]).unwrap()); + assert_eq!(*callback_invoked.borrow(), 0); + + g.set_event_callback( + |_, _, _, _| { + *callback_invoked.borrow_mut() += 1; + }, + &[guestfs::Event::Progress], + ) + .unwrap(); + assert_eq!("ok", g.debug("progress", &["5"]).unwrap()); + } + assert!(*callback_invoked.borrow() > 0); +} -- 2.20.1 (Apple Git-117)
Pino Toscano
2019-Aug-05 12:01 UTC
Re: [Libguestfs] [PATCH 1/2] Rust bindings: Add Event structs, Clarify Handle lifetime
On Monday, 5 August 2019 08:59:31 CEST Hiroyuki Katsura wrote:> Without clarifying handle's lifetime, it is unable > to see how long the callbacks which the handle > owns will live. Then, Rust compiler will infer > that the callbacks have 'static lifetime. It is > not convenient for users. > ---My initial idea was to split the Handle -> Handle<'a> change in an own patch, to make it easier to review; unfortunately, doing this hits error E0392: https://doc.rust-lang.org/error-index.html#E0392 So either this change goes together with other changes (like in this patch), or it uses PhantomData just to remove it after adding the callbacks. :-/ I guess we can keep this patch as it is; please describe a bit more the addition of events, mentioning the Handle<'a> change is needed because <insert here what you already wrote as commit message>.> diff --git a/rust/src/base.rs b/rust/src/base.rs > index 02ad33535..c17607cb3 100644 > --- a/rust/src/base.rs > +++ b/rust/src/base.rs > @@ -17,6 +17,9 @@ > */ > > use crate::error; > +use crate::event; > +use crate::guestfs;guestfs.rs has: use crate::base::*; So it means rust allows cyclic uses of modules/crates?> diff --git a/rust/src/event.rs b/rust/src/event.rs > new file mode 100644 > index 000000000..c363e913a > --- /dev/null > +++ b/rust/src/event.rs > @@ -0,0 +1,4 @@ > +#[derive(Hash, PartialEq, Eq)] > +pub struct EventHandle { > + eh: i32, > +}Remember the copyright/license in new files. -- Pino Toscano