alpm 2.2.1

Rust bindings for libalpm
Documentation
use std::cmp::Ordering;
use std::ffi::CStr;
use std::ffi::CString;
use std::fmt;
use std::ops::Deref;
use std::os::raw::c_char;

use alpm_sys::*;

pub fn vercmp<S: Into<Vec<u8>>>(a: S, b: S) -> Ordering {
    let a = Version::new(a);
    let b = Version::new(b);
    a.vercmp(b)
}

#[repr(transparent)]
#[derive(Debug, Eq)]
pub struct Ver(CStr);

impl Ver {
    pub fn new(s: &CStr) -> &Ver {
        unsafe { &*(s as *const CStr as *const Ver) }
    }

    pub fn as_str(&self) -> &str {
        self
    }

    pub fn vercmp<V: AsRef<Ver>>(&self, other: V) -> Ordering {
        unsafe { alpm_pkg_vercmp(self.0.as_ptr(), other.as_ref().0.as_ptr()).cmp(&0) }
    }

    pub(crate) unsafe fn from_ptr<'a>(s: *const c_char) -> &'a Ver {
        Ver::new(CStr::from_ptr(s))
    }
}

impl<'a> From<&'a CStr> for &'a Ver {
    fn from(s: &'a CStr) -> Self {
        Ver::new(s)
    }
}

impl AsRef<Ver> for Ver {
    fn as_ref(&self) -> &Ver {
        self
    }
}

impl Deref for Ver {
    type Target = str;
    fn deref(&self) -> &Self::Target {
        self.0.to_str().unwrap()
    }
}

impl fmt::Display for Ver {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self)
    }
}

impl PartialOrd for Ver {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        unsafe { alpm_pkg_vercmp(self.0.as_ptr(), other.0.as_ptr()).partial_cmp(&0) }
    }
}

impl PartialOrd<Version> for &Ver {
    fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
        self.partial_cmp(&other.as_ver())
    }
}

impl AsRef<str> for Ver {
    fn as_ref(&self) -> &str {
        self
    }
}

impl PartialEq for Ver {
    fn eq(&self, other: &Self) -> bool {
        unsafe { alpm_pkg_vercmp(self.0.as_ptr(), other.0.as_ptr()) == 0 }
    }
}

impl PartialEq<Version> for &Ver {
    fn eq(&self, other: &Version) -> bool {
        unsafe { alpm_pkg_vercmp(self.0.as_ptr(), other.0.as_ptr()) == 0 }
    }
}

#[derive(Debug, Eq, Clone)]
pub struct Version(CString);

impl Version {
    pub fn new<S: Into<Vec<u8>>>(s: S) -> Self {
        let s = CString::new(s).unwrap();
        Version(s)
    }

    pub fn as_ver(&self) -> &Ver {
        self
    }
}

impl fmt::Display for Version {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt.write_str(self.as_ver())
    }
}

impl Deref for Version {
    type Target = Ver;
    fn deref(&self) -> &Self::Target {
        Ver::new(&self.0)
    }
}

impl PartialOrd for Version {
    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
        unsafe { alpm_pkg_vercmp(self.0.as_ptr(), other.0.as_ptr()).partial_cmp(&0) }
    }
}

impl PartialOrd<&Ver> for Version {
    fn partial_cmp(&self, other: &&Ver) -> Option<Ordering> {
        unsafe { alpm_pkg_vercmp(self.0.as_ptr(), other.0.as_ptr()).partial_cmp(&0) }
    }
}

impl AsRef<Ver> for Version {
    fn as_ref(&self) -> &Ver {
        self.as_ver()
    }
}

impl PartialEq for Version {
    fn eq(&self, other: &Self) -> bool {
        unsafe { alpm_pkg_vercmp(self.0.as_ptr(), other.0.as_ptr()) == 0 }
    }
}

impl PartialEq<&Ver> for Version {
    fn eq(&self, other: &&Ver) -> bool {
        unsafe { alpm_pkg_vercmp(self.0.as_ptr(), other.0.as_ptr()) == 0 }
    }
}

impl PartialEq<str> for Ver {
    fn eq(&self, other: &str) -> bool {
        self.as_str().eq(other)
    }
}

impl PartialEq<String> for Ver {
    fn eq(&self, other: &String) -> bool {
        self.as_str().eq(other)
    }
}

impl PartialEq<&str> for Version {
    fn eq(&self, other: &&str) -> bool {
        self.as_str() == *other
    }
}

impl PartialEq<String> for Version {
    fn eq(&self, other: &String) -> bool {
        self.as_str().eq(other)
    }
}

impl PartialEq<Ver> for str {
    fn eq(&self, other: &Ver) -> bool {
        self == other.as_str()
    }
}

impl PartialEq<&Ver> for String {
    fn eq(&self, other: &&Ver) -> bool {
        self == other.as_str()
    }
}

impl PartialEq<Version> for &str {
    fn eq(&self, other: &Version) -> bool {
        *self == other.as_str()
    }
}

impl PartialEq<Version> for String {
    fn eq(&self, other: &Version) -> bool {
        self == other.as_str()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::Depend;

    #[test]
    fn test_version() {
        assert!(Version::new("0") <= Version::new("1"));
        assert!(Version::new("2") <= Version::new("2"));
        assert!(Version::new("2") > Version::new("1"));
        assert!(Version::new("2") < Version::new("3"));
        assert!(Version::new("2") >= Version::new("1"));
        assert!(Version::new("2") >= Version::new("2"));
        assert!(Version::new("2") == Version::new("2"));

        assert!(Version::new("2") == "2");
        assert!("2" == Version::new("2"));

        let dep1 = Depend::new("foo=20");
        let dep2 = Depend::new("foo=34");

        assert!(Version::new("1").vercmp(&Version::new("2")) == Ordering::Less);
        assert!(Version::new("2-1").vercmp(&Version::new("2")) == Ordering::Equal);

        assert!(dep1.version() != dep2.version());
        assert!(dep1.version() < dep2.version());
        assert!(Version::new("34") == dep2.version().unwrap());
        assert!(Version::new("34") >= dep2.version().unwrap());
        assert!(dep2.version().unwrap() == Version::new("34"));
        assert!(dep2.version().unwrap() >= Version::new("34"));
        assert!(Version::new("1.9.3-2") < Version::new("1.10.2-1"));
    }
}