From 0bae5d9fad3196c79f88a9d47eba38e89d5f291f Mon Sep 17 00:00:00 2001 From: chenjiayi Date: Wed, 8 Nov 2023 05:18:36 +0800 Subject: [PATCH 049/103] test(device): compress closures and add some unit cases Some unnecessary closures may decrease line coverage, thus compress them. Also drop Clone trait implementation for Device as it is not necessary. --- .../src/bin/devctl/subcmds/devctl_info.rs | 4 +- exts/devmaster/src/lib/builtin/hwdb.rs | 12 +- exts/devmaster/src/lib/framework/devmaster.rs | 1 - exts/devmaster/src/lib/framework/job_queue.rs | 2 - .../src/lib/framework/uevent_monitor.rs | 1 - libs/device/examples/device_from.rs | 4 +- libs/device/src/device.rs | 689 +++++++----------- libs/device/src/device_enumerator.rs | 10 +- libs/device/src/device_monitor.rs | 2 +- libs/device/src/error.rs | 65 +- 10 files changed, 324 insertions(+), 466 deletions(-) diff --git a/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs b/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs index 71c9254d..d4fcb6c4 100644 --- a/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs +++ b/exts/devmaster/src/bin/devctl/subcmds/devctl_info.rs @@ -286,7 +286,7 @@ fn print_device_chain(device: Device) -> Result<()> { while let Ok(parent) = child.get_parent() { print_all_attributes(&parent.borrow(), true)?; - child = parent.borrow().clone(); + child = parent.borrow().shallow_clone().unwrap(); } Ok(()) @@ -489,7 +489,7 @@ fn export_devices() -> Result<()> { } for device in e.iter() { - print_record(device.borrow().clone(), ""); + print_record(device.borrow().shallow_clone().unwrap(), ""); } Ok(()) diff --git a/exts/devmaster/src/lib/builtin/hwdb.rs b/exts/devmaster/src/lib/builtin/hwdb.rs index 63308e37..0b797874 100644 --- a/exts/devmaster/src/lib/builtin/hwdb.rs +++ b/exts/devmaster/src/lib/builtin/hwdb.rs @@ -123,7 +123,7 @@ impl Hwdb { let mut last = false; let mut src_dev = match srcdev { Some(d) => d, - None => dev.borrow_mut().clone(), + None => dev.borrow_mut().shallow_clone().unwrap(), }; loop { @@ -131,7 +131,7 @@ impl Hwdb { Ok(str_subsystem) => str_subsystem, Err(_) => { src_dev = match src_dev.get_parent() { - Ok(d) => d.borrow_mut().clone(), + Ok(d) => d.borrow_mut().shallow_clone().unwrap(), Err(_) => break, }; continue; @@ -142,7 +142,7 @@ impl Hwdb { if let Some(str_subsystem) = subsystem { if &dsubsys != str_subsystem { src_dev = match src_dev.get_parent() { - Ok(d) => d.borrow_mut().clone(), + Ok(d) => d.borrow_mut().shallow_clone().unwrap(), Err(_) => break, }; continue; @@ -170,7 +170,7 @@ impl Hwdb { if modalias.is_empty() { src_dev = match src_dev.get_parent() { - Ok(d) => d.borrow_mut().clone(), + Ok(d) => d.borrow_mut().shallow_clone().unwrap(), Err(_) => break, }; continue; @@ -189,7 +189,7 @@ impl Hwdb { } src_dev = match src_dev.get_parent() { - Ok(d) => d.borrow_mut().clone(), + Ok(d) => d.borrow_mut().shallow_clone().unwrap(), Err(_) => break, }; } @@ -258,7 +258,7 @@ impl Builtin for Hwdb { Ok(srcdev) => Some(srcdev), Err(e) => { return Err(Error::Other { - msg: format!("Failed to create sd_device object '{:?}'", dev), + msg: format!("Failed to create device object '{}'", device_id), errno: e.get_errno(), }); } diff --git a/exts/devmaster/src/lib/framework/devmaster.rs b/exts/devmaster/src/lib/framework/devmaster.rs index 370f4e84..94b93ca0 100644 --- a/exts/devmaster/src/lib/framework/devmaster.rs +++ b/exts/devmaster/src/lib/framework/devmaster.rs @@ -24,7 +24,6 @@ use std::{ }; /// encapsulate all submanagers -#[derive(Debug)] pub struct Devmaster { /// reference to events pub(crate) events: Rc, diff --git a/exts/devmaster/src/lib/framework/job_queue.rs b/exts/devmaster/src/lib/framework/job_queue.rs index e3e6e90b..7c6d6a8d 100644 --- a/exts/devmaster/src/lib/framework/job_queue.rs +++ b/exts/devmaster/src/lib/framework/job_queue.rs @@ -51,7 +51,6 @@ impl Display for JobState { } /// device job -#[derive(Debug)] pub struct DeviceJob { /// internal device pub device: Device, @@ -132,7 +131,6 @@ impl PartialEq for DeviceJob { } /// job queue -#[derive(Debug)] pub struct JobQueue { /// internal container of jobs pub(crate) jobs: RefCell>>, diff --git a/exts/devmaster/src/lib/framework/uevent_monitor.rs b/exts/devmaster/src/lib/framework/uevent_monitor.rs index fe7d19c3..809071d1 100644 --- a/exts/devmaster/src/lib/framework/uevent_monitor.rs +++ b/exts/devmaster/src/lib/framework/uevent_monitor.rs @@ -22,7 +22,6 @@ use std::os::unix::io::RawFd; use std::rc::Rc; /// uevent monitor -#[derive(Debug)] pub struct UeventMonitor { /// receive uevent from netlink socket device_monitor: DeviceMonitor, diff --git a/libs/device/examples/device_from.rs b/libs/device/examples/device_from.rs index 29e24bbc..146543ee 100644 --- a/libs/device/examples/device_from.rs +++ b/libs/device/examples/device_from.rs @@ -30,11 +30,11 @@ fn main() { let dev = Device::from_subsystem_sysname("drivers", "usb:hub").unwrap(); println!("{}", dev.get_sysname().unwrap()); println!("{}", dev.get_subsystem().unwrap()); - println!("{:?}", dev); + println!("{}", dev.get_device_id().unwrap()); } { let dev = Device::from_ifindex(2).unwrap(); - println!("{:?}", dev); + println!("{}", dev.get_device_id().unwrap()); } } diff --git a/libs/device/src/device.rs b/libs/device/src/device.rs index 6673d823..284cd6e9 100644 --- a/libs/device/src/device.rs +++ b/libs/device/src/device.rs @@ -12,7 +12,6 @@ //! struct Device //! -use crate::err_wrapper; use crate::utils::readlink_value; use crate::{error::*, DeviceAction}; use basic::fs_util::{chmod, open_temporary, touch_file}; @@ -49,7 +48,7 @@ pub const DB_BASE_DIR: &str = "data"; pub const TAGS_BASE_DIR: &str = "tags"; /// Device -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct Device { /// inotify handler pub watch_handle: RefCell, @@ -355,33 +354,16 @@ impl Device { let buf_trans: &[u8] = unsafe { std::slice::from_raw_parts(buf.as_ptr() as *const _, 16) }; - let ifname = String::from_utf8(buf_trans.to_vec()).map_err(|e| Error::Nix { - msg: format!("from_ifindex failed: from_utf8 {:?} ({})", buf_trans, e), - source: Errno::EINVAL, + let ifname = String::from_utf8(buf_trans.to_vec()).context(FromUtf8 { + msg: format!("invalid utf-8 string {:?}", buf_trans), })?; let syspath = format!("/sys/class/net/{}", ifname.trim_matches(char::from(0))); - let dev = Self::from_syspath(&syspath, true).map_err(|e| Error::Nix { - msg: format!("from_ifindex failed: {}", e), - source: e.get_errno(), - })?; + let dev = Self::from_syspath(&syspath, true)?; - let i = match dev.get_ifindex() { - Ok(i) => i, - Err(e) => { - if e.get_errno() == Errno::ENOENT { - return Err(Error::Nix { - msg: format!("from_ifindex failed: {}", e), - source: Errno::ENXIO, - }); - } - - return Err(Error::Nix { - msg: format!("from_ifindex failed: {}", e), - source: e.get_errno(), - }); - } - }; + let i = dev + .get_ifindex() + .map_err(|e| e.replace_errno(Errno::ENOENT, Errno::ENXIO))?; if i != ifindex { return Err(Error::Nix { @@ -393,8 +375,9 @@ impl Device { Ok(dev) } - /// create a Device instance from subsystem and sysname - /// if subsystem is 'drivers', sysname should be like 'xxx:yyy' + /// Create a Device instance from subsystem and sysname. + /// + /// If subsystem is 'drivers', sysname should be like 'xxx:yyy' pub fn from_subsystem_sysname(subsystem: &str, sysname: &str) -> Result { let sysname = sysname.replace('/', "!"); if subsystem == "subsystem" { @@ -506,10 +489,12 @@ impl Device { }) } - /// set sysattr value + /// Set sysattr value. + /// + /// If the sysattr is not 'uevent', the value will be cached. pub fn set_sysattr_value(&self, sysattr: &str, value: Option<&str>) -> Result<(), Error> { if value.is_none() { - self.remove_cached_sysattr_value(sysattr)?; + self.remove_cached_sysattr_value(sysattr); return Ok(()); } @@ -529,7 +514,7 @@ impl Device { }; if let Err(e) = file.write(value.unwrap().as_bytes()) { - self.remove_cached_sysattr_value(sysattr)?; + self.remove_cached_sysattr_value(sysattr); return Err(Error::Nix { msg: format!( "set_sysattr_value failed: can't write sysattr '{}'", @@ -559,28 +544,18 @@ impl Device { match id.chars().next() { Some('b') | Some('c') => { - let devnum = parse_devnum(&id[1..]).map_err(|_| Error::Nix { + let devnum = parse_devnum(&id[1..]).context(Basic { msg: format!("from_device_id failed: parse_devnum '{}' failed", id), - source: Errno::EINVAL, })?; - return Device::from_devnum(id.chars().next().unwrap(), devnum).map_err(|e| { - Error::Nix { - msg: format!("from_device_id failed: {}", e), - source: e.get_errno(), - } - }); + Device::from_devnum(id.chars().next().unwrap(), devnum) } Some('n') => { - let ifindex = parse_ifindex(&id[1..]).map_err(|_| Error::Nix { + let ifindex = parse_ifindex(&id[1..]).context(Basic { msg: format!("from_device_id failed: parse_ifindex '{}' failed", id), - source: Errno::EINVAL, })?; - Device::from_ifindex(ifindex).map_err(|e| Error::Nix { - msg: format!("from_device_id failed: {}", e), - source: e.get_errno(), - }) + Device::from_ifindex(ifindex) } Some('+') => { let sep = match id.find(':') { @@ -604,10 +579,7 @@ impl Device { let subsystem = id[1..sep].to_string(); let sysname = id[sep + 1..].to_string(); - Device::from_subsystem_sysname(&subsystem, &sysname).map_err(|e| Error::Nix { - msg: format!("from_device_id failed: {}", e), - source: e.get_errno(), - }) + Device::from_subsystem_sysname(&subsystem, &sysname) } _ => Err(Error::Nix { msg: format!("from_device_id failed: invalid id '{}'", id), @@ -648,7 +620,7 @@ impl Device { /// get the sysname of the device pub fn get_sysname(&self) -> Result { if self.sysname.borrow().is_empty() { - err_wrapper!(self.set_sysname_and_sysnum(), "get_sysname")?; + self.set_sysname_and_sysnum()?; } Ok(self.sysname.borrow().clone()) @@ -729,10 +701,7 @@ impl Device { // e.g. /sys/devices/pci0000:00/0000:00:10.0/host2/target2:0:1/2:0:1:0/block/sda/subsystem -> ../../../../../../../../class/block // get `block` let filename = if Path::exists(Path::new(subsystem_path)) { - readlink_value(subsystem_path).map_err(|e| Error::Nix { - msg: format!("get_subsystem failed: {}", e), - source: e.get_errno(), - })? + readlink_value(subsystem_path)? } else { "".to_string() }; @@ -766,10 +735,7 @@ impl Device { /// get the ifindex of device pub fn get_ifindex(&self) -> Result { - self.read_uevent_file().map_err(|e| Error::Nix { - msg: format!("get_ifindex failed: {}", e), - source: e.get_errno(), - })?; + self.read_uevent_file()?; if *self.ifindex.borrow() == 0 { return Err(Error::Nix { @@ -802,12 +768,7 @@ impl Device { /// get devnum pub fn get_devnum(&self) -> Result { - match self.read_uevent_file() { - Ok(_) => {} - Err(e) => { - return Err(e); - } - } + self.read_uevent_file()?; if major(*self.devnum.borrow()) == 0 { return Err(Error::Nix { @@ -870,10 +831,7 @@ impl Device { /// get device sysnum pub fn get_sysnum(&self) -> Result { if self.sysname.borrow().is_empty() { - self.set_sysname_and_sysnum().map_err(|e| Error::Nix { - msg: format!("get_sysnum failed: {}", e), - source: e.get_errno(), - })?; + self.set_sysname_and_sysnum()?; } if self.sysnum.borrow().is_empty() { @@ -915,10 +873,7 @@ impl Device { /// get device diskseq pub fn get_diskseq(&self) -> Result { - self.read_uevent_file().map_err(|e| Error::Nix { - msg: format!("get_diskseq failed: {}", e), - source: e.get_errno(), - })?; + self.read_uevent_file()?; if *self.diskseq.borrow() == 0 { return Err(Error::Nix { @@ -935,7 +890,6 @@ impl Device { /// get is initialized pub fn get_is_initialized(&self) -> Result { - // match self.read_db match self.read_db() { Ok(_) => {} Err(e) => { @@ -979,30 +933,21 @@ impl Device { /// check whether the device has the tag pub fn has_tag(&self, tag: &str) -> Result { - self.read_db().map_err(|e| Error::Nix { - msg: format!("has_tag failed: {}", e), - source: e.get_errno(), - })?; + self.read_db()?; Ok(self.all_tags.borrow().contains(tag)) } /// check whether the device has the current tag pub fn has_current_tag(&self, tag: &str) -> Result { - self.read_db().map_err(|e| Error::Nix { - msg: format!("has_tag failed: {}", e), - source: e.get_errno(), - })?; + self.read_db()?; Ok(self.current_tags.borrow().contains(tag)) } /// get the value of specific device property pub fn get_property_value(&self, key: &str) -> Result { - self.properties_prepare().map_err(|e| Error::Nix { - msg: format!("get_property_value failed: {}", e), - source: e.get_errno(), - })?; + self.properties_prepare()?; match self.properties.borrow().get(key) { Some(v) => Ok(v.clone()), @@ -1083,27 +1028,22 @@ impl Device { let mut file = std::fs::OpenOptions::new() .read(true) .open(&sysattr_path) - .map_err(|e| Error::Nix { + .context(Io { msg: format!( - "get_sysattr_value failed: can't open sysattr '{}': {}", - sysattr, e + "get_sysattr_value failed: can't open sysattr '{}'", + sysattr ), - source: Errno::from_i32(e.raw_os_error().unwrap_or_default()), })?; let mut value = String::new(); - file.read_to_string(&mut value).map_err(|e| Error::Nix { - msg: format!( - "get_sysattr_value failed: can't read sysattr '{}': {}", - sysattr, e - ), - source: Errno::from_i32(e.raw_os_error().unwrap_or_default()), + file.read_to_string(&mut value).context(Io { + msg: format!("get_sysattr_value failed: can't read sysattr '{}'", sysattr), })?; value.trim_end().to_string() } } Err(e) => { - self.remove_cached_sysattr_value(sysattr).unwrap(); + self.remove_cached_sysattr_value(sysattr); return Err(Error::Nix { msg: format!("get_sysattr_value failed: can't lstat '{}'", sysattr_path), source: e, @@ -1111,11 +1051,7 @@ impl Device { } }; - self.cache_sysattr_value(sysattr, &value) - .map_err(|e| Error::Nix { - msg: format!("get_sysattr_value failed: {}", e), - source: e.get_errno(), - })?; + self.cache_sysattr_value(sysattr, &value)?; Ok(value) } @@ -1133,15 +1069,9 @@ impl Device { let s = format!("{}", action); - let id = match randomize() { - Ok(id) => id, - Err(e) => { - return Err(Error::Nix { - msg: "Failed to randomize".to_string(), - source: e, - }) - } - }; + let id = randomize().context(Nix { + msg: "Failed to randomize".to_string(), + })?; let j = s + " " + &id.to_string(); @@ -1152,33 +1082,12 @@ impl Device { /// open device pub fn open(&self, oflags: OFlag) -> Result { - let devname = self.get_devname().map_err(|e| { - if e.get_errno() == Errno::ENOENT { - Error::Nix { - msg: format!("open failed: {}", e), - source: Errno::ENOEXEC, - } - } else { - Error::Nix { - msg: format!("open failed: {}", e), - source: e.get_errno(), - } - } - })?; - - let devnum = self.get_devnum().map_err(|e| { - if e.get_errno() == Errno::ENOENT { - Error::Nix { - msg: format!("open failed: {}", e), - source: Errno::ENOEXEC, - } - } else { - Error::Nix { - msg: format!("open failed: {}", e), - source: e.get_errno(), - } - } - })?; + let devname = self + .get_devname() + .map_err(|e| e.replace_errno(Errno::ENOENT, Errno::ENOEXEC))?; + let devnum = self + .get_devnum() + .map_err(|e| e.replace_errno(Errno::ENOENT, Errno::ENOEXEC))?; let subsystem = match self.get_subsystem() { Ok(s) => s, @@ -1212,19 +1121,13 @@ impl Device { } }; - let stat = match nix::sys::stat::fstat(file.as_raw_fd()) { - Ok(s) => s, - Err(e) => { - return Err(Error::Nix { - msg: format!( - "open failed: can't fstat fd {} for '{}'", - file.as_raw_fd(), - devname - ), - source: e, - }) - } - }; + let stat = nix::sys::stat::fstat(file.as_raw_fd()).context(Nix { + msg: format!( + "open failed: can't fstat fd {} for '{}'", + file.as_raw_fd(), + devname + ), + })?; if stat.st_rdev != devnum { return Err(Error::Nix { @@ -1265,18 +1168,11 @@ impl Device { let mut diskseq: u64 = 0; - if self.get_is_initialized().map_err(|e| Error::Nix { - msg: format!("open failed: {}", e), - source: e.get_errno(), - })? { + if self.get_is_initialized()? { match self.get_property_value("ID_IGNORE_DISKSEQ") { Ok(value) => { - if !value.parse::().map_err(|e| Error::Nix { - msg: format!( - "open failed: failed to parse value '{}' to boolean: {}", - value, e - ), - source: Errno::EINVAL, + if !value.parse::().context(ParseBool { + msg: format!("invalid value '{}'", value), })? { match self.get_diskseq() { Ok(n) => diskseq = n, @@ -1302,28 +1198,16 @@ impl Device { } } - let file2 = - basic::fd_util::fd_reopen(file.as_raw_fd(), oflags).map_err(|e| Error::Nix { - msg: format!("open failed: {}", e), - source: match e { - basic::Error::Nix { source } => source, - _ => Errno::EINVAL, - }, - })?; + let file2 = basic::fd_util::fd_reopen(file.as_raw_fd(), oflags).context(Basic { + msg: format!("failed to open {}", file.as_raw_fd()), + })?; if diskseq == 0 { return Ok(file2); } - let q = basic::fd_util::fd_get_diskseq(file2.as_raw_fd()).map_err(|e| Error::Nix { - msg: format!( - "open failed: failed to get diskseq on fd {}", - file2.as_raw_fd() - ), - source: match e { - basic::Error::Nix { source } => source, - _ => Errno::EINVAL, - }, + let q = basic::fd_util::fd_get_diskseq(file2.as_raw_fd()).context(Basic { + msg: format!("failed to get diskseq on fd {}", file2.as_raw_fd()), })?; if q != diskseq { @@ -1352,18 +1236,9 @@ impl Device { /// shadow clone a device object and import properties from db pub fn clone_with_db(&self) -> Result { - let device = self.shallow_clone().map_err(|e| Error::Nix { - msg: format!("clone_with_db failed: {}", e), - source: e.get_errno(), - })?; - - device.read_db().map_err(|e| Error::Nix { - msg: format!("clone_with_db failed: {}", e), - source: e.get_errno(), - })?; - + let device = self.shallow_clone()?; + device.read_db()?; device.sealed.replace(true); - Ok(device) } @@ -1427,22 +1302,14 @@ impl Device { /// /// The format is like: /// - /// character device: c: - /// - /// block device: b: - /// - /// network interface: n - /// - /// drivers: +drivers:: - /// - /// other subsystems: +: - /// + /// - character device: c: + /// - block device: b: + /// - network interface: n + /// - drivers: +drivers:: + /// - other subsystems: +: pub fn get_device_id(&self) -> Result { if self.device_id.borrow().is_empty() { - let subsystem = self.get_subsystem().map_err(|e| Error::Nix { - msg: format!("get_device_id failed: {}", e), - source: e.get_errno(), - })?; + let subsystem = self.get_subsystem()?; let id: String; if let Ok(devnum) = self.get_devnum() { @@ -1455,10 +1322,7 @@ impl Device { } else if let Ok(ifindex) = self.get_ifindex() { id = format!("n{}", ifindex); } else { - let sysname = self.get_sysname().map_err(|e| Error::Nix { - msg: format!("get_device_id failed: {}", e), - source: e.get_errno(), - })?; + let sysname = self.get_sysname()?; if subsystem == "drivers" { id = format!("+drivers:{}:{}", self.driver_subsystem.borrow(), sysname); @@ -1571,21 +1435,14 @@ impl Device { let db_path = format!("{}/{}/{}", self.base_path.borrow(), DB_BASE_DIR, id); if !has_info && *self.devnum.borrow() == 0 && *self.ifindex.borrow() == 0 { - unlink(db_path.as_str()).map_err(|e| Error::Nix { + unlink(db_path.as_str()).context(Nix { msg: format!("update_db failed: can't unlink db '{}'", db_path), - source: e, })?; - return Ok(()); } - create_dir_all(&format!("{}/{}", self.base_path.borrow(), DB_BASE_DIR)).map_err(|e| { - Error::Nix { - msg: "update_db failed: can't create db directory".to_string(), - source: e - .raw_os_error() - .map_or_else(|| nix::Error::EIO, nix::Error::from_i32), - } + create_dir_all(&format!("{}/{}", self.base_path.borrow(), DB_BASE_DIR)).context(Io { + msg: "failed to create db directory".to_string(), })?; if let Err(e) = chmod( @@ -1595,22 +1452,14 @@ impl Device { log::error!("Failed to set permission for /run/devmaster/data/: {}", e); } - let (mut file, tmp_file) = open_temporary(&db_path).map_err(|e| { - let errno = match e { - basic::error::Error::Nix { source } => source, - _ => nix::Error::EINVAL, - }; - Error::Nix { - msg: "update_db failed: can't open temporary file".to_string(), - source: errno, - } + let (mut file, tmp_file) = open_temporary(&db_path).context(Basic { + msg: "can't open temporary file".to_string(), })?; - self.atomic_create_db(&mut file, tmp_file.as_str(), db_path.as_str()) - .map_err(|e| { - Self::cleanup(&db_path, &tmp_file); - e - })?; + if let Err(e) = self.atomic_create_db(&mut file, tmp_file.as_str(), db_path.as_str()) { + Self::cleanup(&db_path, &tmp_file); + return Err(e); + } Ok(()) } @@ -1713,9 +1562,8 @@ impl Device { ); if add { - touch_file(&tag_path, true, Some(0o444), None, None).map_err(|e| Error::Nix { - msg: format!("tag_persist failed: can't touch file '{}': {}", tag_path, e), - source: nix::Error::EINVAL, + touch_file(&tag_path, true, Some(0o444), None, None).context(Basic { + msg: format!("can't touch file '{}'", tag_path), })?; if let Err(e) = chmod( @@ -1765,10 +1613,7 @@ impl Device { /// read database pub fn read_db(&self) -> Result<(), Error> { - self.read_db_internal(false).map_err(|e| Error::Nix { - msg: format!("read_db failed: {}", e), - source: e.get_errno(), - }) + self.read_db_internal(false) } /// read database internally @@ -1777,18 +1622,11 @@ impl Device { return Ok(()); } - let id = self.get_device_id().map_err(|e| Error::Nix { - msg: format!("read_db_internal failed: {}", e), - source: e.get_errno(), - })?; + let id = self.get_device_id()?; let path = format!("{}/{}/{}", self.base_path.borrow(), DB_BASE_DIR, id); self.read_db_internal_filename(&path) - .map_err(|e| Error::Nix { - msg: format!("read_db_internal failed: {}", e), - source: e.get_errno(), - }) } /// get properties nulstr, if it is out of date, update it @@ -1836,10 +1674,7 @@ impl Device { } // verify subsystem - let subsystem_ret = device.get_subsystem().map_err(|e| Error::Nix { - msg: format!("from_mode_and_devnum failed: {}", e), - source: e.get_errno(), - })?; + let subsystem_ret = device.get_subsystem()?; if (subsystem_ret == "block") != ((mode & S_IFMT) == S_IFBLK) { return Err(Error::Nix { msg: "from_mode_and_devnum failed: inconsistent subsystem".to_string(), @@ -1942,19 +1777,9 @@ impl Device { /// set the sysname and sysnum of device object pub fn set_sysname_and_sysnum(&self) -> Result<(), Error> { - let sysname = match self.devpath.borrow().rfind('/') { - Some(i) => String::from(&self.devpath.borrow()[i + 1..]), - None => { - return Err(Error::Nix { - msg: format!( - "set_sysname_and_sysnum failed: invalid devpath '{}'", - self.devpath.borrow() - ), - source: Errno::EINVAL, - }); - } - }; - + /* The devpath is validated to begin with '/' when setting syspath. */ + let idx = self.devpath.borrow().rfind('/').unwrap(); + let sysname = String::from(&self.devpath.borrow()[idx + 1..]); let sysname = sysname.replace('!', "/"); let mut ridx = sysname.len(); @@ -2146,15 +1971,10 @@ impl Device { /// set ifindex pub fn set_ifindex(&self, ifindex: &str) -> Result<(), Error> { self.add_property_internal("IFINDEX", ifindex).unwrap(); - self.ifindex.replace(match ifindex.parse::() { - Ok(idx) => idx, - Err(e) => { - return Err(Error::Nix { - msg: format!("set_ifindex failed: {}", e), - source: Errno::EINVAL, - }); - } - }); + self.ifindex + .replace(ifindex.parse::().context(ParseInt { + msg: format!("invalid integer '{}'", ifindex), + })?); Ok(()) } @@ -2172,12 +1992,8 @@ impl Device { /// set devmode pub fn set_devmode(&self, devmode: &str) -> Result<(), Error> { - let m = Some(mode_t::from_str_radix(devmode, 8).map_err(|e| Error::Nix { - msg: format!( - "set_devmode failed: can't change '{}' to mode: {}", - devmode, e - ), - source: Errno::EINVAL, + let m = Some(mode_t::from_str_radix(devmode, 8).context(ParseInt { + msg: format!("invalid octal mode '{}'", devmode), })?); self.devmode.replace(m); @@ -2189,9 +2005,8 @@ impl Device { /// set device uid pub fn set_devuid(&self, devuid: &str) -> Result<(), Error> { - let uid = devuid.parse::().map_err(|e| Error::Nix { - msg: format!("set_devuid failed: can't change '{}' to uid: {}", devuid, e), - source: Errno::EINVAL, + let uid = devuid.parse::().context(ParseInt { + msg: format!("invalid uid '{}'", devuid), })?; self.devuid.replace(Some(Uid::from_raw(uid))); @@ -2203,9 +2018,8 @@ impl Device { /// set device gid pub fn set_devgid(&self, devgid: &str) -> Result<(), Error> { - let gid = devgid.parse::().map_err(|e| Error::Nix { - msg: format!("set_devgid failed: can't change '{}' to gid: {}", devgid, e), - source: Errno::EINVAL, + let gid = devgid.parse::().context(ParseInt { + msg: format!("invalid gid '{}'", devgid), })?; self.devgid.replace(Some(Gid::from_raw(gid))); @@ -2217,15 +2031,10 @@ impl Device { /// set devnum pub fn set_devnum(&self, major: &str, minor: &str) -> Result<(), Error> { - let major_num: u64 = match major.parse() { - Ok(n) => n, - Err(e) => { - return Err(Error::Nix { - msg: format!("set_devnum failed: invalid major number '{}': {}", major, e), - source: Errno::EINVAL, - }); - } - }; + let major_num: u64 = major.parse().context(ParseInt { + msg: format!("invalid major number '{}'", major), + })?; + let minor_num: u64 = match minor.parse() { Ok(n) => n, Err(e) => { @@ -2247,15 +2056,9 @@ impl Device { pub fn set_diskseq(&self, diskseq: &str) -> Result<(), Error> { self.add_property_internal("DISKSEQ", diskseq).unwrap(); - let diskseq_num: u64 = match diskseq.parse() { - Ok(n) => n, - Err(e) => { - return Err(Error::Nix { - msg: format!("set_diskseq failed: invalid diskseq '{}': {}", diskseq, e), - source: Errno::EINVAL, - }); - } - }; + let diskseq_num: u64 = diskseq.parse().context(ParseInt { + msg: format!("invalid diskseq '{}'", diskseq), + })?; self.diskseq.replace(diskseq_num); @@ -2271,18 +2074,7 @@ impl Device { /// set action from string pub fn set_action_from_string(&self, action_s: &str) -> Result<(), Error> { - let action = match action_s.parse::() { - Ok(a) => a, - Err(_) => { - return Err(Error::Nix { - msg: format!( - "set_action_from_string failed: invalid action '{}'", - action_s - ), - source: Errno::EINVAL, - }); - } - }; + let action = action_s.parse::()?; self.set_action(action); @@ -2291,19 +2083,9 @@ impl Device { /// set seqnum from string pub fn set_seqnum_from_string(&self, seqnum_s: &str) -> Result<(), Error> { - let seqnum: u64 = match seqnum_s.parse() { - Ok(n) => n, - Err(_) => { - return Err(Error::Nix { - msg: format!( - "set_seqnum_from_string failed: invalid seqnum '{}'", - seqnum_s - ), - source: Errno::EINVAL, - }); - } - }; - + let seqnum: u64 = seqnum_s.parse().context(ParseInt { + msg: format!("invalid seqnum '{}'", seqnum_s), + })?; self.set_seqnum(seqnum); Ok(()) } @@ -2325,7 +2107,7 @@ impl Device { /// cache sysattr value pub fn cache_sysattr_value(&self, sysattr: &str, value: &str) -> Result<(), Error> { if value.is_empty() { - self.remove_cached_sysattr_value(sysattr)?; + self.remove_cached_sysattr_value(sysattr); } else { self.sysattr_values .borrow_mut() @@ -2336,34 +2118,27 @@ impl Device { } /// remove cached sysattr value - pub fn remove_cached_sysattr_value(&self, sysattr: &str) -> Result<(), Error> { + pub fn remove_cached_sysattr_value(&self, sysattr: &str) { self.sysattr_values.borrow_mut().remove(sysattr); - - Ok(()) } /// get cached sysattr value pub fn get_cached_sysattr_value(&self, sysattr: &str) -> Result { if !self.sysattr_values.borrow().contains_key(sysattr) { return Err(Error::Nix { - msg: format!( - "get_cached_sysattr_value failed: no cached sysattr '{}'", - sysattr - ), + msg: format!("no cached sysattr '{}'", sysattr), source: Errno::ESTALE, }); } - match self.sysattr_values.borrow().get(sysattr) { - Some(value) => Ok(value.clone()), - None => Err(Error::Nix { - msg: format!( - "get_cached_sysattr_value failed: non-existing sysattr '{}'", - sysattr - ), + self.sysattr_values + .borrow() + .get(sysattr) + .cloned() + .ok_or(Error::Nix { + msg: format!("non-existing sysattr '{}'", sysattr), source: Errno::ENOENT, - }), - } + }) } /// new from child @@ -2378,8 +2153,7 @@ impl Device { Some(p) => { if p == Path::new("/sys") { return Err(Error::Nix { - msg: "new_from_child failed: no available parent device until /sys" - .to_string(), + msg: "no available parent device".to_string(), source: Errno::ENODEV, }); } @@ -2387,7 +2161,7 @@ impl Device { let path = p .to_str() .ok_or(Error::Nix { - msg: format!("new_from_child failed: invalid path '{:?}'", p), + msg: format!("invalid path '{:?}'", p), source: Errno::ENODEV, })? .to_string(); @@ -2444,15 +2218,9 @@ impl Device { /// 3. if self devlinks are outdated, add to internal property /// 4. if self tags are outdated ,add to internal property pub fn properties_prepare(&self) -> Result<(), Error> { - self.read_uevent_file().map_err(|e| Error::Nix { - msg: format!("properties_prepare failed: {}", e), - source: e.get_errno(), - })?; + self.read_uevent_file()?; - self.read_db().map_err(|e| Error::Nix { - msg: format!("properties_prepare failed: {}", e), - source: e.get_errno(), - })?; + self.read_db()?; let property_devlinks_outdated = *self.property_devlinks_outdated.borrow(); if property_devlinks_outdated { @@ -2518,15 +2286,8 @@ impl Device { }; let mut buf = String::new(); - file.read_to_string(&mut buf).map_err(|e| Error::Nix { - msg: format!( - "read_db_internal_filename failed: can't read db '{}': {}", - filename, e - ), - source: e - .raw_os_error() - .map(nix::Error::from_i32) - .unwrap_or(nix::Error::EIO), + file.read_to_string(&mut buf).context(Io { + msg: format!("can't read db '{}'", filename), })?; self.is_initialized.replace(true); @@ -2540,10 +2301,7 @@ impl Device { let key = &line[0..1]; let value = &line[2..]; - self.handle_db_line(key, value).map_err(|e| Error::Nix { - msg: format!("read_db_internal_filename failed: {}", e), - source: e.get_errno(), - })?; + self.handle_db_line(key, value)?; } Ok(()) @@ -2572,29 +2330,18 @@ impl Device { let (k, v) = (tokens[0], tokens[1]); - self.add_property_internal(k, v).map_err(|e| Error::Nix { - msg: format!("handle_db_line failed: {}", e), - source: e.get_errno(), - })?; + self.add_property_internal(k, v)?; } "I" => { - let time = value.parse::().map_err(|e| Error::Nix { - msg: format!( - "handle_db_line failed: invalid initialized time '{}': {}", - value, e - ), - source: Errno::EINVAL, + let time = value.parse::().context(ParseInt { + msg: format!("invalid usec integer '{}'", value), })?; self.set_usec_initialized(time); } "L" => { - let priority = value.parse::().map_err(|e| Error::Nix { - msg: format!( - "handle_db_line failed: failed to parse devlink priority '{}': {}", - value, e - ), - source: Errno::EINVAL, + let priority = value.parse::().context(ParseInt { + msg: format!("invalid link priority integer '{}'", value), })?; self.devlink_priority.replace(priority); @@ -2603,12 +2350,8 @@ impl Device { log::debug!("watch handle in database is deprecated."); } "V" => { - let version = value.parse::().map_err(|e| Error::Nix { - msg: format!( - "handle_db_line failed: failed to parse database version '{}': {}", - value, e - ), - source: Errno::EINVAL, + let version = value.parse::().context(ParseInt { + msg: format!("invalid db version integer '{}'", value), })?; self.database_version.replace(version); @@ -2625,22 +2368,11 @@ impl Device { pub fn shallow_clone(&self) -> Result { let device = Self::default(); - let syspath = self.get_syspath().map_err(|e| Error::Nix { - msg: format!("shallow_clone failed: {}", e), - source: e.get_errno(), - })?; + let syspath = self.get_syspath()?; - device - .set_syspath(&syspath, false) - .map_err(|e| Error::Nix { - msg: format!("shallow_clone failed: {}", e), - source: e.get_errno(), - })?; + device.set_syspath(&syspath, false)?; - let subsystem = self.get_subsystem().map_err(|e| Error::Nix { - msg: format!("shallow_clone failed: {}", e), - source: e.get_errno(), - })?; + let subsystem = self.get_subsystem()?; device.set_subsystem(&subsystem); @@ -2651,24 +2383,15 @@ impl Device { } if let Ok(ifindex) = self.get_property_value("IFINDEX") { - device.set_ifindex(&ifindex).map_err(|e| Error::Nix { - msg: format!("shallow_clone failed: failed to set_ifindex ({})", e), - source: e.get_errno(), - })?; + device.set_ifindex(&ifindex)?; } if let Ok(major) = self.get_property_value("MAJOR") { let minor = self.get_property_value("MINOR")?; - device.set_devnum(&major, &minor).map_err(|e| Error::Nix { - msg: format!("shallow_clone failed: {}", e), - source: e.get_errno(), - })?; + device.set_devnum(&major, &minor)?; } - device.read_uevent_file().map_err(|e| Error::Nix { - msg: format!("shallow_clone failed: {}", e), - source: e.get_errno(), - })?; + device.read_uevent_file()?; Ok(device) } @@ -2685,12 +2408,8 @@ impl Device { "DRIVER" => self.set_driver(value), "IFINDEX" => self.set_ifindex(value)?, "USEC_INITIALIZED" => { - self.set_usec_initialized(value.parse::().map_err(|e| Error::Nix { - msg: format!( - "amend_key_value failed: failed to parse initialized time '{}': {}", - value, e - ), - source: Errno::EINVAL, + self.set_usec_initialized(value.parse::().context(ParseInt { + msg: format!("invalid usec integer '{}'", value), })?); } "DEVMODE" => self.set_devmode(value)?, @@ -2814,9 +2533,8 @@ impl Device { format!("{}/{}", syspath, subdir) }; - std::fs::read_dir(&dir).map_err(|e| Error::Nix { + std::fs::read_dir(&dir).context(Io { msg: format!("Failed to read directory '{}'", &dir), - source: nix::Error::from_i32(e.raw_os_error().unwrap_or_default()), }) } @@ -3017,7 +2735,7 @@ impl Device { log::debug!( "failed to read db of '{}': {}", self.get_device_id() - .unwrap_or(self.devpath.borrow().clone()), + .unwrap_or_else(|_| self.devpath.borrow().clone()), e ) } @@ -3033,7 +2751,7 @@ impl Device { log::error!( "failed to read db of '{}': {}", self.get_device_id() - .unwrap_or(self.devpath.borrow().clone()), + .unwrap_or_else(|_| self.devpath.borrow().clone()), e ) } @@ -3049,7 +2767,7 @@ impl Device { log::debug!( "failed to read db of '{}': {}", self.get_device_id() - .unwrap_or(self.devpath.borrow().clone()), + .unwrap_or_else(|_| self.devpath.borrow().clone()), e ) } @@ -3065,7 +2783,7 @@ impl Device { log::debug!( "failed to prepare properties of '{}': {}", self.get_device_id() - .unwrap_or(self.devpath.borrow().clone()), + .unwrap_or_else(|_| self.devpath.borrow().clone()), e ) } @@ -3081,7 +2799,7 @@ impl Device { log::debug!( "failed to enumerate children of '{}': {}", self.get_device_id() - .unwrap_or(self.devpath.borrow().clone()), + .unwrap_or_else(|_| self.devpath.borrow().clone()), e ) } @@ -3097,7 +2815,8 @@ impl Device { if let Err(e) = self.read_all_sysattrs() { log::debug!( "{}: failed to read all sysattrs: {}", - self.get_sysname().unwrap_or(self.devpath.borrow().clone()), + self.get_sysname() + .unwrap_or_else(|_| self.devpath.borrow().clone()), e ); } @@ -3117,6 +2836,7 @@ impl PartialEq for Device { #[cfg(test)] mod tests { + use std::fs::OpenOptions; use std::panic::catch_unwind; use crate::{ @@ -3426,6 +3146,11 @@ mod tests { nix::Error::EINVAL ); + /* Test other add_* methods. */ + device.add_property("A", "AA").unwrap(); + device.add_property("B", "BB").unwrap(); + device.add_tags("A:B:C", true); + device.update_db().unwrap(); /* Test enumerating child devices */ @@ -3902,8 +3627,8 @@ Q:devmaster V:100 "; touch_file("/tmp/tmp_db", false, None, None, None).unwrap(); - let mut f = File::options().write(true).open("/tmp/tmp_db").unwrap(); - f.write(content.as_bytes()).unwrap(); + let mut f = OpenOptions::new().write(true).open("/tmp/tmp_db").unwrap(); + f.write_all(content.as_bytes()).unwrap(); let device = Device::new(); device.read_db_internal_filename("/tmp/tmp_db").unwrap(); } @@ -3912,8 +3637,8 @@ V:100 { let content = "error "; - let mut f = File::options().write(true).open("/tmp/tmp_db").unwrap(); - f.write(content.as_bytes()).unwrap(); + let mut f = OpenOptions::new().write(true).open("/tmp/tmp_db").unwrap(); + f.write_all(content.as_bytes()).unwrap(); let device = Device::new(); device.read_db_internal_filename("/tmp/tmp_db").unwrap(); } @@ -3922,13 +3647,13 @@ V:100 { let content = "I:invalid "; - let mut f = File::options().write(true).open("/tmp/tmp_db").unwrap(); - f.write(content.as_bytes()).unwrap(); + let mut f = OpenOptions::new().write(true).open("/tmp/tmp_db").unwrap(); + f.write_all(content.as_bytes()).unwrap(); let device = Device::new(); assert!(device.read_db_internal_filename("/tmp/tmp_db").is_err()); } - /* DB shoud be readable. */ + /* DB should be readable. */ { touch_file("/tmp/tmp_db_writeonly", false, Some(0o222), None, None).unwrap(); let device = Device::new(); @@ -3973,4 +3698,98 @@ V:100 }) .is_err()); } + + #[test] + fn test_set() { + let device = Device::from_subsystem_sysname("net", "lo").unwrap(); + device.set_devuid("1").unwrap(); + device.set_devgid("1").unwrap(); + device.set_devmode("666").unwrap(); + device.set_diskseq("1").unwrap(); + device.set_action_from_string("change").unwrap(); + device.set_sysattr_value("ifalias", Some("test")).unwrap(); + + assert_eq!(&device.get_property_value("DEVUID").unwrap(), "1"); + assert_eq!(&device.get_property_value("DEVGID").unwrap(), "1"); + assert_eq!(&device.get_property_value("DEVMODE").unwrap(), "666"); + assert_eq!(&device.get_property_value("DISKSEQ").unwrap(), "1"); + assert_eq!(&device.get_property_value("ACTION").unwrap(), "change"); + assert_eq!(&device.get_cached_sysattr_value("ifalias").unwrap(), "test"); + + assert!(device.set_devuid("invalid").is_err()); + assert!(device.set_devgid("invalid").is_err()); + assert!(device.set_devmode("invalid").is_err()); + assert!(device.set_diskseq("invalid").is_err()); + assert!(device.set_action_from_string("invalid").is_err()); + assert!(device.set_sysattr_value("nonexist", Some("test")).is_err()); + + assert!(device.set_sysattr_value("nonexist", None).is_ok()); + assert!(device.set_sysattr_value("ifalias", None).is_ok()); + } + + #[test] + fn test_from_device_id() { + assert!(Device::from_device_id("invalid").is_err()); + assert!(Device::from_device_id("b").is_err()); + assert!(Device::from_device_id("+drivers").is_err()); + assert!(Device::from_device_id("+drivers:").is_err()); + assert!(Device::from_device_id("+drivers::usb").is_err()); + + let dev = Device::from_device_id("+drivers:usb:usb").unwrap(); + println!("{}", dev.get_device_id().unwrap()); + + let dev = Device::from_syspath("/sys/bus/usb/drivers/usb", true).unwrap(); + println!("{}", dev.get_device_id().unwrap()); + + let _ = unlink("/tmp/devmaster/data/+drivers:usb:usb"); + dev.set_base_path("/tmp/devmaster"); + assert!(dev.update_db().is_err()); + dev.add_property("hello", "world").unwrap(); + dev.update_db().unwrap(); + assert!(Path::new("/tmp/devmaster/data/+drivers:usb:usb").exists()); + } + + #[test] + fn test_get_err() { + let device = Device::new(); + assert!(device.get_syspath().is_err()); + assert!(device.get_devpath().is_err()); + assert!(device.get_parent().is_err()); + assert!(device.get_devtype().is_err()); + assert!(!device.get_is_initialized().unwrap()); + } + + #[test] + fn test_cleanup() { + let _ = touch_file("/tmp/devmaster/a", false, None, None, None); + let _ = touch_file("/tmp/devmaster/b", false, None, None, None); + Device::cleanup("/tmp/devmaster/a", "/tmp/devmaster/b"); + } + + #[test] + fn test_fmt() { + let device = Device::from_subsystem_sysname("net", "lo").unwrap(); + println!("{:?}", device); + } + + #[test] + fn test_set_syspath_no_verify() { + let device = Device::new(); + device.set_syspath("/sys/test", false).unwrap(); + + assert!(device.set_sysname_and_sysnum().is_ok()); + } + + #[test] + fn test_partial_eq_trait() { + let dev1 = Device::from_syspath("/sys/class/net/lo", true).unwrap(); + let dev2 = Device::from_subsystem_sysname("net", "lo").unwrap(); + + assert!(dev1 == dev2); + } + + #[test] + fn test_from_devnum_err() { + assert!(Device::from_devnum('x', 100).is_err()); + } } diff --git a/libs/device/src/device_enumerator.rs b/libs/device/src/device_enumerator.rs index 96cac2d9..55f8681d 100644 --- a/libs/device/src/device_enumerator.rs +++ b/libs/device/src/device_enumerator.rs @@ -12,7 +12,7 @@ //! enumerate /sys to collect devices //! -use crate::{device::Device, err_wrapper, error::Error, utils::*}; +use crate::{device::Device, error::Error, utils::*}; use bitflags::bitflags; use fnmatch_sys::fnmatch; use nix::errno::Errno; @@ -45,7 +45,7 @@ impl Default for MatchInitializedType { } /// enumerate devices or subsystems under /sys -#[derive(Debug, Default)] +#[derive(Default)] pub struct DeviceEnumerator { /// enumerator type pub(crate) etype: RefCell, @@ -289,7 +289,7 @@ impl DeviceEnumerator { /// add match parent pub fn add_match_parent_incremental(&mut self, parent: &Device) -> Result<(), Error> { - let syspath = err_wrapper!(parent.get_syspath(), "add_match_parent_incremental")?; + let syspath = parent.get_syspath()?; self.match_parent.borrow_mut().insert(syspath); self.scan_up_to_date.replace(false); Ok(()) @@ -364,7 +364,7 @@ impl DeviceEnumerator { // remove already sorted devices from the hashmap (self.devices_by_syspath) // avoid get repeated devices from the hashmap later for device in devices.iter().skip(m) { - let syspath = err_wrapper!(device.borrow().get_syspath(), "sort_devices")?; + let syspath = device.borrow().get_syspath()?; self.devices_by_syspath.borrow_mut().remove(&syspath); } @@ -400,7 +400,7 @@ impl DeviceEnumerator { /// add device pub(crate) fn add_device(&self, device: Rc>) -> Result { - let syspath = err_wrapper!(device.borrow().get_syspath(), "add_device")?; + let syspath = device.borrow().get_syspath()?; match self.devices_by_syspath.borrow_mut().insert(syspath, device) { Some(_) => { diff --git a/libs/device/src/device_monitor.rs b/libs/device/src/device_monitor.rs index 729a7dcb..db51c40f 100644 --- a/libs/device/src/device_monitor.rs +++ b/libs/device/src/device_monitor.rs @@ -250,7 +250,7 @@ mod tests { /// fn dispatch(&self, e: &Events) -> i32 { let device = self.device_monitor.receive_device().unwrap(); - println!("{:?}", device); + println!("{}", device.get_device_id().unwrap()); e.set_exit(); 0 } diff --git a/libs/device/src/error.rs b/libs/device/src/error.rs index 40589c21..10410301 100644 --- a/libs/device/src/error.rs +++ b/libs/device/src/error.rs @@ -38,6 +38,24 @@ pub enum Error { #[snafu(context, display("Basic error: {}", msg))] Basic { msg: String, source: basic::Error }, + + #[snafu(context, display("Failed to parse boolean: {}", msg))] + ParseBool { + msg: String, + source: std::str::ParseBoolError, + }, + + #[snafu(context, display("Failed to parse integer: {}", msg))] + ParseInt { + msg: String, + source: std::num::ParseIntError, + }, + + #[snafu(context, display("Failed to parse utf-8: {}", msg))] + FromUtf8 { + msg: String, + source: std::string::FromUtf8Error, + }, } impl Error { @@ -53,21 +71,13 @@ impl Error { source: errno, } => Errno::from_i32(errno.raw_os_error().unwrap_or_default()), Self::Basic { msg: _, source } => Errno::from_i32(source.get_errno()), + Self::ParseBool { msg: _, source: _ } => nix::Error::EINVAL, + Self::ParseInt { msg: _, source: _ } => nix::Error::EINVAL, + Self::FromUtf8 { msg: _, source: _ } => nix::Error::EINVAL, } } } -/// append current function and inherit the errno -#[macro_export] -macro_rules! err_wrapper { - ($e:expr, $s:expr) => { - $e.map_err(|e| Error::Nix { - msg: format!("$s failed: {}", e), - source: e.get_errno(), - }) - }; -} - impl Error { /// check whether the device error belongs to specific errno pub fn is_errno(&self, errno: nix::Error) -> bool { @@ -81,4 +91,37 @@ impl Error { Errno::ENODEV | Errno::ENXIO | Errno::ENOENT ) } + + pub(crate) fn replace_errno(self, from: Errno, to: Errno) -> Self { + let n = self.get_errno(); + + if n == from { + Self::Nix { + msg: self.to_string(), + source: to, + } + } else { + self + } + } +} + + +#[cfg(test)] +mod test { + use super::*; + use nix::errno::Errno; + + #[test] + fn test_replace_errno() { + let e = Error::Nix { + msg: "test".to_string(), + source: Errno::ENOENT, + }; + + assert_eq!( + Errno::ENOEXEC, + e.replace_errno(Errno::ENOENT, Errno::ENOEXEC).get_errno(), + ); + } } -- 2.33.0