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
118
use std::convert::From;
use std::error::Error;
use std::result;
use std::fmt;
use snap;
use lz4_compress as lz4;

type Result<T> = result::Result<T, CompressionError>;

#[derive(Debug)]
pub enum CompressionError {
    Snappy(Box<Error>),
    Lz4(String)
}

impl fmt::Display for CompressionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &CompressionError::Snappy(ref err) => write!(f, "Snappy Error: {:?}", err),
            &CompressionError::Lz4(ref s) => write!(f, "Lz4 Error: {:?}", s)
        }
    }
}

impl Error for CompressionError {
    fn description(&self) -> &str {
        let desc = match self {
            &CompressionError::Snappy(ref err) => err.description(),
            &CompressionError::Lz4(ref s) => s.as_str()
        };

        return desc;
    }
}

/// Enum which represents a type of compression. Only non-startup frame's body can be compressen.
#[derive(Debug, PartialEq, Clone, Copy)]
pub enum Compression {
    /// [lz4](https://code.google.com/p/lz4/) compression
    Lz4,
    /// [snappy](https://code.google.com/p/snappy/) compression
    Snappy,
    /// Non compression
    None
}

impl Compression {
    /// It encodes `bytes` basing on type of compression.
    pub fn encode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::encode_lz4(bytes),
            &Compression::Snappy => Compression::encode_snappy(bytes),
            &Compression::None => Ok(bytes)
        };
    }

    /// It decodes `bytes` basing on type of compression.
    pub fn decode(&self, bytes: Vec<u8>) -> Result<Vec<u8>> {
        return match self {
            &Compression::Lz4 => Compression::decode_lz4(bytes),
            &Compression::Snappy => Compression::decode_snappy(bytes),
            &Compression::None => Ok(bytes)
        };
    }

    /// It transforms compression method into a string.
    pub fn into_string(&self) -> Option<String> {
        return match self {
            &Compression::Lz4 => Some(String::from("lz4")),
            &Compression::Snappy => Some(String::from("snappy")),
            &Compression::None => None
        };
    }

    fn encode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut encoder = snap::Encoder::new();
        return encoder
            .compress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn decode_snappy(bytes: Vec<u8>) -> Result<Vec<u8>> {
        let mut decoder = snap::Decoder::new();
        return decoder
            .decompress_vec(bytes.as_slice())
            .map_err(|err| CompressionError::Snappy(Box::new(err)));
    }

    fn encode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        return Ok(lz4::compress(bytes.as_slice()));
    }

    fn decode_lz4(bytes: Vec<u8>) -> Result<Vec<u8>> {
        // skip first 4 bytes in accordance to
        // https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec#L805
        return lz4::decompress(&bytes[4..]).map_err(|err| CompressionError::Lz4(err.description().to_string()));
    }
}

impl From<String> for Compression {
    /// It converts `String` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_string: String) -> Compression {
        return Compression::from(compression_string.as_str());
    }
}

impl<'a> From<&'a str> for Compression {
    /// It converts `str` into `Compression`. If string is neither `lz4` nor `snappy` then
    /// `Compression::None` will be returned
    fn from(compression_str: &'a str) -> Compression {
        return match compression_str {
            "lz4" => Compression::Lz4,
            "snappy" => Compression::Snappy,
            _ => Compression::None
        };
    }
}