<commit_before>use std::fmt;
use std::hash::Hash;
use std::io::IoError;
use std::from_str::from_str;
use std::str::from_utf8;
use std::collections::{HashMap, HashSet};
use serialize::json;


/// Helper enum that is used in some situations to describe
/// the behavior of arguments in a numeric context.
#[deriving(PartialEq, Eq, Clone, Show)]
pub enum NumericBehavior {
    NonNumeric,
    NumberIsInteger,
    NumberIsFloat,
}


/// An enum of all error kinds.
#[deriving(PartialEq, Eq, Clone, Show)]
pub enum ErrorKind {
    /// The server generated an invalid response.
    ResponseError,
    /// Operation failed because of a type mismatch.
    TypeError,
    /// A script execution was aborted.
    ExecAbortError,
    /// The server cannot response because it's loading a dump.
    BusyLoadingError,
    /// A script that was requested does not actually exist.
    NoScriptError,
    /// An error that was caused because the parameter to the
    /// client were wrong.
    InvalidClientConfig,
    /// A error that is unknown to the library.  This might be used
    /// by future redis servers or custom extensions.
    ExtensionError(String),
    /// An IO error was encountered while talking to the server.
    InternalIoError(IoError),
}


/// Internal low-level redis value enum.
#[deriving(PartialEq, Eq, Clone)]
pub enum Value {
    /// A nil response from the server.
    Nil,
    /// An integer response.  Note that there are a few situations
    /// in which redis actually returns a string for an integer which
    /// is why this library generally treats integers and strings
    /// the same for all numeric responses.
    Int(i64),
    /// An arbitary binary data.
    Data(Vec<u8>),
    /// A bulk response of more data.  This is generally used by redis
    /// to express nested structures.
    Bulk(Vec<Value>),
    /// A status response.
    Status(String),
    /// A status response which represents the string "OK".
    Okay,
}

/// Values are generally not used directly unless you are using the
/// more low level functionality in the library.  For the most part
/// this is hidden with the help of the `FromRedisValue` trait.
///
/// While on the redis protocol there is an error type this is already
/// separated at an early point so the value only holds the remaining
/// types.
impl Value {

    /// Checks if the return value looks like it fulfils the cursor
    /// protocol.  That means the result is a bulk item of length
    /// two with the first one being a cursor and the second a
    /// bulk response.
    pub fn looks_like_cursor(&self) -> bool {
        match self {
            &Bulk(ref items) => {
                if items.len() != 2 {
                    return false;
                }
                match items[0] {
                    Data(_) => {},
                    _ => { return false; }
                };
                match items[1] {
                    Bulk(_) => {},
                    _ => { return false; }
                }
                return true;
            }
            _ => { return false; }
        }
    }
}

impl fmt::Show for Value {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Nil => write!(fmt, "nil"),
            &Int(val) => write!(fmt, "int({})", val),
            &Data(ref val) => {
                match from_utf8(val[]) {
                    Some(x) => write!(fmt, "string-data('{}')", x.escape_default()),
                    None => write!(fmt, "binary-data({})", val),
                }
            },
            &Bulk(ref values) => {
                try!(write!(fmt, "bulk("));
                let mut is_first = true;
                for val in values.iter() {
                    if !is_first {
                        try!(write!(fmt, ", "));
                    }
                    try!(write!(fmt, "{}", val));
                    is_first = false;
                }
                write!(fmt, ")")
            },
            &Okay => write!(fmt, "ok"),
            &Status(ref s) => write!(fmt, "status({})", s),
        }
    }
}


/// Represents a redis error.
#[deriving(PartialEq, Eq, Clone, Show)]
pub struct Error {
    pub kind: ErrorKind,
    pub desc: &'static str,
    pub detail: Option<String>,
}


/// Library generic result type.
pub type RedisResult<T> = Result<T, Error>;


impl Error {

    pub fn simple(kind: ErrorKind, desc: &'static str) -> Error {
        Error {
            kind: kind,
            desc: desc,
            detail: None,
        }
    }
}


