m2 0.0.0

Set of Unix tools to work with m2dirs
Documentation
use std::{
    env::{self, home_dir},
    io,
    path::PathBuf,
};

use libc::{STDOUT_FILENO, TIOCGWINSZ, c_ushort, ioctl};

/// Retrieves the m2 cache directory path.
///
/// Attempts to use the `XDG_CACHE_HOME` environment variable to find the cache
/// directory, falling back to the system's default cache directory if
/// `XDG_CACHE_HOME` is not set. Returns `None` of none of these can be
/// determined.
pub fn m2_cache_dir() -> Option<PathBuf> {
    env::var_os("XDG_CACHE_HOME")
        .map(Into::into)
        .or_else(|| Some(home_dir()?.join(".cache")))
        .map(|path| path.join("m2"))
}

/// Retrieves the size of the terminal window in columns and rows.
///
/// Makes use of the `ioctl` system call with `TIOCGWINSZ` to query the terminal
/// size directly from the kernel. This method is inherently platform-specific
/// and relies on Unix-like environment compatibility.
pub fn terminal_size() -> io::Result<(u16, u16)> {
    trait IsMinusOne {
        fn is_minus_one(&self) -> bool;
    }

    macro_rules! impl_is_minus_one {
        ($($t:ident)*) => ($(impl IsMinusOne for $t {
            fn is_minus_one(&self) -> bool {
                *self == -1
            }
        })*)
    }

    impl_is_minus_one! { i8 i16 i32 i64 isize }

    fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> {
        if t.is_minus_one() {
            Err(io::Error::last_os_error())
        } else {
            Ok(t)
        }
    }

    #[repr(C)]
    struct TermSize {
        row: c_ushort,
        col: c_ushort,
        x: c_ushort,
        y: c_ushort,
    }

    unsafe {
        let mut size: TermSize = std::mem::zeroed();
        cvt(ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size as *mut _))?;
        Ok((size.col, size.row))
    }
}