#![cfg(unix)]

use crate::{Cvt, Sealed};
use std::io::{self, Error};

macro_rules! impl_for_sint {
    ($($(#[$doc:meta])+ $T:ty)+) => {
        $(
            impl Sealed for $T {
                type Output = Self;
            }

            $(#[$doc])+
            impl Cvt for $T {
                /// Returns [`Err(io::Error::last_os_error())`](Error::last_os_error)
                /// if `self` is `-1`, [`Ok(self)`](Ok) otherwise.
                #[inline]
                fn cvt(self) -> io::Result<Self> {
                    if self == -1 {
                        Err(Error::last_os_error())
                    } else {
                        Ok(self)
                    }
                }
            }
        )+
    };
}

impl_for_sint! {
    /// Implementation for [`i32`], which is generally [`std::os::raw::c_int`].
    i32
    /// Implementation for [`i64`]. Sometimes [`i64`] is [`std::os::raw::c_long`].
    i64
    /// Implementation for [`ssize_t`](https://man7.org/linux/man-pages/man3/ssize_t.3.html),
    /// which is the return type of functions like
    /// [`read(2)`](https://man7.org/linux/man-pages/man2/read.2.html#SYNOPSIS).
    isize
}

#[cfg(test)]
mod tests {
    use crate::Cvt;
    use std::io::Error;

    #[test]
    fn cvt() {
        assert_eq!(0.cvt().unwrap(), 0);
        assert_eq!(0_i64.cvt().unwrap(), 0);
        assert_eq!(0_isize.cvt().unwrap(), 0);
        let code = Error::last_os_error().raw_os_error().unwrap();
        assert_eq!((-1).cvt().unwrap_err().raw_os_error().unwrap(), code);
        assert_eq!((-1_i64).cvt().unwrap_err().raw_os_error().unwrap(), code);
        assert_eq!((-1_isize).cvt().unwrap_err().raw_os_error().unwrap(), code);
    }
}