/// An info dictionary type.
pub struct InfoDict {
    map: HashMap<String, Value>,
}

/// This type provides convenient access to key/value data returned by
/// the "INFO" command.  It acts like a regular mapping but also has
/// a convenience method `get` which can return data in the appropriate
/// type.
///
/// For instance this can be used to query the server for the role it's
/// in (master, slave) etc:
///
/// ```rust,no_run
/// # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// # let con = client.get_connection().unwrap();
/// let info : redis::InfoDict = redis::cmd("INFO").query(&con).unwrap();
/// let role : String = info.get("role").unwrap();
/// ```
impl InfoDict {
    /// Creates a new info dictionary from a string in the response of
    /// the INFO command.  Each line is a key, value pair with the
    /// key and value separated by a colon (`:`).  Lines starting with a
    /// hash (`#`) are ignored.
    pub fn new(kvpairs: &str) -> InfoDict {
        let mut map = HashMap::new();
        for line in kvpairs.lines_any() {
            if line.len() == 0 || line.starts_with("#") {
                continue;
            }
            let mut p = line.splitn(1, ':');
            let k = unwrap_or!(p.next(), continue).to_string();
            let v = unwrap_or!(p.next(), continue).to_string();
            map.insert(k, Status(v));
        }
        InfoDict { map: map }
    }

    /// Fetches a value by key and converts it into the given type.
    /// Typical types are `String`, `bool` and integer types.
    pub fn get<T: FromRedisValue>(&self, key: &str) -> Option<T> {
        match self.find(&key) {
            Some(ref x) => from_redis_value(*x).ok(),
            None => None,
        }
    }
}

impl<'a> Map<&'a str, Value> for InfoDict {

    fn find<'x>(&'x self, key: &&str) -> Option<&'x Value> {
        self.map.find_equiv(key)
    }

    fn contains_key<'x>(&'x self, key: &&str) -> bool {
        self.find(key).is_some()
    }
}

impl Collection for InfoDict {
    fn len(&self) -> uint {
        self.map.len()
    }
}


/// Used to convert a value into one or multiple redis argument
/// strings.  Most values will produce exactly one item but in
/// some cases it might make sense to produce more than one.
pub trait ToRedisArgs {
    /// This converts the value into a vector of bytes.  Each item
    /// is a single argument.  Most items generate a vector of a
    /// single item.
    ///
    /// The exception to this rule currently are vectors of items.
    fn to_redis_args(&self) -> Vec<Vec<u8>>;

    /// Returns an information about the contained value with regards
    /// to it's numeric behavior in a redis context.  This is used in
    /// some high level concepts to switch between different implementations
    /// of redis functions.
    fn describe_numeric_behavior(&self) -> NumericBehavior {
        NonNumeric
    }

    /// Returns an indiciation if the value contained is exactly one
    /// argument of zero or more.
    fn is_single_arg(&self) -> bool {
        true
    }

    /// This only exists internally as a workaround for the lack of
    /// specialization.
    #[doc(hidden)]
    fn make_arg_vec(items: &[Self]) -> Vec<Vec<u8>> {
        let mut rv = vec![];
        for item in items.iter() {
            rv.push_all(item.to_redis_args().as_slice());
        }
        rv
    }

    #[doc(hidden)]
    fn is_single_vec_arg(items: &[Self]) -> bool {
        items.len() == 1
    }
}


