diff --git a/justfile b/justfile index 6021fae..4274873 100644 --- a/justfile +++ b/justfile @@ -1,12 +1,19 @@ workspace := "~/libnv-rs" ubuntu_host := "zetta-ubuntu" +freebsd_host := "zetta-freebsd13" rsync_exclude := "--exclude .git --exclude .idea --exclude target --exclude libzfs_core-sys/target" set positional-arguments + test-ubuntu args='': just copy-code-to {{ubuntu_host}} ssh {{ubuntu_host}} '. "$HOME/.cargo/env";cd {{workspace}} && cargo test --no-default-features --features nvpair {{args}}' + +test-freebsd args='': + just copy-code-to {{freebsd_host}} + ssh {{freebsd_host}} '. "$HOME/.cargo/env";cd {{workspace}} && cargo test --no-default-features --features libnv {{args}}' + copy-code-to host: rsync -az -e "ssh" {{rsync_exclude}} --progress ./ {{host}}:{{workspace}} diff --git a/libnv-sys/build.rs b/libnv-sys/build.rs index 2a4bafd..a7411c0 100644 --- a/libnv-sys/build.rs +++ b/libnv-sys/build.rs @@ -2,14 +2,8 @@ extern crate regex; #[cfg(target_os = "freebsd")] fn main() { - use regex::Regex; - use std::{ - env, - fs::File, - io::Write, - path::PathBuf, - }; + use std::{env, fs::File, io::Write, path::PathBuf}; println!("cargo:rerun-if-env-changed=LLVM_CONFIG_PATH"); println!("cargo:rustc-link-lib=nv"); @@ -44,17 +38,16 @@ fn main() { let new_fragment = match cap.get(1).unwrap().as_str() { "fn" => { let funcname = cap.get(2).unwrap().as_str(); - fixed_bindings.push_str( - &format!("#[link_name = \"FreeBSD_{}\"]\n", funcname)); + fixed_bindings.push_str(&format!("#[link_name = \"FreeBSD_{}\"]\n", funcname)); format!("pub fn {}", funcname) }, "type" => { let typename = cap.get(2).unwrap().as_str(); - fixed_bindings.push_str(&format!("pub type FreeBSD_{} = {};\n", - typename, typename)); + fixed_bindings + .push_str(&format!("pub type FreeBSD_{} = {};\n", typename, typename)); format!("pub type {}", typename) - } - _ => unreachable!() + }, + _ => unreachable!(), }; fixed_bindings.push_str(&new_fragment); prev_index = index + new_fragment.len() + "FreeBSD_".len(); diff --git a/libnv-sys/src/lib.rs b/libnv-sys/src/lib.rs index b4d8a63..a3bc39f 100644 --- a/libnv-sys/src/lib.rs +++ b/libnv-sys/src/lib.rs @@ -4,9 +4,8 @@ //! These are raw, `unsafe` FFI bindings. Here be dragons! You probably //! shouldn't use this crate directly. Instead, you should use the //! [`libnv`](https://crates.io/crates/libnv) crate. -#![cfg_attr(crossdocs, doc="")] -#![cfg_attr(crossdocs, doc="These docs are just stubs! Don't trust them.")] - +#![cfg_attr(crossdocs, doc = "")] +#![cfg_attr(crossdocs, doc = "These docs are just stubs! Don't trust them.")] // bindgen generates some unconventional type names #![allow(non_camel_case_types)] diff --git a/src/lib.rs b/src/lib.rs index b6aa9ac..9620013 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,17 +9,23 @@ //! [nvpair]: https://github.com/zfsonlinux/zfs/tree/master/module/nvpair extern crate libc; -#[macro_use] extern crate quick_error; +#[macro_use] +extern crate quick_error; -#[cfg(feature = "libnv")] extern crate libnv_sys; +#[cfg(feature = "libnv")] +extern crate libnv_sys; #[cfg(feature = "nvpair")] extern crate nvpair_sys; -#[cfg(feature = "libnv")] pub mod libnv; +#[cfg(feature = "libnv")] +pub mod libnv; -#[cfg(feature = "nvpair")] pub mod nvpair; +#[cfg(feature = "nvpair")] +pub mod nvpair; +use std::borrow::Cow; +use std::ffi::{CStr, CString}; use std::{ffi::NulError, io}; quick_error! { #[derive(Debug)] @@ -61,3 +67,33 @@ impl NvError { /// Short-cut to Result. pub type NvResult = Result; + +/// Trait to keep public interface friendly (i.e. support rust types like `&str`) and at the same time +/// allow using lower level types like `CString` & `CStr`. +pub trait IntoCStr<'a> { + fn into_c_str(self) -> NvResult>; +} + +impl<'a> IntoCStr<'a> for &'a CStr { + fn into_c_str(self) -> NvResult> { + Ok(Cow::from(self)) + } +} + +impl<'a> IntoCStr<'a> for CString { + fn into_c_str(self) -> NvResult> { + Ok(Cow::from(self)) + } +} + +impl<'a> IntoCStr<'a> for &str { + fn into_c_str(self) -> NvResult> { + CString::new(self).map(Cow::from).map_err(NvError::from) + } +} + +impl<'a> IntoCStr<'a> for String { + fn into_c_str(self) -> NvResult> { + CString::new(self).map(Cow::from).map_err(NvError::from) + } +} diff --git a/src/libnv.rs b/src/libnv.rs index f4d7ccb..9364279 100644 --- a/src/libnv.rs +++ b/src/libnv.rs @@ -22,40 +22,43 @@ use libc::ENOMEM; // Importing all because it's cold, I dont want to turn on heater and it's hard // to type. use libnv_sys::*; -use std::{convert::{From, Into}, - ffi::{CStr, CString}, - os::raw::c_void, - os::unix::io::AsRawFd, - slice}; +use std::{ + convert::{From, Into}, + ffi::CStr, + os::raw::c_void, + os::unix::io::AsRawFd, + slice, +}; +use IntoCStr; use crate::{NvError, NvResult}; /// Enumeration of available data types that the API supports. pub enum NvType { /// Empty type - None = 0, + None = 0, /// There is no associated data with the name - Null = 1, + Null = 1, /// The value is a `bool` value - Bool = 2, + Bool = 2, /// The value is a `u64` value - Number = 3, + Number = 3, /// The value is a C string - String = 4, + String = 4, /// The value is another `nvlist` - NvList = 5, + NvList = 5, /// The value is a file descriptor - Descriptor = 6, + Descriptor = 6, /// The value is a binary buffer - Binary = 7, + Binary = 7, /// The value is an array of `bool` values - BoolArray = 8, + BoolArray = 8, /// The value is an array of `u64` values - NumberArray = 9, + NumberArray = 9, /// The value is an array of C strings - StringArray = 10, + StringArray = 10, /// The value is an array of other `nvlist`'s - NvListArray = 11, + NvListArray = 11, /// The value is an array of file descriptors DescriptorArray = 12, } @@ -65,13 +68,13 @@ pub enum NvType { #[derive(Copy, Clone, Debug, PartialEq)] pub enum NvFlag { /// No user specified options. - None = 0, + None = 0, /// Perform case-insensitive lookups of provided names. IgnoreCase = 1, /// Names in the nvlist do not have to be unique. - NoUnique = 2, + NoUnique = 2, /// Allow duplicate case-insensitive keys. - Both = 3, + Both = 3, } impl From for NvFlag { @@ -92,7 +95,7 @@ macro_rules! impl_list_op { ($type_:ty, $method:ident, false) => { impl NvTypeOp for $type_ { /// Add a `$type_` value to the `NvList` - fn add_to_list(&self, list: &mut NvList, name: &str) -> NvResult<()> { + fn add_to_list<'a, N: IntoCStr<'a>>(&self, list: &mut NvList, name: N) -> NvResult<()> { return list.$method(name, *self); } } @@ -100,7 +103,7 @@ macro_rules! impl_list_op { ($type_:ty, $method:ident, true) => { impl NvTypeOp for $type_ { /// Add a `$type_` value to the `NvList` - fn add_to_list(&self, list: &mut NvList, name: &str) -> NvResult<()> { + fn add_to_list<'a, N: IntoCStr<'a>>(&self, list: &mut NvList, name: N) -> NvResult<()> { return list.$method(name, &*self); } } @@ -111,7 +114,7 @@ macro_rules! impl_list_op { /// own types if you don't want to convert to primitive types every time. pub trait NvTypeOp { /// Add self to given list. - fn add_to_list(&self, list: &mut NvList, name: &str) -> NvResult<()>; + fn add_to_list<'a, N: IntoCStr<'a>>(&self, list: &mut NvList, name: N) -> NvResult<()>; } impl_list_op! {bool, insert_bool, false} @@ -129,7 +132,7 @@ impl NvTypeOp for Option where T: NvTypeOp, { - fn add_to_list(&self, list: &mut NvList, name: &str) -> NvResult<()> { + fn add_to_list<'a, N: IntoCStr<'a>>(&self, list: &mut NvList, name: N) -> NvResult<()> { match self { Some(ref val) => val.add_to_list(list, name), None => list.insert_null(name), @@ -146,11 +149,15 @@ pub struct NvList { #[doc(hidden)] /// Return new list with no flags. impl Default for NvList { - fn default() -> NvList { NvList::new(NvFlag::None).expect("Failed to create new list") } + fn default() -> NvList { + NvList::new(NvFlag::None).expect("Failed to create new list") + } } impl NvList { /// Make a copy of a pointer. Danger zone. - pub fn as_ptr(&self) -> *mut nvlist_t { self.ptr } + pub fn as_ptr(&self) -> *mut nvlist_t { + self.ptr + } fn check_if_error(&self) -> NvResult<()> { match self.error() { @@ -186,7 +193,7 @@ impl NvList { // Note: this cannot be `impl From<*mut nvlist_t> for Self` because that // trait is only for safe conversions. pub unsafe fn from_ptr(ptr: *mut nvlist_t) -> Self { - Self{ptr} + Self { ptr } } /// Determines if the `nvlist` is empty. @@ -196,7 +203,9 @@ impl NvList { /// let nvlist = NvList::new(NvFlag::IgnoreCase).unwrap(); /// assert!(nvlist.is_empty()); /// ``` - pub fn is_empty(&self) -> bool { unsafe { nvlist_empty(self.ptr) } } + pub fn is_empty(&self) -> bool { + unsafe { nvlist_empty(self.ptr) } + } /// The flags the `nvlist` was created with. /// @@ -206,7 +215,9 @@ impl NvList { /// /// assert_eq!(nvlist.flags(), NvFlag::NoUnique); /// ``` - pub fn flags(&self) -> NvFlag { NvFlag::from(unsafe { nvlist_flags(self.ptr) }) } + pub fn flags(&self) -> NvFlag { + NvFlag::from(unsafe { nvlist_flags(self.ptr) }) + } /// Gets error value that the list may have accumulated. /// @@ -216,7 +227,9 @@ impl NvList { /// /// assert_eq!(0, list.error()); /// ``` - pub fn error(&self) -> i32 { unsafe { nvlist_error(self.ptr) } } + pub fn error(&self) -> i32 { + unsafe { nvlist_error(self.ptr) } + } /// Sets the `NvList` to be in an error state. /// @@ -256,7 +269,7 @@ impl NvList { /// /// assert_eq!(list.get_number("Important year").unwrap().unwrap(), 1776); /// ``` - pub fn insert(&mut self, name: &str, value: T) -> NvResult<()> { + pub fn insert<'a, N: IntoCStr<'a>, T: NvTypeOp>(&mut self, name: N, value: T) -> NvResult<()> { value.add_to_list(self, name) } @@ -267,8 +280,8 @@ impl NvList { /// let mut list = NvList::new(NvFlag::Both).unwrap(); /// list.insert_null("Hello, World!"); /// ``` - pub fn insert_null(&mut self, name: &str) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_null<'a, N: IntoCStr<'a>>(&mut self, name: N) -> NvResult<()> { + let c_name = name.into_c_str()?; unsafe { nvlist_add_null(self.ptr, c_name.as_ptr()); } @@ -284,8 +297,12 @@ impl NvList { /// /// list.insert_number("Important year", 1776u64); /// ``` - pub fn insert_number>(&mut self, name: &str, value: I) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_number<'a, N: IntoCStr<'a>, I: Into>( + &mut self, + name: N, + value: I, + ) -> NvResult<()> { + let c_name = name.into_c_str()?; unsafe { nvlist_add_number(self.ptr, c_name.as_ptr(), value.into()); } @@ -293,8 +310,8 @@ impl NvList { } /// Add a `bool` to the list. - pub fn insert_bool(&mut self, name: &str, value: bool) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_bool<'a, N: IntoCStr<'a>>(&mut self, name: N, value: bool) -> NvResult<()> { + let c_name = name.into_c_str()?; unsafe { nvlist_add_bool(self.ptr, c_name.as_ptr(), value); } @@ -302,9 +319,13 @@ impl NvList { } /// Add string to the list. - pub fn insert_string(&mut self, name: &str, value: &str) -> NvResult<()> { - let c_name = CString::new(name)?; - let c_value = CString::new(value)?; + pub fn insert_string<'a, 'b, N: IntoCStr<'a>, V: IntoCStr<'b>>( + &mut self, + name: N, + value: V, + ) -> NvResult<()> { + let c_name = name.into_c_str()?; + let c_value = value.into_c_str()?; unsafe { nvlist_add_string(self.ptr, c_name.as_ptr(), c_value.as_ptr()); } @@ -322,8 +343,8 @@ impl NvList { /// /// list.insert_nvlist("other list", &other_list).unwrap(); /// ``` - pub fn insert_nvlist(&mut self, name: &str, value: &NvList) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_nvlist<'a, N: IntoCStr<'a>>(&mut self, name: N, value: &NvList) -> NvResult<()> { + let c_name = name.into_c_str()?; if !value.as_ptr().is_null() { unsafe { nvlist_add_nvlist(self.ptr, c_name.as_ptr(), value.as_ptr()); @@ -334,17 +355,27 @@ impl NvList { /// Add binary data to the list. #[deprecated(since = "0.4.0", note = "use insert_binary instead")] - pub unsafe fn add_binary(&mut self, name: &str, value: *const i8, size: usize) -> NvResult<()> { - let c_name = CString::new(name)?; + pub unsafe fn add_binary<'a, N: IntoCStr<'a>>( + &mut self, + name: N, + value: *const i8, + size: usize, + ) -> NvResult<()> { + let c_name = name.into_c_str()?; nvlist_add_binary(self.ptr, c_name.as_ptr(), value as *const c_void, size); self.check_if_error() } /// Add a byte array to the list. - pub fn insert_binary(&mut self, name: &str, value: &[u8]) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_binary<'a, N: IntoCStr<'a>>(&mut self, name: N, value: &[u8]) -> NvResult<()> { + let c_name = name.into_c_str()?; unsafe { - nvlist_add_binary(self.ptr, c_name.as_ptr(), value.as_ptr() as *const c_void, value.len()); + nvlist_add_binary( + self.ptr, + c_name.as_ptr(), + value.as_ptr() as *const c_void, + value.len(), + ); } self.check_if_error() } @@ -360,8 +391,8 @@ impl NvList { /// /// list.insert_bools("Important year", &slice); /// ``` - pub fn insert_bools(&mut self, name: &str, value: &[bool]) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_bools<'a, N: IntoCStr<'a>>(&mut self, name: N, value: &[bool]) -> NvResult<()> { + let c_name = name.into_c_str()?; unsafe { nvlist_add_bool_array(self.ptr, c_name.as_ptr(), value.as_ptr(), value.len()); } @@ -379,8 +410,8 @@ impl NvList { /// /// list.insert_numbers("Important year", &slice); /// ``` - pub fn insert_numbers(&mut self, name: &str, value: &[u64]) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_numbers<'a, N: IntoCStr<'a>>(&mut self, name: N, value: &[u64]) -> NvResult<()> { + let c_name = name.into_c_str()?; unsafe { nvlist_add_number_array(self.ptr, c_name.as_ptr(), value.as_ptr(), value.len()); } @@ -396,19 +427,17 @@ impl NvList { /// /// let orig = ["Hello", "World!"]; /// - /// list.insert_strings("key", &orig).unwrap(); + /// list.insert_strings("key", orig).unwrap(); /// /// let vec = list.get_strings("key").unwrap().unwrap(); /// /// assert_eq!(*vec, ["Hello", "World!"]); /// ``` - pub fn insert_strings(&mut self, name: &str, value: &[&str]) -> NvResult<()> { - let c_name = CString::new(name)?; - let strings: Vec = value - .iter() - .map(|e| CString::new(*e)) - .map(|e| e.expect("Failed to convert str to Cstring")) - .collect(); + pub fn insert_strings<'a, 'b, N: IntoCStr<'a>, V: IntoCStr<'b>, I: IntoIterator>(&mut self, name: N, value: I) -> NvResult<()> { + let c_name = name.into_c_str()?; + let strings= value.into_iter() + .map(IntoCStr::into_c_str) + .collect::>>()?; unsafe { let pointers: Vec<*const i8> = strings.iter().map(|e| e.as_ptr()).collect(); @@ -439,8 +468,12 @@ impl NvList { /// /// assert_eq!(NvFlag::None, nvlists.pop().unwrap().flags()); /// ``` - pub fn insert_nvlists(&mut self, name: &str, value: &[NvList]) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_nvlists<'a, N: IntoCStr<'a>>( + &mut self, + name: N, + value: &[NvList], + ) -> NvResult<()> { + let c_name = name.into_c_str()?; let vec = value.to_vec(); unsafe { let lists: Vec<*const nvlist_t> = @@ -468,8 +501,8 @@ impl NvList { /// /// assert!(list.contains_key("Important year").unwrap()); /// ``` - pub fn contains_key(&self, name: &str) -> NvResult { - let c_name = CString::new(name)?; + pub fn contains_key<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult { + let c_name = name.into_c_str()?; unsafe { Ok(nvlist_exists(self.ptr, c_name.as_ptr())) } } @@ -485,8 +518,12 @@ impl NvList { /// /// assert!(!list.contains_key_with_type("Important year", NvType::Bool).unwrap()); /// ``` - pub fn contains_key_with_type(&self, name: &str, ty: NvType) -> NvResult { - let c_name = CString::new(name)?; + pub fn contains_key_with_type<'a, N: IntoCStr<'a>>( + &self, + name: N, + ty: NvType, + ) -> NvResult { + let c_name = name.into_c_str()?; unsafe { Ok(nvlist_exists_type(self.ptr, c_name.as_ptr(), ty as i32)) } } @@ -502,8 +539,8 @@ impl NvList { /// let v = list.get_binary("x").unwrap().unwrap(); /// assert_eq!(&x, v); /// ``` - pub fn get_binary(&self, name: &str) -> NvResult> { - let c_name = CString::new(name)?; + pub fn get_binary<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult> { + let c_name = name.into_c_str()?; unsafe { let mut size: usize = 0; let ret = nvlist_get_binary(self.ptr, c_name.as_ptr(), &mut size as *mut usize); @@ -528,8 +565,8 @@ impl NvList { /// assert!(list.get_bool("Did history start on 1776?").unwrap().unwrap(), /// true); /// ``` - pub fn get_bool(&self, name: &str) -> NvResult> { - let c_name = CString::new(name)?; + pub fn get_bool<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult> { + let c_name = name.into_c_str()?; unsafe { if nvlist_exists_bool(self.ptr, c_name.as_ptr()) { Ok(Some(nvlist_get_bool(self.ptr, c_name.as_ptr()))) @@ -541,8 +578,8 @@ impl NvList { /// Get the first matching `u64` value paired with /// the given name. - pub fn get_number(&self, name: &str) -> NvResult> { - let c_name = CString::new(name)?; + pub fn get_number<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult> { + let c_name = name.into_c_str()?; unsafe { if nvlist_exists_number(self.ptr, c_name.as_ptr()) { Ok(Some(nvlist_get_number(self.ptr, c_name.as_ptr()))) @@ -565,8 +602,8 @@ impl NvList { /// /// assert_eq!(list.get_string("Hello").unwrap().unwrap(), "World!"); /// ``` - pub fn get_string(&self, name: &str) -> NvResult> { - let c_name = CString::new(name)?; + pub fn get_string<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult> { + let c_name = name.into_c_str()?; unsafe { if nvlist_exists_string(self.ptr, c_name.as_ptr()) { let ret = nvlist_get_string(self.ptr, c_name.as_ptr()); @@ -603,8 +640,8 @@ impl NvList { /// assert_eq!(other_nvlist.get_number("Important year").unwrap().unwrap(), /// 42); /// ``` - pub fn get_nvlist(&self, name: &str) -> NvResult> { - let c_name = CString::new(name)?; + pub fn get_nvlist<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult> { + let c_name = name.into_c_str()?; unsafe { if nvlist_exists_nvlist(self.ptr, c_name.as_ptr()) { let res = nvlist_get_nvlist(self.ptr, c_name.as_ptr()); @@ -628,8 +665,8 @@ impl NvList { /// assert_eq!(list.get_bools("true/false").unwrap().unwrap(), &[true, /// false, true]); /// ``` - pub fn get_bools<'a>(&'a self, name: &str) -> NvResult> { - let c_name = CString::new(name)?; + pub fn get_bools<'a, 'b, N: IntoCStr<'b>>(&'a self, name: N) -> NvResult> { + let c_name = name.into_c_str()?; unsafe { if nvlist_exists_bool_array(self.ptr, c_name.as_ptr()) { let mut len: usize = 0; @@ -654,8 +691,8 @@ impl NvList { /// assert_eq!(list.get_numbers("The Year").unwrap().unwrap(), &[1, 7, 7, /// 6]); /// ``` - pub fn get_numbers(&self, name: &str) -> NvResult> { - let c_name = CString::new(name)?; + pub fn get_numbers<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult> { + let c_name = name.into_c_str()?; unsafe { if nvlist_exists_number_array(self.ptr, c_name.as_ptr()) { let mut len: usize = 0; @@ -670,8 +707,8 @@ impl NvList { /// Get a `Vec` of the first string slice added to the `NvList` /// for the given name - pub fn get_strings(&self, name: &str) -> NvResult>> { - let c_name = CString::new(name)?; + pub fn get_strings<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult>> { + let c_name = name.into_c_str()?; unsafe { if nvlist_exists_string_array(self.ptr, c_name.as_ptr()) { let mut len: usize = 0; @@ -707,8 +744,8 @@ impl NvList { /// assert_eq!(vec.len(), 2); /// assert_eq!(vec[0].flags(), NvFlag::None); /// ``` - pub fn get_nvlists(&self, name: &str) -> NvResult>> { - let c_name = CString::new(name)?; + pub fn get_nvlists<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult>> { + let c_name = name.into_c_str()?; unsafe { if nvlist_exists_nvlist_array(self.ptr, c_name.as_ptr()) { let mut len: usize = 0; @@ -740,11 +777,13 @@ impl NvList { } /// The size of the current list - pub fn len(&self) -> usize { unsafe { nvlist_size(self.ptr) } } + pub fn len(&self) -> usize { + unsafe { nvlist_size(self.ptr) } + } /// Removes a key from the `NvList`. - pub fn remove(&mut self, name: &str) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn remove<'a, N: IntoCStr<'a>>(&mut self, name: N) -> NvResult<()> { + let c_name = name.into_c_str()?; unsafe { nvlist_free(self.ptr, c_name.as_ptr()); } @@ -753,8 +792,8 @@ impl NvList { /// Remove the element of the given name and type /// from the `NvList` - pub fn remove_with_type(&mut self, name: &str, ty: NvType) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn remove_with_type<'a, N: IntoCStr<'a>>(&mut self, name: N, ty: NvType) -> NvResult<()> { + let c_name = name.into_c_str()?; unsafe { nvlist_free_type(self.ptr, c_name.as_ptr(), ty as i32); } @@ -764,7 +803,9 @@ impl NvList { impl Clone for NvList { /// Clone list using libnv method. This will perform deep copy. - fn clone(&self) -> NvList { NvList { ptr: unsafe { nvlist_clone(self.ptr) } } } + fn clone(&self) -> NvList { + NvList { ptr: unsafe { nvlist_clone(self.ptr) } } + } } impl Drop for NvList { diff --git a/src/nvpair.rs b/src/nvpair.rs index 9432446..fc8cea2 100644 --- a/src/nvpair.rs +++ b/src/nvpair.rs @@ -1,17 +1,17 @@ //! Solaris implementation of Name/Value pairs library. -use nvpair_sys as sys; +use {nvpair_sys as sys, IntoCStr}; use crate::{NvError, NvResult}; -use std::{collections::HashMap, - convert::TryInto, - ffi::{CStr, CString}, - fmt::Formatter, - mem::MaybeUninit, - ptr::null_mut}; use std::os::unix::io::AsRawFd; - - +use std::{ + collections::HashMap, + convert::TryInto, + ffi::{CStr, CString}, + fmt::Formatter, + mem::MaybeUninit, + ptr::null_mut, +}; extern "C" { pub fn nvlist_print_json(fp: *mut libc::FILE, nvl: *const sys::nvlist_t) -> i32; @@ -20,7 +20,7 @@ extern "C" { /// own types if you don't want to convert to primitive types every time. pub trait NvTypeOp { /// Add self to given list. - fn add_to_list(&self, list: &mut NvList, name: &str) -> NvResult<()>; + fn add_to_list<'a, N: IntoCStr<'a>>(&self, list: &mut NvList, name: N) -> NvResult<()>; } #[repr(i32)] @@ -29,16 +29,16 @@ pub enum NvEncoding { /// A basic copy on insert operation. Native = 0, /// [XDR](https://tools.ietf.org/html/rfc4506) copy suitable for sending to remote host. - Xdr = 1, + Xdr = 1, } /// Options available for creation of an `nvlist` #[repr(u32)] #[derive(Copy, Clone, Debug, PartialEq)] pub enum NvFlag { /// No flags. Allows duplicate names of any type, but renders `get_` methods useless. - None = 0b000, + None = 0b000, /// An existing pair of the same type will be removed prior inserting. - UniqueName = 0b001, + UniqueName = 0b001, /// An existing pair of any type will be removed prior inserting. UniqueNameType = 0b010, } @@ -85,41 +85,63 @@ impl Value { } impl From for Value { - fn from(src: i8) -> Self { Value::Int8(src) } + fn from(src: i8) -> Self { + Value::Int8(src) + } } impl From for Value { - fn from(src: u8) -> Self { Value::Uint8(src) } + fn from(src: u8) -> Self { + Value::Uint8(src) + } } impl From for Value { - fn from(src: i16) -> Self { Value::Int16(src) } + fn from(src: i16) -> Self { + Value::Int16(src) + } } impl From for Value { - fn from(src: u16) -> Self { Value::Uint16(src) } + fn from(src: u16) -> Self { + Value::Uint16(src) + } } impl From for Value { - fn from(src: i32) -> Self { Value::Int32(src) } + fn from(src: i32) -> Self { + Value::Int32(src) + } } impl From for Value { - fn from(src: u32) -> Self { Value::Uint32(src) } + fn from(src: u32) -> Self { + Value::Uint32(src) + } } impl From for Value { - fn from(src: i64) -> Self { Value::Int64(src) } + fn from(src: i64) -> Self { + Value::Int64(src) + } } impl From for Value { - fn from(src: u64) -> Self { Value::Uint64(src) } + fn from(src: u64) -> Self { + Value::Uint64(src) + } } impl From for Value { - fn from(src: String) -> Self { Value::String(src) } + fn from(src: String) -> Self { + Value::String(src) + } } impl From<&str> for Value { - fn from(src: &str) -> Self { Value::String(src.into()) } + fn from(src: &str) -> Self { + Value::String(src.into()) + } } pub struct NvList { ptr: *mut sys::nvlist_t, } impl Drop for NvList { - fn drop(&mut self) { unsafe { sys::nvlist_free(self.ptr) } } + fn drop(&mut self) { + unsafe { sys::nvlist_free(self.ptr) } + } } /// Return new list with no flags. @@ -132,7 +154,7 @@ macro_rules! impl_list_op { ($type_:ty, $method:ident, false) => { impl NvTypeOp for $type_ { /// Add a `$type_` value to the `NvList` - fn add_to_list(&self, list: &mut NvList, name: &str) -> NvResult<()> { + fn add_to_list<'a, N: IntoCStr<'a>>(&self, list: &mut NvList, name: N) -> NvResult<()> { return list.$method(name, *self); } } @@ -140,7 +162,7 @@ macro_rules! impl_list_op { ($type_:ty, $method:ident, true) => { impl NvTypeOp for $type_ { /// Add a `$type_` value to the `NvList` - fn add_to_list(&self, list: &mut NvList, name: &str) -> NvResult<()> { + fn add_to_list<'a, N: IntoCStr<'a>>(&self, list: &mut NvList, name: N) -> NvResult<()> { return list.$method(name, &*self); } } @@ -150,9 +172,20 @@ macro_rules! impl_list_op { macro_rules! nvpair_type_array_method { ($type_:ty, $rmethod_insert:ident, $smethod_insert:ident, $rmethod_get:ident, $smethod_get:ident) => { /// Add `&[$type_]` value to the list. - pub fn $rmethod_insert(&mut self, name: &str, value: &mut [$type_]) -> NvResult<()> { - let c_name = CString::new(name)?; - let errno = unsafe { sys::$smethod_insert(self.ptr, c_name.as_ptr(), value.as_mut_ptr(), value.len() as u32) }; + pub fn $rmethod_insert<'a, N: IntoCStr<'a>>( + &mut self, + name: N, + value: &mut [$type_], + ) -> NvResult<()> { + let c_name = name.into_c_str()?; + let errno = unsafe { + sys::$smethod_insert( + self.ptr, + c_name.as_ptr(), + value.as_mut_ptr(), + value.len() as u32, + ) + }; if errno != 0 { Err(NvError::from_errno(errno)) } else { @@ -160,30 +193,30 @@ macro_rules! nvpair_type_array_method { } } - /// Get a `$type_` value by given name from the list. - pub fn $rmethod_get<'a>(&'a self, name: &str) -> NvResult<&'a [$type_]> { - let c_name = CString::new(name)?; + /// Get a `$type_` value by given name from the list. + pub fn $rmethod_get<'a, 'b, N: IntoCStr<'b>>(&'a self, name: N) -> NvResult<&'a [$type_]> { + let c_name = name.into_c_str()?; let mut ptr = null_mut(); let mut len = 0; - let errno = unsafe { - sys::$smethod_get(self.ptr, c_name.as_ptr(), &mut ptr, &mut len) - }; + let errno = unsafe { sys::$smethod_get(self.ptr, c_name.as_ptr(), &mut ptr, &mut len) }; if errno != 0 { Err(NvError::from_errno(errno)) } else { - let ret = unsafe { - std::slice::from_raw_parts(&mut *ptr, len.try_into().unwrap()) - }; + let ret = unsafe { std::slice::from_raw_parts(&mut *ptr, len.try_into().unwrap()) }; Ok(ret) } } - } + }; } macro_rules! nvpair_type_method { ($type_:ty, $rmethod_insert:ident, $smethod_insert:ident, $rmethod_get:ident, $smethod_get:ident) => { /// Add `$type_` value to the list. - pub fn $rmethod_insert(&mut self, name: &str, value: $type_) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn $rmethod_insert<'a, N: IntoCStr<'a>>( + &mut self, + name: N, + value: $type_, + ) -> NvResult<()> { + let c_name = name.into_c_str()?; let errno = unsafe { sys::$smethod_insert(self.ptr, c_name.as_ptr(), value) }; if errno != 0 { Err(NvError::from_errno(errno)) @@ -193,12 +226,10 @@ macro_rules! nvpair_type_method { } /// Get a `$type_` value by given name from the list. - pub fn $rmethod_get(&self, name: &str) -> NvResult<$type_> { - let c_name = CString::new(name)?; + pub fn $rmethod_get<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult<$type_> { + let c_name = name.into_c_str()?; let mut ptr = MaybeUninit::<$type_>::uninit(); - let errno = unsafe { - sys::$smethod_get(self.ptr, c_name.as_ptr(), ptr.as_mut_ptr()) - }; + let errno = unsafe { sys::$smethod_get(self.ptr, c_name.as_ptr(), ptr.as_mut_ptr()) }; if errno != 0 { Err(NvError::from_errno(errno)) } else { @@ -206,7 +237,7 @@ macro_rules! nvpair_type_method { Ok(ret) } } - } + }; } impl NvList { @@ -291,7 +322,9 @@ impl NvList { ); /// Make a copy of a pointer. Danger zone. - pub fn as_ptr(&self) -> *mut sys::nvlist_t { self.ptr } + pub fn as_ptr(&self) -> *mut sys::nvlist_t { + self.ptr + } pub fn new(flags: NvFlag) -> NvResult { let mut raw_list = null_mut(); @@ -303,7 +336,9 @@ impl NvList { } } - pub unsafe fn from_ptr(ptr: *mut sys::nvlist_t) -> Self { Self { ptr } } + pub unsafe fn from_ptr(ptr: *mut sys::nvlist_t) -> Self { + Self { ptr } + } pub fn iter(&self) -> impl Iterator + '_ { NvListIter { list: self, position: null_mut() } @@ -323,19 +358,23 @@ impl NvList { ret != sys::boolean_t::B_FALSE } - pub fn exists(&self, name: &str) -> NvResult { - let c_name = CString::new(name)?; + pub fn exists<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult { + let c_name = name.into_c_str()?; let ret = unsafe { sys::nvlist_exists(self.as_ptr(), c_name.as_ptr()) }; Ok(ret != sys::boolean_t::B_FALSE) } - pub fn insert(&mut self, name: &str, value: T) -> NvResult<()> { + pub fn insert<'a, N: IntoCStr<'a>, T: NvTypeOp>(&mut self, name: N, value: T) -> NvResult<()> { value.add_to_list(self, name) } /// Add a `bool` to the list. - pub fn insert_boolean_value(&mut self, name: &str, value: bool) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_boolean_value<'a, N: IntoCStr<'a>>( + &mut self, + name: N, + value: bool, + ) -> NvResult<()> { + let c_name = name.into_c_str()?; let v = { if value { sys::boolean_t::B_TRUE @@ -352,8 +391,8 @@ impl NvList { } /// Add a `bool` to the list. - pub fn insert_boolean(&mut self, name: &str) -> NvResult<()> { - let c_name = CString::new(name)?; + pub fn insert_boolean<'a, N: IntoCStr<'a>>(&mut self, name: N) -> NvResult<()> { + let c_name = name.into_c_str()?; let errno = unsafe { sys::nvlist_add_boolean(self.ptr, c_name.as_ptr()) }; if errno != 0 { Err(NvError::from_errno(errno)) @@ -362,10 +401,9 @@ impl NvList { } } - /// Get a `bool` from the list. - pub fn get_boolean_value(&self, name: &str) -> NvResult { - let c_name = CString::new(name)?; + pub fn get_boolean_value<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult { + let c_name = name.into_c_str()?; let mut ptr = MaybeUninit::::uninit(); let errno = unsafe { @@ -380,12 +418,10 @@ impl NvList { } /// Get a `bool` from the list. - pub fn get_boolean(&self, name: &str) -> NvResult { - let c_name = CString::new(name)?; + pub fn get_boolean<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult { + let c_name = name.into_c_str()?; - let errno = unsafe { - sys::nvlist_lookup_boolean(self.ptr, c_name.as_ptr()) - }; + let errno = unsafe { sys::nvlist_lookup_boolean(self.ptr, c_name.as_ptr()) }; if errno != 0 { Err(NvError::from_errno(errno)) } else { @@ -393,11 +429,14 @@ impl NvList { } } - /// Add a `&str` to the list. - pub fn insert_string(&mut self, name: &str, value: &str) -> NvResult<()> { - let c_name = CString::new(name)?; - let c_value = CString::new(value)?; + pub fn insert_string<'a, 'b, N: IntoCStr<'a>, V: IntoCStr<'b>>( + &mut self, + name: N, + value: V, + ) -> NvResult<()> { + let c_name = name.into_c_str()?; + let c_value = value.into_c_str()?; let errno = unsafe { sys::nvlist_add_string(self.ptr, c_name.as_ptr(), c_value.as_ptr()) }; if errno != 0 { Err(NvError::from_errno(errno)) @@ -406,8 +445,8 @@ impl NvList { } } - pub fn get_cstr(&self, name: &str) -> NvResult<&CStr> { - let c_name = CString::new(name)?; + pub fn get_cstr<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult<&CStr> { + let c_name = name.into_c_str()?; let mut ptr = null_mut(); let errno = unsafe { sys::nvlist_lookup_string(self.ptr, c_name.as_ptr(), &mut ptr) }; if errno != 0 { @@ -419,12 +458,12 @@ impl NvList { } /// Get a `String` from the list. - pub fn get_string(&self, name: &str) -> NvResult { + pub fn get_string<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult { self.get_str(name).map(str::to_owned) } /// Get a `String` from the list. - pub fn get_str(&self, name: &str) -> NvResult<&str> { + pub fn get_str<'a, N: IntoCStr<'a>>(&self, name: N) -> NvResult<&str> { self.get_cstr(name).and_then(|v| v.to_str().map_err(NvError::from)) } @@ -471,11 +510,17 @@ pub struct NvPairRef { } impl NvPairRef { - pub fn as_ptr(&self) -> *mut sys::nvpair_t { self.ptr } + pub fn as_ptr(&self) -> *mut sys::nvpair_t { + self.ptr + } - pub unsafe fn from_ptr(ptr: *mut sys::nvpair_t) -> Self { Self { ptr } } + pub unsafe fn from_ptr(ptr: *mut sys::nvpair_t) -> Self { + Self { ptr } + } - pub fn key(&self) -> &CStr { unsafe { CStr::from_ptr(sys::nvpair_name(self.as_ptr())) } } + pub fn key(&self) -> &CStr { + unsafe { CStr::from_ptr(sys::nvpair_name(self.as_ptr())) } + } pub fn value(&self) -> Value { let data_type = unsafe { sys::nvpair_type(self.as_ptr()) }; @@ -573,7 +618,7 @@ impl std::fmt::Debug for NvPairRef { } pub struct NvListIter<'a> { - list: &'a NvList, + list: &'a NvList, position: *mut sys::nvpair_t, } @@ -682,7 +727,6 @@ mod test { assert_eq!(true, ret); } - #[test] fn cr_i8() { let val = 4; @@ -891,4 +935,21 @@ mod test { assert_eq!(expected_map, list.into_hashmap()); } + + #[test] + fn into_c_str() { + let owned = CString::new("owned").unwrap(); + let mut list = NvList::new(NvFlag::UniqueNameType).unwrap(); + list.insert(owned, 1u32).unwrap(); + + let borrowed_name = CString::new("borrowed").unwrap(); + let borrowed: &CStr = borrowed_name.as_c_str(); + list.insert(borrowed, 2u32).unwrap(); + + let mut expected_map = HashMap::with_capacity(3); + expected_map.insert(String::from("owned"), Value::from(1u32)); + expected_map.insert(String::from("borrowed"), Value::from(2u32)); + + assert_eq!(expected_map, list.into_hashmap()); + } }