rdb 0.3.0

Fast and efficient RDB parsing utility
Documentation
#![allow(unused_must_use)]
use indexmap::IndexMap;

use super::write_str;
use crate::formatter::Formatter;
use std::io;
use std::io::Write;
use std::path::PathBuf;

pub struct Protocol {
    out: Box<dyn Write + 'static>,
    last_expiry: Option<u64>,
}

impl Protocol {
    pub fn new(file_path: Option<PathBuf>) -> Protocol {
        let out: Box<dyn Write> = match file_path {
            Some(path) => match std::fs::File::create(path) {
                Ok(file) => Box::new(file),
                Err(_) => Box::new(io::stdout()),
            },
            None => Box::new(io::stdout()),
        };

        Protocol {
            out,
            last_expiry: None,
        }
    }
}

impl Protocol {
    fn emit(&mut self, args: Vec<&[u8]>) {
        write_str(&mut self.out, "*");
        self.out.write_all(args.len().to_string().as_bytes());
        write_str(&mut self.out, "\r\n");
        for arg in &args {
            write_str(&mut self.out, "$");
            self.out.write_all(arg.len().to_string().as_bytes());
            write_str(&mut self.out, "\r\n");
            self.out.write_all(arg);
            write_str(&mut self.out, "\r\n");
        }
    }

    fn pre_expire(&mut self, expiry: &Option<u64>) {
        self.last_expiry = *expiry;
    }

    fn post_expire(&mut self, key: &[u8]) {
        if let Some(expire) = self.last_expiry {
            let expire = expire.to_string();
            self.emit(vec!["PEXPIREAT".as_bytes(), key, expire.as_bytes()]);
            self.last_expiry = None;
        }
    }

    fn set(&mut self, key: &[u8], value: &[u8], expiry: &Option<u64>) {
        self.pre_expire(expiry);
        self.emit(vec!["SET".as_bytes(), key, value]);
        self.post_expire(key);
    }

    fn start_hash(&mut self, expiry: &Option<u64>) {
        self.pre_expire(expiry);
    }
    fn end_hash(&mut self, key: &[u8]) {
        self.post_expire(key);
    }
    fn hash_element(&mut self, key: &[u8], field: &[u8], value: &[u8]) {
        self.emit(vec!["HSET".as_bytes(), key, field, value]);
    }

    fn start_set(&mut self, expiry: &Option<u64>) {
        self.pre_expire(expiry);
    }
    fn end_set(&mut self, key: &[u8]) {
        self.post_expire(key);
    }
    fn set_element(&mut self, key: &[u8], member: &[u8]) {
        self.emit(vec!["SADD".as_bytes(), key, member]);
    }

    fn start_list(&mut self, expiry: &Option<u64>) {
        self.pre_expire(expiry);
    }
    fn end_list(&mut self, key: &[u8]) {
        self.post_expire(key);
    }
    fn list_element(&mut self, key: &[u8], value: &[u8]) {
        self.emit(vec!["RPUSH".as_bytes(), key, value]);
    }

    fn start_sorted_set(&mut self, expiry: &Option<u64>) {
        self.pre_expire(expiry);
    }
    fn end_sorted_set(&mut self, key: &[u8]) {
        self.post_expire(key);
    }
    fn sorted_set_element(&mut self, key: &[u8], score: f64, member: &[u8]) {
        let score = score.to_string();
        self.emit(vec!["ZADD".as_bytes(), key, score.as_bytes(), member]);
    }
}

impl Formatter for Protocol {
    fn string(&mut self, key: &[u8], value: &[u8], _expiry: &Option<u64>) {
        self.set(key, value, _expiry);
    }

    fn hash(&mut self, key: &[u8], values: &IndexMap<Vec<u8>, Vec<u8>>, expiry: &Option<u64>) {
        self.start_hash(expiry);
        for (field, value) in values {
            self.hash_element(key, field, value);
        }
        self.end_hash(key);
    }

    fn set(&mut self, key: &[u8], values: &[Vec<u8>], expiry: &Option<u64>) {
        self.start_set(expiry);
        for value in values {
            self.set_element(key, value);
        }
        self.end_set(key);
    }

    fn list(&mut self, key: &[u8], values: &[Vec<u8>], expiry: &Option<u64>) {
        self.start_list(expiry);
        for value in values {
            self.list_element(key, value);
        }
        self.end_list(key);
    }

    fn sorted_set(&mut self, key: &[u8], values: &[(f64, Vec<u8>)], expiry: &Option<u64>) {
        self.start_sorted_set(expiry);
        for (score, member) in values {
            self.sorted_set_element(key, *score, member);
        }
        self.end_sorted_set(key);
    }

    fn start_database(&mut self, db_number: u32) {
        let db = db_number.to_string();
        self.emit(vec!["SELECT".as_bytes(), db.as_bytes()])
    }
}