1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
//
// utils
//

use std::{num::ParseIntError, string::FromUtf8Error};
use std::fmt::Write;

pub fn byte_array_to_string(bytes: &[u8]) -> Result<String, FromUtf8Error> {
    for (i, b) in bytes.iter().enumerate() {
        if *b == 0u8 {
            return String::from_utf8(bytes[..i].to_vec());
        }
    }
    return String::from_utf8(bytes[..].to_vec());
}

pub fn decode_hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
    (0..s.len())
        .step_by(2)
        .map(|i| u8::from_str_radix(&s[i..i + 2], 16))
        .collect()
}

pub fn md5_to_string(digest: &[u8]) -> String {
    let mut s = String::with_capacity(2* digest.len());
    for byte in digest {
        write!(s, "{:02X}", byte).expect("Couldn't decode hash byte");
    }
    return s;
}

////////////////////////////////// C# Version String
//////////////////////////////////

pub struct Version {
    pub s: String,
}

impl Version {
    pub fn new<T: ToString>(s: T) -> Self {
        Self {
            s: s.to_string()
        }
    }
}

impl PartialEq for Version {
    fn eq(&self, other: &Self) -> bool {
        self.s.eq(&other.s)
    }
}

impl PartialOrd for Version {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        let mut self_split = self.s.split(".").map(|x| i32::from_str_radix(x, 10).unwrap());
        let mut other_split = other.s.split(".").map(|x| i32::from_str_radix(x, 10).unwrap());
        let (major,minor,build,rev) = (self_split.next()?, self_split.next()?, self_split.next()?, self_split.next()?);
        let (omajor,ominor,obuild,orev) = (other_split.next()?, other_split.next()?, other_split.next()?, other_split.next()?);

        let mut is_greater = rev > orev;

        if build > obuild {
            is_greater = true;
        }

        if build < obuild {
            is_greater = false;
        }

        if minor > ominor {
            is_greater = true;
        }

        if minor < ominor {
            is_greater = false;
        }

        if major > omajor {
            is_greater = true;
        }
        
        if major < omajor {
            is_greater = false;
        }

        if self == other {
            return Some(std::cmp::Ordering::Equal);
        }

        if is_greater {
            return Some(std::cmp::Ordering::Greater);
        }

        Some(std::cmp::Ordering::Less)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn version() {
        use crate::utils::Version;

        let a = Version::new("0.6.9.1");
        let b = Version::new("0.6.8.1");
        let c = Version::new("0.6.9.1");
        let d = Version::new("0.7.1.1");

        assert!(a > b);
        assert!(a != b);
        assert!(b < a);
        assert!(a == c);
        assert!(a < d);
        assert!(d >= a);
    }
}