macro_rules! invalid_type_error(
    ($v:expr, $det:expr) => ({
        return Err(Error {
            kind: TypeError,
            desc: "Response was of incompatible type",
            detail: Some(format!("{} (response was {})", $det, $v)),<commit_msg>Some tiny doc adjustments.<commit_after>use std::fmt;
use std::hash::Hash;
use std::io::IoError;
use std::from_str::from_str;
use std::str::from_utf8;
use std::collections::{HashMap, HashSet};
use serialize::json;


/// Helper enum that is used in some situations to describe
/// the behavior of arguments in a numeric context.
#[deriving(PartialEq, Eq, Clone, Show)]
pub enum NumericBehavior {
    NonNumeric,
    NumberIsInteger,
    NumberIsFloat,
}


/// An enum of all error kinds.
#[deriving(PartialEq, Eq, Clone, Show)]
pub enum ErrorKind {
    /// The server generated an invalid response.
    ResponseError,
    /// Operation failed because of a type mismatch.
    TypeError,
    /// A script execution was aborted.
    ExecAbortError,
    /// The server cannot response because it's loading a dump.
    BusyLoadingError,
    /// A script that was requested does not actually exist.
    NoScriptError,
    /// An error that was caused because the parameter to the
    /// client were wrong.
    InvalidClientConfig,
    /// A error that is unknown to the library.  This might be used
    /// by future redis servers or custom extensions.
    ExtensionError(String),
    /// An IO error was encountered while talking to the server.
    InternalIoError(IoError),
}


/// Internal low-level redis value enum.
#[deriving(PartialEq, Eq, Clone)]
pub enum Value {
    /// A nil response from the server.
    Nil,
    /// An integer response.  Note that there are a few situations
    /// in which redis actually returns a string for an integer which
    /// is why this library generally treats integers and strings
    /// the same for all numeric responses.
    Int(i64),
    /// An arbitary binary data.
    Data(Vec<u8>),
    /// A bulk response of more data.  This is generally used by redis
    /// to express nested structures.
    Bulk(Vec<Value>),
    /// A status response.
    Status(String),
    /// A status response which represents the string "OK".
    Okay,
}

/// Values are generally not used directly unless you are using the
/// more low level functionality in the library.  For the most part
/// this is hidden with the help of the `FromRedisValue` trait.
///
/// While on the redis protocol there is an error type this is already
/// separated at an early point so the value only holds the remaining
/// types.
impl Value {

    /// Checks if the return value looks like it fulfils the cursor
    /// protocol.  That means the result is a bulk item of length
    /// two with the first one being a cursor and the second a
    /// bulk response.
    pub fn looks_like_cursor(&self) -> bool {
        match self {
            &Bulk(ref items) => {
                if items.len() != 2 {
                    return false;
                }
                match items[0] {
                    Data(_) => {},
                    _ => { return false; }
                };
                match items[1] {
                    Bulk(_) => {},
                    _ => { return false; }
                }
                return true;
            }
            _ => { return false; }
        }
    }
}

impl fmt::Show for Value {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &Nil => write!(fmt, "nil"),
            &Int(val) => write!(fmt, "int({})", val),
            &Data(ref val) => {
                match from_utf8(val[]) {
                    Some(x) => write!(fmt, "string-data('{}')", x.escape_default()),
                    None => write!(fmt, "binary-data({})", val),
                }
            },
            &Bulk(ref values) => {
                try!(write!(fmt, "bulk("));
                let mut is_first = true;
                for val in values.iter() {
                    if !is_first {
                        try!(write!(fmt, ", "));
                    }
                    try!(write!(fmt, "{}", val));
                    is_first = false;
                }
                write!(fmt, ")")
            },
            &Okay => write!(fmt, "ok"),
            &Status(ref s) => write!(fmt, "status({})", s),
        }
    }
}


/// Represents a redis error.
#[deriving(PartialEq, Eq, Clone, Show)]
pub struct Error {
    pub kind: ErrorKind,
    pub desc: &'static str,
    pub detail: Option<String>,
}


/// Library generic result type.
pub type RedisResult<T> = Result<T, Error>;


impl Error {

    pub fn simple(kind: ErrorKind, desc: &'static str) -> Error {
        Error {
            kind: kind,
            desc: desc,
            detail: None,
        }
    }
}


/// An info dictionary type.
pub struct InfoDict {
    map: HashMap<String, Value>,
}

/// This type provides convenient access to key/value data returned by
/// the "INFO" command.  It acts like a regular mapping but also has
/// a convenience method `get` which can return data in the appropriate
/// type.
///
/// For instance this can be used to query the server for the role it's
/// in (master, slave) etc:
///
/// ```rust,no_run
/// # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// # let con = client.get_connection().unwrap();
/// let info : redis::InfoDict = redis::cmd("INFO").query(&con).unwrap();
/// let role : String = info.get("role").unwrap();
/// ```
impl InfoDict {
    /// Creates a new info dictionary from a string in the response of
    /// the INFO command.  Each line is a key, value pair with the
    /// key and value separated by a colon (`:`).  Lines starting with a
    /// hash (`#`) are ignored.
    pub fn new(kvpairs: &str) -> InfoDict {
        let mut map = HashMap::new();
        for line in kvpairs.lines_any() {
            if line.len() == 0 || line.starts_with("#") {
                continue;
            }
            let mut p = line.splitn(1, ':');
            let k = unwrap_or!(p.next(), continue).to_string();
            let v = unwrap_or!(p.next(), continue).to_string();
            map.insert(k, Status(v));
        }
        InfoDict { map: map }
    }

    /// Fetches a value by key and converts it into the given type.
    /// Typical types are `String`, `bool` and integer types.
    pub fn get<T: FromRedisValue>(&self, key: &str) -> Option<T> {
        match self.find(&key) {
            Some(ref x) => from_redis_value(*x).ok(),
            None => None,
        }
    }
}

impl<'a> Map<&'a str, Value> for InfoDict {

    fn find<'x>(&'x self, key: &&str) -> Option<&'x Value> {
        self.map.find_equiv(key)
    }

    fn contains_key<'x>(&'x self, key: &&str) -> bool {
        self.find(key).is_some()
    }
}

