microsd 0.2.0

Light‐weight systemd auxiliars
Documentation
use std::iter::FusedIterator;
use std::ops::Range;
use std::os::fd::RawFd;
use std::str::Split;

use nix::unistd::close;

use crate::Error;
use crate::fd::Fd;
use crate::thin::ThinStr;

/// Listen file descriptor iterator for undifferentiated [`Fd`].
#[must_use]
#[derive(Debug)]
pub struct ListenFds(Range<RawFd>);

#[must_use]
#[derive(Debug)]
/// Listen file descriptor names.
pub struct ListenFdNames(ThinStr);

impl ListenFds {
	pub(crate) const START: RawFd = 3;

	/// Construct new [`ListenFds`] instance from a file descriptor number.
	///
	/// # Safety
	///
	/// This function is only safe to use if `num` unowned system file
	/// descriptors are available starting from descriptor number `3`
	/// ([`SD_LISTEN_FDS_START`](https://www.freedesktop.org/software/systemd/man/latest/SD_LISTEN_FDS_START.html)).
	///
	/// It is intended to be used with the number provided through the
	/// `LISTEN_FDS` environment variable by the session manager.
	#[inline]
	pub(crate) const unsafe fn new(num: u32) -> Self {
		Self(Range { start: Self::START, end: Self::START + num as RawFd })
	}
}

impl Drop for ListenFds {
	fn drop(&mut self) {
		// Close remaining file descriptors
		for fd in &mut self.0 {
			let res = close(fd);
			debug_assert!(res.is_ok());
		}
	}
}

impl Iterator for ListenFds {
	type Item = Result<Fd, Error>;

	#[inline]
	fn next(&mut self) -> Option<Self::Item> {
		self.0.next().map(|fd| unsafe { Fd::try_from(fd) })
	}

	#[inline]
	fn size_hint(&self) -> (usize, Option<usize>) {
		self.0.size_hint()
	}
}

impl DoubleEndedIterator for ListenFds {
	#[inline]
	fn next_back(&mut self) -> Option<Self::Item> {
		self.0.next_back().map(|fd| unsafe { Fd::try_from(fd) })
	}
}

impl ExactSizeIterator for ListenFds {}
impl FusedIterator for ListenFds {}

impl ListenFdNames {
	#[inline]
	pub(crate) const fn new(names: ThinStr) -> Self { Self(names) }
}

impl<'i> IntoIterator for &'i ListenFdNames {
	type IntoIter = Split<'i, char>;
	type Item = &'i str;

	#[inline]
	fn into_iter(self) -> Self::IntoIter { self.0.as_str().split(':') }
}