#![forbid(unsafe_code)]
use crate::{
error::{Error, ErrorImpl},
flags::OpenFlags,
procfs::ProcfsHandle,
utils::FdExt,
};
use std::{
fs::File,
os::unix::io::{AsFd, BorrowedFd, OwnedFd},
};
#[derive(Debug)]
pub struct Handle {
inner: OwnedFd,
}
impl Handle {
#[inline]
pub fn from_fd(fd: impl Into<OwnedFd>) -> Self {
Self { inner: fd.into() }
}
#[inline]
pub fn as_ref(&self) -> HandleRef<'_> {
HandleRef {
inner: self.as_fd(),
}
}
#[inline]
pub fn try_clone(&self) -> Result<Self, Error> {
self.as_ref().try_clone()
}
#[doc(alias = "pathrs_reopen")]
#[inline]
pub fn reopen(&self, flags: impl Into<OpenFlags>) -> Result<File, Error> {
self.as_ref().reopen(flags)
}
}
impl From<OwnedFd> for Handle {
fn from(fd: OwnedFd) -> Self {
Self::from_fd(fd)
}
}
impl From<Handle> for OwnedFd {
#[inline]
fn from(handle: Handle) -> Self {
handle.inner
}
}
impl AsFd for Handle {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}
#[derive(Copy, Clone, Debug)]
pub struct HandleRef<'fd> {
inner: BorrowedFd<'fd>,
}
impl HandleRef<'_> {
pub fn from_fd(inner: BorrowedFd<'_>) -> HandleRef<'_> {
HandleRef { inner }
}
pub fn try_clone(&self) -> Result<Handle, Error> {
self.as_fd()
.try_clone_to_owned()
.map_err(|err| {
ErrorImpl::OsError {
operation: "clone underlying handle file".into(),
source: err,
}
.into()
})
.map(Handle::from_fd)
}
#[doc(alias = "pathrs_reopen")]
pub fn reopen(&self, flags: impl Into<OpenFlags>) -> Result<File, Error> {
self.inner
.reopen(&ProcfsHandle::new()?, flags.into())
.map(File::from)
}
}
impl<'fd> From<BorrowedFd<'fd>> for HandleRef<'fd> {
fn from(fd: BorrowedFd<'fd>) -> Self {
Self::from_fd(fd)
}
}
impl AsFd for HandleRef<'_> {
#[inline]
fn as_fd(&self) -> BorrowedFd<'_> {
self.inner.as_fd()
}
}
#[cfg(test)]
mod tests {
use crate::{Handle, HandleRef, Root};
use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd};
use anyhow::Error;
use pretty_assertions::assert_eq;
#[test]
fn from_fd() -> Result<(), Error> {
let handle = Root::open(".")?.resolve(".")?;
let handle_ref1 = handle.as_ref();
let handle_ref2 = HandleRef::from_fd(handle.as_fd());
assert_eq!(
handle.as_fd().as_raw_fd(),
handle_ref1.as_fd().as_raw_fd(),
"Handle::as_ref should have the same underlying fd"
);
assert_eq!(
handle.as_fd().as_raw_fd(),
handle_ref2.as_fd().as_raw_fd(),
"HandleRef::from_fd should have the same underlying fd"
);
Ok(())
}
#[test]
fn into_from_ownedfd() -> Result<(), Error> {
let handle = Root::open(".")?.resolve(".")?;
let handle_fd = handle.as_fd().as_raw_fd();
let owned: OwnedFd = handle.into();
let owned_fd = owned.as_fd().as_raw_fd();
let handle2: Handle = owned.into();
let handle2_fd = handle2.as_fd().as_raw_fd();
assert_eq!(
handle_fd, owned_fd,
"OwnedFd::from(handle) should have same underlying fd",
);
assert_eq!(
handle_fd, handle2_fd,
"Handle -> OwnedFd -> Handle roundtrip should have same underlying fd",
);
Ok(())
}
#[test]
fn from_borrowedfd() -> Result<(), Error> {
let handle = Root::open(".")?.resolve(".")?;
let borrowed_fd: BorrowedFd<'_> = handle.as_fd();
let handle_ref: HandleRef<'_> = borrowed_fd.into();
assert_eq!(
handle.as_fd().as_raw_fd(),
borrowed_fd.as_fd().as_raw_fd(),
"BorrowedFd::from(HandleRef) should have the same underlying fd"
);
assert_eq!(
handle.as_fd().as_raw_fd(),
handle_ref.as_fd().as_raw_fd(),
"HandleRef::from(BorrowedFd) should have the same underlying fd"
);
Ok(())
}
}