impl Collection for InfoDict {
    fn len(&self) -> uint {
        self.map.len()
    }
}


/// Used to convert a value into one or multiple redis argument
/// strings.  Most values will produce exactly one item but in
/// some cases it might make sense to produce more than one.
pub trait ToRedisArgs {
    /// This converts the value into a vector of bytes.  Each item
    /// is a single argument.  Most items generate a vector of a
    /// single item.
    ///
    /// The exception to this rule currently are vectors of items.
    fn to_redis_args(&self) -> Vec<Vec<u8>>;

    /// Returns an information about the contained value with regards
    /// to it's numeric behavior in a redis context.  This is used in
    /// some high level concepts to switch between different implementations
    /// of redis functions (for instance `INCR` vs `INCRBYFLOAT`).
    fn describe_numeric_behavior(&self) -> NumericBehavior {
        NonNumeric
    }

    /// Returns an indiciation if the value contained is exactly one
    /// argument.  It returns false if it's zero or more than one.  This
    /// is used in some high level functions to intelligently switch
    /// between `GET` and `MGET` variants.
    fn is_single_arg(&self) -> bool {
        true
    }

    /// This only exists internally as a workaround for the lack of
    /// specialization.
    #[doc(hidden)]
    fn make_arg_vec(items: &[Self]) -> Vec<Vec<u8>> {
        let mut rv = vec![];
        for item in items.iter() {
            rv.push_all(item.to_redis_args().as_slice());
        }
        rv
    }

    #[doc(hidden)]
    fn is_single_vec_arg(items: &[Self]) -> bool {
        items.len() == 1
    }
}


macro_rules! invalid_type_error(
    ($v:expr, $det:expr) => ({
        return Err(Error {
            kind: TypeError,
            desc: "Response was of incompatible type",
            detail: Some(format!("{} (response was {})", $det, $v)),
{'fraction_non_alphanumeric': 0.129820587709364, 'fraction_numerical': 0.0022650056625141564, 'mean_word_length': 1.969557522123894, 'pattern_counts': {'":': 0, '<': 37, '<?xml version=': 0, '>': 97, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6136292', 'n_tokens_mistral': 4862, 'n_tokens_neox': 4740, 'n_words': 1747}