use std::cmp::Ordering;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::path::Path;
use std::ptr::NonNull;
use crate::core::device::Tag;
use crate::core::device::TagName;
use crate::cache::Cache;
use crate::cache::TagIter;
use crate::cache::TagIterError;
use crate::ffi_utils;
#[derive(Debug)]
pub struct Device<'a> {
pub(crate) inner: libblkid::blkid_dev,
_marker: PhantomData<&'a Cache>,
}
impl<'a> Device<'a> {
pub(super) fn new(_: &'a Cache, device: NonNull<libblkid::blkid_struct_dev>) -> Device<'a> {
log::debug!("Device::new creating new `Device` instance");
Self {
inner: device.as_ptr(),
_marker: PhantomData,
}
}
pub fn name(&self) -> &Path {
let mut ptr = MaybeUninit::<*const libc::c_char>::zeroed();
unsafe {
ptr.write(libblkid::blkid_dev_devname(self.inner));
};
let device_name = unsafe { ptr.assume_init() };
let name = ffi_utils::const_c_char_array_to_path_ref(device_name);
log::debug!("Device::name device named {:?}", name);
name
}
#[doc(hidden)]
fn check_tag(
device: libblkid::blkid_dev,
tag_name: *const libc::c_char,
tag_value: *const libc::c_char,
) -> bool {
let result = unsafe { libblkid::blkid_dev_has_tag(device, tag_name, tag_value) };
match result {
1 => {
log::debug!("Device::check_tag libblkid::blkid_dev_has_tag found tag on device");
true
}
code => {
log::debug!(
"Device::check_tag failed to find tag on device. libblkid::blkid_dev_has_tag returned error code: {}",
code
);
false
}
}
}
pub fn has_tag<T>(&self, tag: T) -> bool
where
T: AsRef<Tag>,
{
let tag = tag.as_ref();
let tag_name = tag.name().to_c_string();
log::debug!(
"Device::has_tag checking if device {:?} has tag {:?}",
self.name(),
tag
);
tag.value_to_c_string()
.map(|tag_value| Self::check_tag(self.inner, tag_name.as_ptr(), tag_value.as_ptr()))
.map_err(|e| {
log::debug!(
"Device::has_tag failed to convert tag_name and/or tag_value to CString. {:?}",
e
);
e
})
.unwrap_or(false)
}
pub fn has_tag_named<T>(&self, tag_name: T) -> bool
where
T: AsRef<TagName>,
{
let tag_name = tag_name.as_ref();
log::debug!(
"Device::has_tag_named checking if device {:?} has tag with name {:?}",
self.name(),
tag_name
);
let c_tag_name = tag_name.to_c_string();
Self::check_tag(self.inner, c_tag_name.as_ptr(), std::ptr::null())
}
pub fn iter(&'a self) -> TagIter<'a> {
log::debug!("Device::iter creating new `TagIter` instance");
TagIter::new(self).unwrap()
}
pub fn try_iter(&'a self) -> Result<TagIter<'a>, TagIterError> {
log::debug!("Device::try_iter creating new `TagIter` instance");
TagIter::new(self)
}
}
impl<'a> PartialEq for Device<'a> {
fn eq(&self, other: &Self) -> bool {
self.name() == other.name() && self.iter().eq(other.iter())
}
}
impl<'a> Eq for Device<'a> {}
impl<'a> PartialOrd for Device<'a> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<'a> Ord for Device<'a> {
fn cmp(&self, other: &Self) -> Ordering {
let name_cmp = self.name().cmp(other.name());
let tags_cmp = self.iter().cmp(other.iter());
name_cmp.then(tags_cmp)
}
}