<commit_before>use repo::Workspace;

use std::str;
use std::process::{Command, ExitStatus};
use std::sync::Mutex;

pub struct TastingResult {
    pub commit_id: String,
    pub build: bool,
    pub bench: bool,
}

fn benchmark(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("bench")
        .arg("--release")
        .status()
        .expect("Failed to execute 'cargo bench'!")
}

fn build(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("build")
        .arg("--release")
        .status()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(wsl: &Mutex<Workspace>, id: &String) -> TastingResult {
    println!("Tasting commit {}", id);
    let ws = wsl.lock().unwrap();
    ws.checkout_commit(id);

    TastingResult {
        commit_id: id.clone(),
        build: update(&ws.path).success() && build(&ws.path).success(),
        bench: benchmark(&ws.path).success(),
    }
}

fn update(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("update")
        .status()
        .expect("Failed to execute 'cargo update'!")
}
<commit_msg>`cargo bench` doesn't take --release<commit_after>use repo::Workspace;

use std::str;
use std::process::{Command, ExitStatus};
use std::sync::Mutex;

pub struct TastingResult {
    pub commit_id: String,
    pub build: bool,
    pub bench: bool,
}

fn benchmark(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("bench")
        .status()
        .expect("Failed to execute 'cargo bench'!")
}

fn build(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("build")
        .arg("--release")
        .status()
        .expect("Failed to execute 'cargo build'!")
}

pub fn taste_commit(wsl: &Mutex<Workspace>, id: &String) -> TastingResult {
    println!("Tasting commit {}", id);
    let ws = wsl.lock().unwrap();
    ws.checkout_commit(id);

    TastingResult {
        commit_id: id.clone(),
        build: update(&ws.path).success() && build(&ws.path).success(),
        bench: benchmark(&ws.path).success(),
    }
}

fn update(workdir: &str) -> ExitStatus {
    Command::new("cargo")
        .current_dir(workdir)
        .arg("update")
        .status()
        .expect("Failed to execute 'cargo update'!")
}
<|endoftext|>


<commit_before>use sodiumoxide::crypto::hash::sha256;
use sodiumoxide::crypto::sign;
use sodiumoxide::randombytes;
use bs58;

mod error;
pub use self::error::*;

const PUB_ADDRESS_PREFIX: &str = "GOD";
const PRIV_BUF_PREFIX: u8 = 0x01;
const PUB_BUF_PREFIX: u8 = 0x02;

pub trait Wif<T> {
    fn from_wif(s: &str) -> Result<T, WifError>;
    fn to_wif(&self) -> Box<str>;
}

#[derive(Debug, Clone, PartialEq)]
pub struct PublicKey {
    key: sign::PublicKey
}

impl PublicKey {
    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        self.key.as_ref()
    }

    #[inline]
    pub fn verify(&self, msg: &[u8], sig: &sign::Signature) -> bool {
        sign::verify_detached(sig, msg, &self.key)
    }

    #[inline]
    pub fn from_bytes(bytes: &[u8]) -> Option<PublicKey> {
        let key = sign::PublicKey::from_slice(bytes)?;
        Some(PublicKey { key })
    }
}

impl Wif<PublicKey> for PublicKey {
    fn from_wif(s: &str) -> Result<PublicKey, WifError> {
        if s.len() < 3 || &s[0..3] != PUB_ADDRESS_PREFIX {
            return Err(WifError::new(WifErrorKind::InvalidPrefix))
        }
        let raw = match bs58::decode(&s[3..]).into_vec() {
            Ok(bytes) => bytes,
            Err(_) => return Err(WifError::new(WifErrorKind::InvalidBs58Encoding))
        };
        if raw.len() != 37 {
            return Err(WifError::new(WifErrorKind::InvalidLen))
        } else if raw[0] != PUB_BUF_PREFIX {
            return Err(WifError::new(WifErrorKind::InvalidPrefix))
        }

        let prefixed_key = &raw[0..raw.len() - 4];
        {
            let checksum_a = &raw[raw.len() - 4 .. raw.len()];
            let checksum_b = &double_sha256(prefixed_key)[0..4];
            if checksum_a != checksum_b {
                return Err(WifError::new(WifErrorKind::InvalidChecksum))
            }
        }

        let key = &prefixed_key[1 .. prefixed_key.len()];
        Ok(PublicKey {
            key: sign::PublicKey::from_slice(key).unwrap()
        })
    }

    fn to_wif(&self) -> Box<str> {
        let mut buf: Vec<u8> = Vec::<u8>::with_capacity(37);
        buf.push(PUB_BUF_PREFIX);
        buf.extend_from_slice(self.key.as_ref());

        let checksum = &double_sha256(&buf)[0..4];
        buf.extend_from_slice(checksum);

        let mut s = bs58::encode(buf).into_string();
        s.insert_str(0, PUB_ADDRESS_PREFIX);
        s.into_boxed_str()
    }
}

#[derive(Debug, Clone)]
pub struct PrivateKey {
    seed: sign::Seed,
    key: sign::SecretKey
}

impl PrivateKey {
    #[inline]
    pub fn sign(&self, msg: &[u8]) -> sign::Signature {
        sign::sign_detached(msg, &self.key)
    }
}

impl Wif<KeyPair> for PrivateKey {
    fn from_wif(s: &str) -> Result<KeyPair, WifError> {
        let raw = match bs58::decode(s).into_vec() {
            Ok(bytes) => bytes,
            Err(_) => return Err(WifError::new(WifErrorKind::InvalidBs58Encoding))
        };
        if raw.len() != 37 {
            return Err(WifError::new(WifErrorKind::InvalidLen))
        } else if raw[0] != PRIV_BUF_PREFIX {
            return Err(WifError::new(WifErrorKind::InvalidPrefix))
        }

        let key = &raw[0..raw.len() - 4];
        {
            let checksum_a = &raw[raw.len() - 4 .. raw.len()];
            let checksum_b = &double_sha256(key)[0..4];
            if checksum_a != checksum_b {
                return Err(WifError::new(WifErrorKind::InvalidChecksum))
            }
        }

        let seed = sign::Seed::from_slice(&key[1..]).unwrap();
        let (pk, sk) = sign::keypair_from_seed(&seed);
        Ok(KeyPair(PublicKey {
            key: pk
        }, PrivateKey {
            seed,
            key: sk
        }))
    }

    fn to_wif(&self) -> Box<str> {
        let mut buf = Vec::<u8>::with_capacity(37);
        buf.push(PRIV_BUF_PREFIX);
        buf.extend_from_slice(&self.seed.0);

        let checksum = &double_sha256(&buf)[0..4];
        buf.extend_from_slice(checksum);

        bs58::encode(buf).into_string().into_boxed_str()
    }
}

#[derive(Debug, Clone)]
pub struct KeyPair(pub PublicKey, pub PrivateKey);

impl KeyPair {
    #[inline]
    pub fn sign(&self, msg: &[u8]) -> SigPair {
        SigPair {
            pub_key: self.0.clone(),
            signature: self.1.sign(msg)
        }
    }

    #[inline]
    pub fn verify(&self, msg: &[u8], sig: &sign::Signature) -> bool {
        PublicKey::verify(&self.0, msg, sig)
    }

    pub fn gen_keypair() -> KeyPair {
        let mut raw_seed: [u8; sign::SEEDBYTES] = [0; sign::SEEDBYTES];
        randombytes::randombytes_into(&mut raw_seed);
        let seed = sign::Seed::from_slice(&raw_seed).unwrap();
        let (pk, sk) = sign::keypair_from_seed(&seed);
        KeyPair(PublicKey {
            key: pk
        }, PrivateKey {
            seed,
            key: sk
        })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct SigPair {
    pub pub_key: PublicKey,
    pub signature: sign::Signature
}

impl SigPair {
    #[inline]
    pub fn verify(&self, msg: &[u8]) -> bool {
        sign::verify_detached(&self.signature, msg, &self.pub_key.key)
    }
}

#[inline]
pub fn double_sha256(buf: &[u8]) -> sha256::Digest {
    sha256::hash(sha256::hash(buf).as_ref())
}

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

    #[test]
    fn test_create_and_recover_keys() {
        let kp = KeyPair::gen_keypair();

        let pk = &*kp.0.to_wif();
        assert_eq!(&*PublicKey::from_wif(pk).unwrap().to_wif(), pk);

        let sk = &*kp.1.to_wif();
        assert_eq!(&*PrivateKey::from_wif(sk).unwrap().1.to_wif(), sk);
    }

    #[test]
    fn test_import_keys_from_wif() {
        let kp = PrivateKey::from_wif("3GAD3otqozDorfu1iDpMQJ1gzWp8PRFEjVHZivZdedKW3i3KtM").unwrap();
        assert_eq!(&*kp.1.to_wif(), "3GAD3otqozDorfu1iDpMQJ1gzWp8PRFEjVHZivZdedKW3i3KtM");
        assert_eq!(&*kp.0.to_wif(), "GOD52QZDBUStV5CudxvKf6bPsQeN7oeKTkEm2nAU1vAUqNVexGTb8");
    }

    #[test]
    fn test_invalid_prefix() {
        let mut bytes = bs58::decode("3GAD3otqozDorfu1iDpMQJ1gzWp8PRFEjVHZivZdedKW3i3KtM").into_vec().unwrap();
        bytes[0] = 255;
        let wif = bs58::encode(bytes).into_string();
        assert_eq!(PrivateKey::from_wif(&wif).unwrap_err().kind, WifErrorKind::InvalidPrefix);

        let mut bytes = bs58::decode("52QZDBUStV5CudxvKf6bPsQeN7oeKTkEm2nAU1vAUqNVexGTb8").into_vec().unwrap();
        bytes[0] = 255;
        let mut wif = bs58::encode(bytes).into_string();
        wif.insert_str(0, PUB_ADDRESS_PREFIX);
        assert_eq!(PublicKey::from_wif(&wif).unwrap_err().kind, WifErrorKind::InvalidPrefix);
    }

    #[test]
    fn test_invalid_checksum() {
        let mut bytes = bs58::decode("3GAD3otqozDorfu1iDpMQJ1gzWp8PRFEjVHZivZdedKW3i3KtM").into_vec().unwrap();
        let len = bytes.len();
        for i in 1..5 {
            bytes[len - i] = 0;
        }
        let wif = bs58::encode(bytes).into_string();
        assert_eq!(PrivateKey::from_wif(&wif).unwrap_err().kind, WifErrorKind::InvalidChecksum);

        let mut bytes = bs58::decode("52QZDBUStV5CudxvKf6bPsQeN7oeKTkEm2nAU1vAUqNVexGTb8").into_vec().unwrap();
        let len = bytes.len();
        for i in 1..5 {
            bytes[len - i] = 0;
        }
        let mut wif = bs58::encode(bytes).into_string();
        wif.insert_str(0, PUB_ADDRESS_PREFIX);
        assert_eq!(PublicKey::from_wif(&wif).unwrap_err().kind, WifErrorKind::InvalidChecksum);
    }

    #[test]
    fn test_sign_message() {
        let msg = "Hello world!".as_bytes();
        let kp = KeyPair::gen_keypair();

        let sig = &kp.1.sign(msg);
        assert!(kp.0.verify(msg, sig));

        let pair = SigPair {
            pub_key: kp.0,
            signature: *sig
        };
        assert!(pair.verify(msg));

        // Test bad keys
        let kp = KeyPair::gen_keypair();
        assert!(!kp.verify(msg, sig));
    }
}
<commit_msg>Add as_bytes function to PrivateKey<commit_after>use sodiumoxide::crypto::hash::sha256;
use sodiumoxide::crypto::sign;
use sodiumoxide::randombytes;
use bs58;

mod error;
pub use self::error::*;

const PUB_ADDRESS_PREFIX: &str = "GOD";
const PRIV_BUF_PREFIX: u8 = 0x01;
const PUB_BUF_PREFIX: u8 = 0x02;

pub trait Wif<T> {
    fn from_wif(s: &str) -> Result<T, WifError>;
    fn to_wif(&self) -> Box<str>;
}

#[derive(Debug, Clone, PartialEq)]
pub struct PublicKey {
    key: sign::PublicKey
}

impl PublicKey {
    #[inline]
    pub fn as_bytes(&self) -> &[u8] {
        self.key.as_ref()
    }

    #[inline]
    pub fn verify(&self, msg: &[u8], sig: &sign::Signature) -> bool {
        sign::verify_detached(sig, msg, &self.key)
    }

    #[inline]
    pub fn from_bytes(bytes: &[u8]) -> Option<PublicKey> {
        let key = sign::PublicKey::from_slice(bytes)?;
        Some(PublicKey { key })
    }
}

impl Wif<PublicKey> for PublicKey {
    fn from_wif(s: &str) -> Result<PublicKey, WifError> {
        if s.len() < 3 || &s[0..3] != PUB_ADDRESS_PREFIX {
            return Err(WifError::new(WifErrorKind::InvalidPrefix))
        }
        let raw = match bs58::decode(&s[3..]).into_vec() {
            Ok(bytes) => bytes,
            Err(_) => return Err(WifError::new(WifErrorKind::InvalidBs58Encoding))
        };
        if raw.len() != 37 {
            return Err(WifError::new(WifErrorKind::InvalidLen))
        } else if raw[0] != PUB_BUF_PREFIX {
            return Err(WifError::new(WifErrorKind::InvalidPrefix))
        }

        let prefixed_key = &raw[0..raw.len() - 4];
        {
            let checksum_a = &raw[raw.len() - 4 .. raw.len()];
            let checksum_b = &double_sha256(prefixed_key)[0..4];
            if checksum_a != checksum_b {
                return Err(WifError::new(WifErrorKind::InvalidChecksum))
            }
        }

        let key = &prefixed_key[1 .. prefixed_key.len()];
        Ok(PublicKey {
            key: sign::PublicKey::from_slice(key).unwrap()
        })
    }

    fn to_wif(&self) -> Box<str> {
        let mut buf: Vec<u8> = Vec::<u8>::with_capacity(37);
        buf.push(PUB_BUF_PREFIX);
        buf.extend_from_slice(self.key.as_ref());

        let checksum = &double_sha256(&buf)[0..4];
        buf.extend_from_slice(checksum);

        let mut s = bs58::encode(buf).into_string();
        s.insert_str(0, PUB_ADDRESS_PREFIX);
        s.into_boxed_str()
    }
}

#[derive(Debug, Clone)]
pub struct PrivateKey {
    seed: sign::Seed,
    key: sign::SecretKey
}

impl PrivateKey {
    pub fn as_bytes(&self) -> ([u8; sign::SEEDBYTES], [u8; sign::SECRETKEYBYTES]) {
        (self.seed.0, self.key.0)
    }

    #[inline]
    pub fn sign(&self, msg: &[u8]) -> sign::Signature {
        sign::sign_detached(msg, &self.key)
    }
}

impl Wif<KeyPair> for PrivateKey {
    fn from_wif(s: &str) -> Result<KeyPair, WifError> {
        let raw = match bs58::decode(s).into_vec() {
            Ok(bytes) => bytes,
            Err(_) => return Err(WifError::new(WifErrorKind::InvalidBs58Encoding))
        };
        if raw.len() != 37 {
            return Err(WifError::new(WifErrorKind::InvalidLen))
        } else if raw[0] != PRIV_BUF_PREFIX {
            return Err(WifError::new(WifErrorKind::InvalidPrefix))
        }

        let key = &raw[0..raw.len() - 4];
        {
            let checksum_a = &raw[raw.len() - 4 .. raw.len()];
            let checksum_b = &double_sha256(key)[0..4];
            if checksum_a != checksum_b {
                return Err(WifError::new(WifErrorKind::InvalidChecksum))
            }
        }

        let seed = sign::Seed::from_slice(&key[1..]).unwrap();
        let (pk, sk) = sign::keypair_from_seed(&seed);
        Ok(KeyPair(PublicKey {
            key: pk
        }, PrivateKey {
            seed,
            key: sk
        }))
    }

    fn to_wif(&self) -> Box<str> {
        let mut buf = Vec::<u8>::with_capacity(37);
        buf.push(PRIV_BUF_PREFIX);
        buf.extend_from_slice(&self.seed.0);

        let checksum = &double_sha256(&buf)[0..4];
        buf.extend_from_slice(checksum);

        bs58::encode(buf).into_string().into_boxed_str()
    }
}

#[derive(Debug, Clone)]
pub struct KeyPair(pub PublicKey, pub PrivateKey);

impl KeyPair {
    #[inline]
    pub fn sign(&self, msg: &[u8]) -> SigPair {
        SigPair {
            pub_key: self.0.clone(),
            signature: self.1.sign(msg)
        }
    }

    #[inline]
    pub fn verify(&self, msg: &[u8], sig: &sign::Signature) -> bool {
        PublicKey::verify(&self.0, msg, sig)
    }

    pub fn gen_keypair() -> KeyPair {
        let mut raw_seed: [u8; sign::SEEDBYTES] = [0; sign::SEEDBYTES];
        randombytes::randombytes_into(&mut raw_seed);
        let seed = sign::Seed::from_slice(&raw_seed).unwrap();
        let (pk, sk) = sign::keypair_from_seed(&seed);
        KeyPair(PublicKey {
            key: pk
        }, PrivateKey {
            seed,
            key: sk
        })
    }
}

#[derive(Debug, Clone, PartialEq)]
pub struct SigPair {
    pub pub_key: PublicKey,
    pub signature: sign::Signature
}

impl SigPair {
    #[inline]
    pub fn verify(&self, msg: &[u8]) -> bool {
        sign::verify_detached(&self.signature, msg, &self.pub_key.key)
    }
}

#[inline]
pub fn double_sha256(buf: &[u8]) -> sha256::Digest {
    sha256::hash(sha256::hash(buf).as_ref())
}

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

    #[test]
    fn test_create_and_recover_keys() {
        let kp = KeyPair::gen_keypair();

        let pk = &*kp.0.to_wif();
        assert_eq!(&*PublicKey::from_wif(pk).unwrap().to_wif(), pk);

        let sk = &*kp.1.to_wif();
        assert_eq!(&*PrivateKey::from_wif(sk).unwrap().1.to_wif(), sk);
    }

    #[test]
    fn test_import_keys_from_wif() {
        let kp = PrivateKey::from_wif("3GAD3otqozDorfu1iDpMQJ1gzWp8PRFEjVHZivZdedKW3i3KtM").unwrap();
        assert_eq!(&*kp.1.to_wif(), "3GAD3otqozDorfu1iDpMQJ1gzWp8PRFEjVHZivZdedKW3i3KtM");
        assert_eq!(&*kp.0.to_wif(), "GOD52QZDBUStV5CudxvKf6bPsQeN7oeKTkEm2nAU1vAUqNVexGTb8");
    }

    #[test]
    fn test_invalid_prefix() {
        let mut bytes = bs58::decode("3GAD3otqozDorfu1iDpMQJ1gzWp8PRFEjVHZivZdedKW3i3KtM").into_vec().unwrap();
        bytes[0] = 255;
        let wif = bs58::encode(bytes).into_string();
        assert_eq!(PrivateKey::from_wif(&wif).unwrap_err().kind, WifErrorKind::InvalidPrefix);

        let mut bytes = bs58::decode("52QZDBUStV5CudxvKf6bPsQeN7oeKTkEm2nAU1vAUqNVexGTb8").into_vec().unwrap();
        bytes[0] = 255;
        let mut wif = bs58::encode(bytes).into_string();
        wif.insert_str(0, PUB_ADDRESS_PREFIX);
        assert_eq!(PublicKey::from_wif(&wif).unwrap_err().kind, WifErrorKind::InvalidPrefix);
    }

    #[test]
    fn test_invalid_checksum() {
        let mut bytes = bs58::decode("3GAD3otqozDorfu1iDpMQJ1gzWp8PRFEjVHZivZdedKW3i3KtM").into_vec().unwrap();
        let len = bytes.len();
        for i in 1..5 {
            bytes[len - i] = 0;
        }
        let wif = bs58::encode(bytes).into_string();
        assert_eq!(PrivateKey::from_wif(&wif).unwrap_err().kind, WifErrorKind::InvalidChecksum);

        let mut bytes = bs58::decode("52QZDBUStV5CudxvKf6bPsQeN7oeKTkEm2nAU1vAUqNVexGTb8").into_vec().unwrap();
        let len = bytes.len();
        for i in 1..5 {
            bytes[len - i] = 0;
        }
        let mut wif = bs58::encode(bytes).into_string();
        wif.insert_str(0, PUB_ADDRESS_PREFIX);
        assert_eq!(PublicKey::from_wif(&wif).unwrap_err().kind, WifErrorKind::InvalidChecksum);
    }

    #[test]
    fn test_sign_message() {
        let msg = "Hello world!".as_bytes();
        let kp = KeyPair::gen_keypair();

        let sig = &kp.1.sign(msg);
        assert!(kp.0.verify(msg, sig));

        let pair = SigPair {
            pub_key: kp.0,
            signature: *sig
        };
        assert!(pair.verify(msg));

        // Test bad keys
        let kp = KeyPair::gen_keypair();
        assert!(!kp.verify(msg, sig));
    }
}
<|endoftext|>


<commit_before>
// A regex "token" represents a single character in the text
// It can be one of:
//     a character literal
//     a character class
//     a any character (.)
#[derive(PartialEq, Debug)]
pub enum Token {
    Literal(char),
    Class(Vec<char>),
    Any
}

#[derive(PartialEq, Debug)]
pub struct Multiplicity {
    minimum: usize,
    maximum: usize
}

// A regex "expression" represents a token with multiplicity, or a position
// Eg. `[abc]*`, `[abc]{1,3}`, `^`
#[derive(PartialEq, Debug)]
pub enum Expression {
    BeginPosition,
    EndPosition,
    Token(Token, Multiplicity)
}

pub fn parse_expressions(text: &str) -> Result<Vec<Expression>,&str> {
    let mut result = Vec::<Expression>::new();

    for c in text.chars() {
        match c {
            '?' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: 0,
                                            maximum: 1
                                        }));
                            },
                            _ => { return Err("invalid token before the `?` metacharacter"); }
                        }},
                    None => { return Err("no token before `?` metacharacter"); }

                }
            },
            c => { // Not a meta-character, treat as a literal
                result.push(Expression::Token(
                    Token::Literal(c),
                    Multiplicity {
                        minimum: 1,
                        maximum: 1
                    }
                ));
            }

        }
    }

    Ok(result)
}

#[cfg(test)]
mod expression_spec {
    use super::{Token, Multiplicity, Expression, parse_expressions};

    #[test]
    fn parses_single_literal() {
        assert_eq!(vec![
                   Expression::Token(
                       Token::Literal('a'),
                       Multiplicity {
                           minimum: 1,
                           maximum: 1
                       }
                   )
        ], parse_expressions("a").unwrap())
    }

    #[test]
    fn parses_many_literals() {
        assert_eq!(6, parse_expressions("abcxyz").unwrap().len());
    }

    #[test]
    fn handles_optional_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: 0,
                       maximum: 1
                   })
        ], parse_expressions("a?").unwrap());

        assert!(parse_expressions("?").is_err());
        assert!(parse_expressions("??").is_err());
    }
}

<commit_msg>add ability to parse "+" metacharacter<commit_after>
// A regex "token" represents a single character in the text
// It can be one of:
//     a character literal
//     a character class
//     a any character (.)
#[derive(PartialEq, Debug)]
pub enum Token {
    Literal(char),
    Class(Vec<char>),
    Any
}

// Represents an upper and lower bound on the multiplicity of a regex token
// Eg. `(Some(0), Some(1))` corresponds to `?` and `(Some(0), None)` corresponds
// to `*`
#[derive(PartialEq, Debug)]
pub struct Multiplicity {
    minimum: Option<usize>,
    maximum: Option<usize>
}

// A regex "expression" represents a token with multiplicity, or a position
// Eg. `[abc]*`, `[abc]{1,3}`, `^`
#[derive(PartialEq, Debug)]
pub enum Expression {
    BeginPosition,
    EndPosition,
    Token(Token, Multiplicity)
}

pub fn parse_expressions(text: &str) -> Result<Vec<Expression>,&str> {
    let mut result = Vec::<Expression>::new();

    for c in text.chars() {
        match c {
            '?' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(0),
                                            maximum: Some(1)
                                        }));
                            },
                            _ => { return Err("invalid token before `?` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `?` metacharacter"); }
                }
            },
            '+' => {
                match result.pop() {
                    Some(value) => {
                        match value {
                            Expression::Token(token, multiplicity) => {
                                result.push(Expression::Token(
                                        token,
                                        Multiplicity {
                                            minimum: Some(1),
                                            maximum: None
                                        }));
                            },
                            _ => { return Err("invalid token before `+` metacharacter"); }
                        }
                    },
                    None => { return Err("no token before `+` metacharacter"); }
                }
            },
            c => { // Not a meta-character, treat as a literal
                result.push(Expression::Token(
                    Token::Literal(c),
                    Multiplicity {
                        minimum: Some(1),
                        maximum: Some(1)
                    }
                ));
            }

        }
    }

    Ok(result)
}

#[cfg(test)]
mod expression_spec {
    use super::{Token, Multiplicity, Expression, parse_expressions};

    #[test]
    fn parses_single_literal() {
        assert_eq!(vec![
                   Expression::Token(
                       Token::Literal('a'),
                       Multiplicity {
                           minimum: Some(1),
                           maximum: Some(1)
                       }
                   )
        ], parse_expressions("a").unwrap())
    }

    #[test]
    fn parses_many_literals() {
        assert_eq!(6, parse_expressions("abcxyz").unwrap().len());
    }

    #[test]
    fn handles_optional_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(0),
                       maximum: Some(1)
                   })
        ], parse_expressions("a?").unwrap());

        assert!(parse_expressions("?").is_err());
        assert!(parse_expressions("??").is_err());
    }

    #[test]
    fn handles_one_or_more_multiplicity() {
        assert_eq!(vec![
                   Expression::Token(Token::Literal('a'),
                   Multiplicity {
                       minimum: Some(1),
                       maximum: None
                   })
        ], parse_expressions("a+").unwrap());

        assert!(parse_expressions("+").is_err());
        assert!(parse_expressions("++").is_err());
    }
}

<|endoftext|>


<commit_before>use rgtk::*;
use rustc_serialize::{Encodable, json};
use std::env;
use std::collections::HashSet;
use std::io::{Read, Write};
use std::fs;
use std::fs::PathExt;
use std::path::Path;

pub static DATA_DIR : &'static str = ".soak";
pub static CONFIG_FILE : &'static str = ".soakrc";
pub static CONFIG_CONTENT : &'static str = include_str!("../resources/soakrc");
pub static PREFS_FILE : &'static str = "prefs.json";
pub static NO_WINDOW_FLAG : &'static str = "-nw";

struct Resource {
    pub path: &'static [&'static str],
    pub data: &'static str,
}
pub static DATA_CONTENT : &'static [Resource] = &[
    Resource{path: &["after", "syntax", "rust.vim"],
             data: include_str!("../resources/soak/after/syntax/rust.vim")},

    Resource{path: &["autoload", "rust.vim"],
             data: include_str!("../resources/soak/autoload/rust.vim")},
    Resource{path: &["autoload", "paste.vim"],
             data: include_str!("../resources/soak/autoload/paste.vim")},

    Resource{path: &["compiler", "rustc.vim"],
             data: include_str!("../resources/soak/compiler/rustc.vim")},
    Resource{path: &["compiler", "cargo.vim"],
             data: include_str!("../resources/soak/compiler/cargo.vim")},

    Resource{path: &["doc", "rust.txt"],
             data: include_str!("../resources/soak/doc/rust.txt")},

    Resource{path: &["ftdetect", "rust.vim"],
             data: include_str!("../resources/soak/ftdetect/rust.vim")},

    Resource{path: &["ftplugin", "rust.vim"],
             data: include_str!("../resources/soak/ftplugin/rust.vim")},
    Resource{path: &["ftplugin", "c.vim"],
             data: include_str!("../resources/soak/ftplugin/c.vim")},

    Resource{path: &["indent", "rust.vim"],
             data: include_str!("../resources/soak/indent/rust.vim")},
    Resource{path: &["indent", "c.vim"],
             data: include_str!("../resources/soak/indent/c.vim")},

    Resource{path: &["plugin", "eunuch.vim"],
             data: include_str!("../resources/soak/plugin/eunuch.vim")},
    Resource{path: &["plugin", "rust.vim"],
             data: include_str!("../resources/soak/plugin/rust.vim")},

    Resource{path: &["syntax", "c.vim"],
             data: include_str!("../resources/soak/syntax/c.vim")},
    Resource{path: &["syntax", "nosyntax.vim"],
             data: include_str!("../resources/soak/syntax/nosyntax.vim")},
    Resource{path: &["syntax", "rust.vim"],
             data: include_str!("../resources/soak/syntax/rust.vim")},
    Resource{path: &["syntax", "syncolor.vim"],
             data: include_str!("../resources/soak/syntax/syncolor.vim")},
    Resource{path: &["syntax", "synload.vim"],
             data: include_str!("../resources/soak/syntax/synload.vim")},
    Resource{path: &["syntax", "syntax.vim"],
             data: include_str!("../resources/soak/syntax/syntax.vim")},

    Resource{path: &["syntax_checkers", "rust", "rustc.vim"],
             data: include_str!("../resources/soak/syntax_checkers/rust/rustc.vim")},

    Resource{path: &["evim.vim"],
             data: include_str!("../resources/soak/evim.vim")},
    Resource{path: &["filetype.vim"],
             data: include_str!("../resources/soak/filetype.vim")},
    Resource{path: &["mswin.vim"],
             data: include_str!("../resources/soak/mswin.vim")}
];

pub struct State<'a> {
    pub projects: HashSet<String>,
    pub expansions: HashSet<String>,
    pub selection: Option<String>,
    pub window: &'a gtk::Window,
    pub tree_store: &'a gtk::TreeStore,
    pub tree_model: &'a gtk::TreeModel,
    pub tree_selection: &'a gtk::TreeSelection,
    pub rename_button: &'a gtk::Button,
    pub remove_button: &'a gtk::Button,
    pub is_refreshing_tree: bool
}

#[derive(RustcDecodable, RustcEncodable)]
struct Prefs {
    projects: Vec<String>,
    expansions: Vec<String>,
    selection: Option<String>
}

pub fn get_home_dir() -> String {
    if let Some(old_path) = env::home_dir() {
        if let Some(path) = old_path.as_str() {
            return path.to_string();
        }
    }
    ".".to_string()
}

fn get_prefs(state: &State) -> Prefs {
    Prefs {
        projects: state.projects.clone().into_iter().collect(),
        expansions: state.expansions.clone().into_iter().collect(),
        selection: state.selection.clone()
    }
}

pub fn is_parent_path(parent_str: &String, child_str: &String) -> bool {
    child_str.starts_with(parent_str.as_slice()) &&
    Path::new(parent_str).parent() != Path::new(child_str).parent()
}

pub fn remove_expansions_for_path(state: &mut ::utils::State, path_str: &String) {
    for expansion_str in state.expansions.clone().iter() {
        if !Path::new(expansion_str).exists() ||
            path_str == expansion_str ||
            ::utils::is_parent_path(path_str, expansion_str)
        {
            state.expansions.remove(expansion_str);
        }
    }
}

pub fn get_selected_path(state: &State) -> Option<String> {
    let mut iter = gtk::TreeIter::new().unwrap();

    if state.tree_selection.get_selected(state.tree_model, &mut iter) {
        state.tree_model.get_value(&iter, 1).get_string()
    } else {
        None
    }
}

pub fn write_prefs(state: &State) {
    let prefs = get_prefs(state);

    let mut json_str = String::new();
    {
        let mut encoder = json::Encoder::new_pretty(&mut json_str);
        prefs.encode(&mut encoder).ok().expect("Error encoding prefs.");
    }

    let prefs_path = Path::new(&get_home_dir()).join(DATA_DIR).join(PREFS_FILE);
    if let Some(mut f) = fs::File::create(&prefs_path).ok() {
        match f.write(json_str.as_bytes()) {
            Ok(_) => {},
            Err(e) => println!("Error writing prefs: {}", e)
        };
    }
}

pub fn read_prefs(state: &mut State) {
    let prefs_path = Path::new(&get_home_dir()).join(DATA_DIR).join(PREFS_FILE);
    if let Some(mut f) = fs::File::open(&prefs_path).ok() {
        let mut json_str = String::new();
        let prefs_option : Option<Prefs> = match f.read_to_string(&mut json_str) {
            Ok(_) => {
                match json::decode(json_str.as_slice()) {
                    Ok(object) => Some(object),
                    Err(e) => {
                        println!("Error decoding prefs: {}", e);
                        None
                    }
                }
            },
            Err(_) => None
        };

        if let Some(prefs) = prefs_option {
            state.projects.clear();
            for path_str in prefs.projects.iter() {
                state.projects.insert(path_str.clone());
            }

            state.expansions.clear();
            for path_str in prefs.expansions.iter() {
                state.expansions.insert(path_str.clone());
            }

            state.selection = prefs.selection;
        }
    }
}
<commit_msg>Update for latest version of Rust<commit_after>use rgtk::*;
use rustc_serialize::{Encodable, json};
use std::env;
use std::collections::HashSet;
use std::io::{Read, Write};
use std::fs;
use std::fs::PathExt;
use std::ops::Deref;
use std::path::Path;

pub static DATA_DIR : &'static str = ".soak";
pub static CONFIG_FILE : &'static str = ".soakrc";
pub static CONFIG_CONTENT : &'static str = include_str!("../resources/soakrc");
pub static PREFS_FILE : &'static str = "prefs.json";
pub static NO_WINDOW_FLAG : &'static str = "-nw";

struct Resource {
    pub path: &'static [&'static str],
    pub data: &'static str,
}
pub static DATA_CONTENT : &'static [Resource] = &[
    Resource{path: &["after", "syntax", "rust.vim"],
             data: include_str!("../resources/soak/after/syntax/rust.vim")},

    Resource{path: &["autoload", "rust.vim"],
             data: include_str!("../resources/soak/autoload/rust.vim")},
    Resource{path: &["autoload", "paste.vim"],
             data: include_str!("../resources/soak/autoload/paste.vim")},

    Resource{path: &["compiler", "rustc.vim"],
             data: include_str!("../resources/soak/compiler/rustc.vim")},
    Resource{path: &["compiler", "cargo.vim"],
             data: include_str!("../resources/soak/compiler/cargo.vim")},

    Resource{path: &["doc", "rust.txt"],
             data: include_str!("../resources/soak/doc/rust.txt")},

    Resource{path: &["ftdetect", "rust.vim"],
             data: include_str!("../resources/soak/ftdetect/rust.vim")},

    Resource{path: &["ftplugin", "rust.vim"],
             data: include_str!("../resources/soak/ftplugin/rust.vim")},
    Resource{path: &["ftplugin", "c.vim"],
             data: include_str!("../resources/soak/ftplugin/c.vim")},

    Resource{path: &["indent", "rust.vim"],
             data: include_str!("../resources/soak/indent/rust.vim")},
    Resource{path: &["indent", "c.vim"],
             data: include_str!("../resources/soak/indent/c.vim")},

    Resource{path: &["plugin", "eunuch.vim"],
             data: include_str!("../resources/soak/plugin/eunuch.vim")},
    Resource{path: &["plugin", "rust.vim"],
             data: include_str!("../resources/soak/plugin/rust.vim")},

    Resource{path: &["syntax", "c.vim"],
             data: include_str!("../resources/soak/syntax/c.vim")},
    Resource{path: &["syntax", "nosyntax.vim"],
             data: include_str!("../resources/soak/syntax/nosyntax.vim")},
    Resource{path: &["syntax", "rust.vim"],
             data: include_str!("../resources/soak/syntax/rust.vim")},
    Resource{path: &["syntax", "syncolor.vim"],
             data: include_str!("../resources/soak/syntax/syncolor.vim")},
    Resource{path: &["syntax", "synload.vim"],
             data: include_str!("../resources/soak/syntax/synload.vim")},
    Resource{path: &["syntax", "syntax.vim"],
             data: include_str!("../resources/soak/syntax/syntax.vim")},

    Resource{path: &["syntax_checkers", "rust", "rustc.vim"],
             data: include_str!("../resources/soak/syntax_checkers/rust/rustc.vim")},

    Resource{path: &["evim.vim"],
             data: include_str!("../resources/soak/evim.vim")},
    Resource{path: &["filetype.vim"],
             data: include_str!("../resources/soak/filetype.vim")},
    Resource{path: &["mswin.vim"],
             data: include_str!("../resources/soak/mswin.vim")}
];

pub struct State<'a> {
    pub projects: HashSet<String>,
    pub expansions: HashSet<String>,
    pub selection: Option<String>,
    pub window: &'a gtk::Window,
    pub tree_store: &'a gtk::TreeStore,
    pub tree_model: &'a gtk::TreeModel,
    pub tree_selection: &'a gtk::TreeSelection,
    pub rename_button: &'a gtk::Button,
    pub remove_button: &'a gtk::Button,
    pub is_refreshing_tree: bool
}

#[derive(RustcDecodable, RustcEncodable)]
struct Prefs {
    projects: Vec<String>,
    expansions: Vec<String>,
    selection: Option<String>
}

pub fn get_home_dir() -> String {
    if let Some(path) = env::home_dir() {
        if let Some(path_str) = path.deref().to_str() {
            return path_str.to_string();
        }
    }
    ".".to_string()
}

fn get_prefs(state: &State) -> Prefs {
    Prefs {
        projects: state.projects.clone().into_iter().collect(),
        expansions: state.expansions.clone().into_iter().collect(),
        selection: state.selection.clone()
    }
}

pub fn is_parent_path(parent_str: &String, child_str: &String) -> bool {
    child_str.starts_with(parent_str.as_slice()) &&
    Path::new(parent_str).parent() != Path::new(child_str).parent()
}

pub fn remove_expansions_for_path(state: &mut ::utils::State, path_str: &String) {
    for expansion_str in state.expansions.clone().iter() {
        if !Path::new(expansion_str).exists() ||
            path_str == expansion_str ||
            ::utils::is_parent_path(path_str, expansion_str)
        {
            state.expansions.remove(expansion_str);
        }
    }
}

pub fn get_selected_path(state: &State) -> Option<String> {
    let mut iter = gtk::TreeIter::new().unwrap();

    if state.tree_selection.get_selected(state.tree_model, &mut iter) {
        state.tree_model.get_value(&iter, 1).get_string()
    } else {
        None
    }
}

pub fn write_prefs(state: &State) {
    let prefs = get_prefs(state);

    let mut json_str = String::new();
    {
        let mut encoder = json::Encoder::new_pretty(&mut json_str);
        prefs.encode(&mut encoder).ok().expect("Error encoding prefs.");
    }

    let prefs_path = Path::new(&get_home_dir()).join(DATA_DIR).join(PREFS_FILE);
    if let Some(mut f) = fs::File::create(&prefs_path).ok() {
        match f.write(json_str.as_bytes()) {
            Ok(_) => {},
            Err(e) => println!("Error writing prefs: {}", e)
        };
    }
}

pub fn read_prefs(state: &mut State) {
    let prefs_path = Path::new(&get_home_dir()).join(DATA_DIR).join(PREFS_FILE);
    if let Some(mut f) = fs::File::open(&prefs_path).ok() {
        let mut json_str = String::new();
        let prefs_option : Option<Prefs> = match f.read_to_string(&mut json_str) {
            Ok(_) => {
                match json::decode(json_str.as_slice()) {
                    Ok(object) => Some(object),
                    Err(e) => {
                        println!("Error decoding prefs: {}", e);
                        None
                    }
                }
            },
            Err(_) => None
        };

        if let Some(prefs) = prefs_option {
            state.projects.clear();
            for path_str in prefs.projects.iter() {
                state.projects.insert(path_str.clone());
            }

            state.expansions.clear();
            for path_str in prefs.expansions.iter() {
                state.expansions.insert(path_str.clone());
            }

            state.selection = prefs.selection;
        }
    }
}
<|endoftext|>


<commit_before>use syntax::ast::{Expr, Ident, Stmt};
use syntax::ext::base::ExtCtxt;
use syntax::parse::token;
use syntax::ptr::P;

use super::parse::{Markup, Value};
use maud::escape;

pub fn render(cx: &mut ExtCtxt, markups: &[Markup]) -> P<Expr> {
    let w = Ident::new(token::intern("w"));
    let mut stmts = vec![];
    for markup in markups.iter() {
        render_markup(cx, markup, w, &mut stmts);
    }
    quote_expr!(cx, |&: $w: &mut ::std::io::Writer| -> ::std::io::IoResult<()> {
        $stmts
        Ok(())
    })
}

fn render_markup(cx: &mut ExtCtxt, markup: &Markup, w: Ident, out: &mut Vec<P<Stmt>>) {
    use super::parse::Markup::*;
    match *markup {
        Empty => {},
        Element(..) => unimplemented!(),
        Value(ref value) => {
            let stmt = render_value(cx, value, w, false);
            out.push(stmt);
        },
    }
}

fn render_value(cx: &mut ExtCtxt, value: &Value, w: Ident, is_attr: bool) -> P<Stmt> {
    use super::parse::Escape::*;
    use super::parse::Value_::*;
    let &Value { ref value, escape } = value;
    match *value {
        Literal(ref s) => {
            let s = match escape {
                NoEscape => (&**s).into_cow(),
                Escape => if is_attr {
                    escape::attribute(&**s).into_cow()
                } else {
                    escape::non_attribute(&**s).into_cow()
                },
            };
            quote_stmt!(cx, {
                try!($w.write_str($s))
            })
        },
        Splice(ref expr) => match escape {
            NoEscape => quote_stmt!(cx, {
                try!(write!($w, "{}", $expr));
            }),
            Escape => quote_stmt!(cx, {
                let s = $expr.to_string();
                for c in s.chars() {
                    try!(if $is_attr {
                            ::maud::rt::escape_attribute(c, $w)
                        } else {
                            ::maud::rt::escape_non_attribute(c, $w)
                        });
                }
            }),
        },
    }
}
<commit_msg>Change render_value to return a Expr, not a Stmt<commit_after>use syntax::ast::{Expr, Ident, Stmt};
use syntax::ext::base::ExtCtxt;
use syntax::parse::token;
use syntax::ptr::P;

use super::parse::{Markup, Value};
use maud::escape;

pub fn render(cx: &mut ExtCtxt, markups: &[Markup]) -> P<Expr> {
    let w = Ident::new(token::intern("w"));
    let mut stmts = vec![];
    for markup in markups.iter() {
        render_markup(cx, markup, w, &mut stmts);
    }
    quote_expr!(cx, |&: $w: &mut ::std::io::Writer| -> ::std::io::IoResult<()> {
        $stmts
        Ok(())
    })
}

fn render_markup(cx: &mut ExtCtxt, markup: &Markup, w: Ident, out: &mut Vec<P<Stmt>>) {
    use super::parse::Markup::*;
    match *markup {
        Empty => {},
        Element(..) => unimplemented!(),
        Value(ref value) => {
            let expr = render_value(cx, value, w, false);
            out.push(quote_stmt!(cx, $expr));
        },
    }
}

fn render_value(cx: &mut ExtCtxt, value: &Value, w: Ident, is_attr: bool) -> P<Expr> {
    use super::parse::Escape::*;
    use super::parse::Value_::*;
    let &Value { ref value, escape } = value;
    match *value {
        Literal(ref s) => {
            let s = match escape {
                NoEscape => (&**s).into_cow(),
                Escape => if is_attr {
                    escape::attribute(&**s).into_cow()
                } else {
                    escape::non_attribute(&**s).into_cow()
                },
            };
            quote_expr!(cx, {
                try!($w.write_str($s))
            })
        },
        Splice(ref expr) => match escape {
            NoEscape => quote_expr!(cx, {
                try!(write!($w, "{}", $expr));
            }),
            Escape => quote_expr!(cx, {
                let s = $expr.to_string();
                for c in s.chars() {
                    try!(if $is_attr {
                            ::maud::rt::escape_attribute(c, $w)
                        } else {
                            ::maud::rt::escape_non_attribute(c, $w)
                        });
                }
            }),
        },
    }
}
<|endoftext|>


<commit_before>//! Types representing for data which will be retrieved from the database.
//! Currently this data is expected to look like a JSON object but this may be
//! changed in the future. Driver authors must cast the data they retrieve from
//! the database to these types.

use std::collections::BTreeMap;
use structure::Collection;

/// The atomic level of a pointer.
pub type Key = String;

/// Represents a JSON pointer to a document property.
pub type Pointer = Vec<Key>;

/// Various value types. Based on types in the [JSON standard][1] (see section
/// 5).
///
/// [1]: http://ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
pub enum Value {
  /// The abscense of any value.
  Null,
  /// True or false.
  Boolean(bool),
  /// A numeric value, float, integer, whatever.
  Number(f64),
  /// A list of characters.
  String(String),
  /// A list of values.
  Array(Vec<Value>),
  /// A map of key/value pairs.
  Object(BTreeMap<Key, Value>)
}

/// A schema detailing what the data received from the database (or inserted
/// into the database) should be. Inspired after [JSON Schema][1]. A JSON
/// Schema general keyword reference may be found [here][2].
///
/// [1]: http://json-schema.org
/// [2]: http://spacetelescope.github.io/understanding-json-schema/reference/generic.html
pub struct Schema {
  /// The types an object may be. Contains some custom validation information
  /// for each type.
  types: Vec<SchemaType>,
  /// The default value of this part of the schema.
  default: Option<Value>,
  /// Equivelent to JSON Schema‘s `enum`. If not `None`, the value must be
  /// exactly equal to one of these.
  one_of: Option<Vec<Value>>
}

/// Type specific schema validations. A reference on JSON Schema type-specific
/// validations may be found [here][1].
///
/// [1]: http://spacetelescope.github.io/understanding-json-schema/reference/type.html
pub enum SchemaType {
  /// The absence of any value is also represented as the absence of any type.
  Null,
  /// Represents a binary true/false value.
  Boolean,
  /// Represents a numeric type.
  Number {
    /// Forces the number to be a multiple of another. This helps specifying
    /// integers if this value is `Some(1)`.
    multiple_of: Option<f32>,
    /// The minimum value the number can be.
    minimum: Option<f64>,
    /// The maximum value the number can be.
    maximum: Option<f64>
  },
  /// Represents a set of characters type.
  String {
    /// The mimimum length of characters in the string.
    min_length: Option<u64>,
    /// The maximum length of characters in the string.
    max_length: Option<u64>,
    /// A regular expression pattern to validate the string against.
    // TODO: Use a regex crate.
    pattern: Option<String>
  },
  /// Represents a set of any type.
  Array {
    /// A schema which all items in the array must match.
    item_schema: Option<Schema>
  },
  /// Represents a set of key/value pairs.
  Object {
    /// Schemas associated to the object keys.
    key_schemas: BTreeMap<Key, Schema>,
    /// Keys that are required to be present in the object.
    required_keys: Vec<Key>,
    /// Whether or not extra keys may be present in the object.
    additional_keys: bool
  }
}

/// Different database collection property updates.
pub enum Patch {
  /// Set a property to a new value.
  Set(Pointer, Value),
  /// Reset a property to its default value.
  Reset(Pointer),
  /// Remove a property from the database entirely.
  Remove(Pointer)
}

/// A recursive filter condition for a `Value`.
pub enum Filter {
  /// Combine multiple filters with an “and” operator.
  And(Vec<Filter>),
  /// Combine multiple filters with an “or” operator.
  Or(Vec<Filter>),
  /// Inverts the filter.
  Not(Box<Filter>),
  /// The basic condition of a filter.
  Condition(Pointer, FilterCondition)
}

pub enum FilterCondition {
  Equal(Value),
  OneOf(Vec<Value>),
  GreaterThan(Value),
  LessThan(Value)
}

/// A single way in which to order a collection of documents.
pub struct Ordering(Pointer, OrderDirection);

pub enum OrderDirection {
  Ascending,
  Descending
}

// TODO: Find a more Rust idiomatic solution for ranges.
pub struct Range(Option<u32>, Option<u32>);
<commit_msg>Use record syntax for Range<commit_after>//! Types representing for data which will be retrieved from the database.
//! Currently this data is expected to look like a JSON object but this may be
//! changed in the future. Driver authors must cast the data they retrieve from
//! the database to these types.

use std::collections::BTreeMap;
use structure::Collection;

/// The atomic level of a pointer.
pub type Key = String;

/// Represents a JSON pointer to a document property.
pub type Pointer = Vec<Key>;

/// Various value types. Based on types in the [JSON standard][1] (see section
/// 5).
///
/// [1]: http://ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
pub enum Value {
  /// The abscense of any value.
  Null,
  /// True or false.
  Boolean(bool),
  /// A numeric value, float, integer, whatever.
  Number(f64),
  /// A list of characters.
  String(String),
  /// A list of values.
  Array(Vec<Value>),
  /// A map of key/value pairs.
  Object(BTreeMap<Key, Value>)
}

/// A schema detailing what the data received from the database (or inserted
/// into the database) should be. Inspired after [JSON Schema][1]. A JSON
/// Schema general keyword reference may be found [here][2].
///
/// [1]: http://json-schema.org
/// [2]: http://spacetelescope.github.io/understanding-json-schema/reference/generic.html
pub struct Schema {
  /// The types an object may be. Contains some custom validation information
  /// for each type.
  types: Vec<SchemaType>,
  /// The default value of this part of the schema.
  default: Option<Value>,
  /// Equivelent to JSON Schema‘s `enum`. If not `None`, the value must be
  /// exactly equal to one of these.
  one_of: Option<Vec<Value>>
}

/// Type specific schema validations. A reference on JSON Schema type-specific
/// validations may be found [here][1].
///
/// [1]: http://spacetelescope.github.io/understanding-json-schema/reference/type.html
pub enum SchemaType {
  /// The absence of any value is also represented as the absence of any type.
  Null,
  /// Represents a binary true/false value.
  Boolean,
  /// Represents a numeric type.
  Number {
    /// Forces the number to be a multiple of another. This helps specifying
    /// integers if this value is `Some(1)`.
    multiple_of: Option<f32>,
    /// The minimum value the number can be.
    minimum: Option<f64>,
    /// The maximum value the number can be.
    maximum: Option<f64>
  },
  /// Represents a set of characters type.
  String {
    /// The mimimum length of characters in the string.
    min_length: Option<u64>,
    /// The maximum length of characters in the string.
    max_length: Option<u64>,
    /// A regular expression pattern to validate the string against.
    // TODO: Use a regex crate.
    pattern: Option<String>
  },
  /// Represents a set of any type.
  Array {
    /// A schema which all items in the array must match.
    item_schema: Option<Schema>
  },
  /// Represents a set of key/value pairs.
  Object {
    /// Schemas associated to the object keys.
    key_schemas: BTreeMap<Key, Schema>,
    /// Keys that are required to be present in the object.
    required_keys: Vec<Key>,
    /// Whether or not extra keys may be present in the object.
    additional_keys: bool
  }
}

/// Different database collection property updates.
pub enum Patch {
  /// Set a property to a new value.
  Set(Pointer, Value),
  /// Reset a property to its default value.
  Reset(Pointer),
  /// Remove a property from the database entirely.
  Remove(Pointer)
}

/// A recursive filter condition for a `Value`.
pub enum Filter {
  /// Combine multiple filters with an “and” operator.
  And(Vec<Filter>),
  /// Combine multiple filters with an “or” operator.
  Or(Vec<Filter>),
  /// Inverts the filter.
  Not(Box<Filter>),
  /// The basic condition of a filter.
  Condition(Pointer, FilterCondition)
}

pub enum FilterCondition {
  Equal(Value),
  OneOf(Vec<Value>),
  GreaterThan(Value),
  LessThan(Value)
}

/// A single way in which to order a collection of documents.
pub struct Ordering(Pointer, OrderDirection);

pub enum OrderDirection {
  Ascending,
  Descending
}

// TODO: Find a more Rust idiomatic solution for ranges.
pub struct Range {
  from: Option<u32>,
  to: Option<u32>
}
<|endoftext|>


<commit_before>/// Code based on https://github.com/defuz/sublimate/blob/master/src/core/syntax/theme.rs
/// released under the MIT license by @defuz

use std::str::FromStr;

use super::settings::{ParseSettings, Settings};
use super::style::*;
use super::selector::*;
use parsing::ParseScopeError;

use self::ParseThemeError::*;

/// A theme parsed from a `.tmTheme` file.
/// Contains fields useful for a theme list as well as `settings` for styling your editor.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Theme {
    pub name: Option<String>,
    pub author: Option<String>,
    pub settings: ThemeSettings,
    pub scopes: Vec<ThemeItem>,
}

/// Various properties meant to be used to style a text editor.
/// Basically all the styles that aren't directly applied to text like selection colour.
/// Use this to make your editor UI match the highlighted text.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ThemeSettings {
    /// The default color for text.
    pub foreground: Option<Color>,
    /// The default backgound color of the view.
    pub background: Option<Color>,
    /// Color of the caret.
    pub caret: Option<Color>,
    /// Color of the line the caret is in.
    /// Only used when the `higlight_line` setting is set to `true`.
    pub line_highlight: Option<Color>,

    /// The color to use for the squiggly underline drawn under misspelled words.
    pub misspelling: Option<Color>,
    /// The color of the border drawn around the viewport area of the minimap.
    /// Only used when the `draw_minimap_border` setting is enabled.
    pub minimap_border: Option<Color>,
    /// A color made available for use by the theme.
    pub accent: Option<Color>,
    /// CSS passed to popups.
    pub popup_css: Option<Color>,
    /// CSS passed to phantoms.
    pub phantom_css: Option<Color>,

    /// Color of bracketed sections of text when the caret is in a bracketed section.
    /// Only applied when the `match_brackets` setting is set to `true`.
    pub bracket_contents_foreground: Option<Color>,
    /// Controls certain options when the caret is in a bracket section.
    /// Only applied when the `match_brackets` setting is set to `true`.
    pub bracket_contents_options: Option<UnderlineOption>,
    /// Foreground color of the brackets when the caret is next to a bracket.
    /// Only applied when the `match_brackets` setting is set to `true`.
    pub brackets_foreground: Option<Color>,
    /// Background color of the brackets when the caret is next to a bracket.
    /// Only applied when the `match_brackets` setting is set to `true`.
    pub brackets_background: Option<Color>,
    /// Controls certain options when the caret is next to a bracket.
    /// Only applied when the match_brackets setting is set to `true`.
    pub brackets_options: Option<UnderlineOption>,

    /// Color of tags when the caret is next to a tag.
    /// Only used when the `match_tags` setting is set to `true`.
    pub tags_foreground: Option<Color>,
    /// Controls certain options when the caret is next to a tag.
    /// Only applied when the match_tags setting is set to `true`.
    pub tags_options: Option<UnderlineOption>,

    /// The border color for "other" matches.
    pub highlight: Option<Color>,
    /// Background color of regions matching the current search.
    pub find_highlight: Option<Color>,
    /// Text color of regions matching the current search.
    pub find_highlight_foreground: Option<Color>,

    /// Background color of the gutter.
    pub gutter: Option<Color>,
    /// Foreground color of the gutter.
    pub gutter_foreground: Option<Color>,

    /// The background color of selected text.
    pub selection: Option<Color>,
    /// A color that will override the scope-based text color of the selection.
    pub selection_foreground: Option<Color>,

    /// Deprecated!
    ///
    /// This property is not part of the recognized tmTheme format. It may be
    /// removed in a future release.
    pub selection_background: Option<Color>,

    /// Color of the selection regions border.
    pub selection_border: Option<Color>,
    /// The background color of a selection in a view that is not currently focused.
    pub inactive_selection: Option<Color>,
    /// A color that will override the scope-based text color of the selection
    /// in a view that is not currently focused.
    pub inactive_selection_foreground: Option<Color>,

    /// Color of the guides displayed to indicate nesting levels.
    pub guide: Option<Color>,
    /// Color of the guide lined up with the caret.
    /// Only applied if the `indent_guide_options` setting is set to `draw_active`.
    pub active_guide: Option<Color>,
    /// Color of the current guide’s parent guide level.
    /// Only used if the `indent_guide_options` setting is set to `draw_active`.
    pub stack_guide: Option<Color>,

    /// Foreground color for regions added via `sublime.add_regions()`
    /// with the `sublime.DRAW_OUTLINED` flag added.
    ///
    /// Deprecated!
    /// This setting does not exist in any available documentation.
    /// Use is discouraged, and it may be removed in a future release.
    pub highlight_foreground: Option<Color>,

    /// The color of the shadow used when a text area can be horizontally scrolled.
    pub shadow: Option<Color>,
}

/// A component of a theme meant to highlight a specific thing (e.g string literals)
/// in a certain way.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ThemeItem {
    /// Target scope name.
    pub scope: ScopeSelectors,
    pub style: StyleModifier,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum UnderlineOption {
    None,
    Underline,
    StippledUnderline,
    SquigglyUnderline,
}

#[derive(Debug)]
pub enum ParseThemeError {
    IncorrectUnderlineOption,
    IncorrectFontStyle(String),
    IncorrectColor,
    IncorrectSyntax,
    IncorrectSettings,
    UndefinedSettings,
    UndefinedScopeSettings(String),
    ColorShemeScopeIsNotObject,
    ColorShemeSettingsIsNotObject,
    ScopeSelectorIsNotString(String),
    DuplicateSettings,
    ScopeParse(ParseScopeError),
}

impl From<ParseScopeError> for ParseThemeError {
    fn from(error: ParseScopeError) -> ParseThemeError {
        ScopeParse(error)
    }
}

impl Default for UnderlineOption {
    fn default() -> UnderlineOption {
        UnderlineOption::None
    }
}

impl Default for FontStyle {
    fn default() -> FontStyle {
        FontStyle::empty()
    }
}

impl FromStr for UnderlineOption {
    type Err = ParseThemeError;

    fn from_str(s: &str) -> Result<UnderlineOption, Self::Err> {
        Ok(match s {
            "underline" => UnderlineOption::Underline,
            "stippled_underline" => UnderlineOption::StippledUnderline,
            "squiggly_underline" => UnderlineOption::SquigglyUnderline,
            _ => return Err(IncorrectUnderlineOption),
        })
    }
}

impl ParseSettings for UnderlineOption {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<UnderlineOption, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(UnderlineOption::from_str(&value))),
            _ => Err(IncorrectUnderlineOption),
        }
    }
}

impl FromStr for FontStyle {
    type Err = ParseThemeError;

    fn from_str(s: &str) -> Result<FontStyle, Self::Err> {
        let mut font_style = FontStyle::empty();
        for i in s.split_whitespace() {
            font_style.insert(match i {
                "bold" => FONT_STYLE_BOLD,
                "underline" => FONT_STYLE_UNDERLINE,
                "italic" => FONT_STYLE_ITALIC,
                "normal" |
                "regular" => FontStyle::empty(),
                s => return Err(IncorrectFontStyle(s.to_owned())),
            })
        }
        Ok(font_style)
    }
}

impl ParseSettings for FontStyle {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<FontStyle, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(FontStyle::from_str(&value))),
            c => Err(IncorrectFontStyle(c.to_string())),
        }
    }
}

impl FromStr for Color {
    type Err = ParseThemeError;

    fn from_str(s: &str) -> Result<Color, Self::Err> {
        let mut chars = s.chars();
        if chars.next() != Some('#') {
            return Err(IncorrectColor);
        }
        let mut d = Vec::new();
        for char in chars {
            d.push(try!(char.to_digit(16).ok_or(IncorrectColor)) as u8);
        }
        Ok(match d.len() {
            3 => {
                Color {
                    r: d[0],
                    g: d[1],
                    b: d[2],
                    a: 255,
                }
            }
            6 => {
                Color {
                    r: d[0] * 16 + d[1],
                    g: d[2] * 16 + d[3],
                    b: d[4] * 16 + d[5],
                    a: 255,
                }
            }
            8 => {
                Color {
                    r: d[0] * 16 + d[1],
                    g: d[2] * 16 + d[3],
                    b: d[4] * 16 + d[5],
                    a: d[6] * 16 + d[7],
                }
            }
            _ => return Err(IncorrectColor),
        })
    }
}

impl ParseSettings for Color {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<Color, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(Color::from_str(&value))),
            _ => Err(IncorrectColor),
        }
    }
}

impl ParseSettings for StyleModifier {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<StyleModifier, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeScopeIsNotObject),
        };
        let font_style = match obj.remove("fontStyle") {
            Some(Settings::String(value)) => Some(try!(FontStyle::from_str(&value))),
            None => None,
            Some(c) => return Err(IncorrectFontStyle(c.to_string())),
        };
        let foreground = match obj.remove("foreground") {
            Some(Settings::String(value)) => Some(try!(Color::from_str(&value))),
            None => None,
            _ => return Err(IncorrectColor),
        };
        let background = match obj.remove("background") {
            Some(Settings::String(value)) => Some(try!(Color::from_str(&value))),
            None => None,
            _ => return Err(IncorrectColor),
        };

        Ok(StyleModifier {
            foreground: foreground,
            background: background,
            font_style: font_style,
        })
    }
}

impl ParseSettings for ThemeItem {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<ThemeItem, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeScopeIsNotObject),
        };
        let scope = match obj.remove("scope") {
            Some(Settings::String(value)) => try!(ScopeSelectors::from_str(&value)),
            _ => return Err(ScopeSelectorIsNotString(format!("{:?}", obj))),
        };
        let style = match obj.remove("settings") {
            Some(settings) => try!(StyleModifier::parse_settings(settings)),
            None => return Err(IncorrectSettings),
        };
        Ok(ThemeItem {
            scope: scope,
            style: style,
        })
    }
}

impl ParseSettings for ThemeSettings {
    type Error = ParseThemeError;

    fn parse_settings(json: Settings) -> Result<ThemeSettings, Self::Error> {
        let mut settings = ThemeSettings::default();

        let obj = match json {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeSettingsIsNotObject),
        };

        for (key, value) in obj {
            match &key[..] {
                "foreground" => settings.foreground = Color::parse_settings(value).ok(),
                "background" => settings.background = Color::parse_settings(value).ok(),
                "caret" => settings.caret = Color::parse_settings(value).ok(),
                "lineHighlight" => settings.line_highlight = Color::parse_settings(value).ok(),
                "misspelling" => settings.misspelling = Color::parse_settings(value).ok(),
                "minimapBorder" => settings.minimap_border = Color::parse_settings(value).ok(),
                "accent" => settings.accent = Color::parse_settings(value).ok(),
                "popupCss" => settings.popup_css = Color::parse_settings(value).ok(),
                "phantomCss" => settings.phantom_css = Color::parse_settings(value).ok(),
                "bracketContentsForeground" => {
                    settings.bracket_contents_foreground = Color::parse_settings(value).ok()
                }
                "bracketContentsOptions" => {
                    settings.bracket_contents_options = UnderlineOption::parse_settings(value).ok()
                }
                "bracketsForeground" => {
                    settings.brackets_foreground = Color::parse_settings(value).ok()
                }
                "bracketsBackground" => {
                    settings.brackets_background = Color::parse_settings(value).ok()
                }
                "bracketsOptions" => {
                    settings.brackets_options = UnderlineOption::parse_settings(value).ok()
                }
                "tagsForeground" => settings.tags_foreground = Color::parse_settings(value).ok(),
                "tagsOptions" => {
                    settings.tags_options = UnderlineOption::parse_settings(value).ok()
                }
                "highlight" => settings.highlight = Color::parse_settings(value).ok(),
                "findHighlight" => settings.find_highlight = Color::parse_settings(value).ok(),
                "findHighlightForeground" => {
                    settings.find_highlight_foreground = Color::parse_settings(value).ok()
                }
                "gutter" => settings.gutter = Color::parse_settings(value).ok(),
                "gutterForeground" => {
                    settings.gutter_foreground = Color::parse_settings(value).ok()
                }
                "selection" => settings.selection = Color::parse_settings(value).ok(),
                "selectionForeground" => {
                    settings.selection_foreground = Color::parse_settings(value).ok()
                }
                "selectionBorder" => settings.selection_border = Color::parse_settings(value).ok(),
                "inactiveSelection" => {
                    settings.inactive_selection = Color::parse_settings(value).ok()
                }
                "inactiveSelectionForeground" => {
                    settings.inactive_selection_foreground = Color::parse_settings(value).ok()
                }
                "guide" => settings.guide = Color::parse_settings(value).ok(),
                "activeGuide" => settings.active_guide = Color::parse_settings(value).ok(),
                "stackGuide" => settings.stack_guide = Color::parse_settings(value).ok(),
                "shadow" => settings.shadow = Color::parse_settings(value).ok(),
                "shadowWidth"| // ignored
                "invisibles" | // ignored
                _ => (),
            }
        }
        Ok(settings)
    }
}

impl ParseSettings for Theme {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<Theme, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(IncorrectSyntax),
        };
        let name = match obj.remove("name") {
            Some(Settings::String(name)) => Some(name),
            None => None,
            _ => return Err(IncorrectSyntax),
        };
        let author = match obj.remove("author") {
            Some(Settings::String(author)) => Some(author),
            None => None,
            _ => return Err(IncorrectSyntax),
        };
        let items = match obj.remove("settings") {
            Some(Settings::Array(items)) => items,
            _ => return Err(IncorrectSyntax),
        };
        let mut iter = items.into_iter();
        let settings = match iter.next() {
            Some(Settings::Object(mut obj)) => {
                match obj.remove("settings") {
                    Some(settings) => try!(ThemeSettings::parse_settings(settings)),
                    None => return Err(UndefinedSettings),
                }
            }
            _ => return Err(UndefinedSettings),
        };
        let mut scopes = Vec::new();
        for json in iter {
            // TODO option to disable best effort parsing and bubble up warnings
            if let Ok(item) = ThemeItem::parse_settings(json) {
                scopes.push(item);
            }
        }
        Ok(Theme {
            name: name,
            author: author,
            settings: settings,
            scopes: scopes,
        })
    }
}
<commit_msg>Fix parsing of CSS theme properties<commit_after>/// Code based on https://github.com/defuz/sublimate/blob/master/src/core/syntax/theme.rs
/// released under the MIT license by @defuz

use std::str::FromStr;

use super::settings::{ParseSettings, Settings};
use super::style::*;
use super::selector::*;
use parsing::ParseScopeError;

use self::ParseThemeError::*;

/// A theme parsed from a `.tmTheme` file.
/// Contains fields useful for a theme list as well as `settings` for styling your editor.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Theme {
    pub name: Option<String>,
    pub author: Option<String>,
    pub settings: ThemeSettings,
    pub scopes: Vec<ThemeItem>,
}

/// Various properties meant to be used to style a text editor.
/// Basically all the styles that aren't directly applied to text like selection colour.
/// Use this to make your editor UI match the highlighted text.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ThemeSettings {
    /// The default color for text.
    pub foreground: Option<Color>,
    /// The default backgound color of the view.
    pub background: Option<Color>,
    /// Color of the caret.
    pub caret: Option<Color>,
    /// Color of the line the caret is in.
    /// Only used when the `higlight_line` setting is set to `true`.
    pub line_highlight: Option<Color>,

    /// The color to use for the squiggly underline drawn under misspelled words.
    pub misspelling: Option<Color>,
    /// The color of the border drawn around the viewport area of the minimap.
    /// Only used when the `draw_minimap_border` setting is enabled.
    pub minimap_border: Option<Color>,
    /// A color made available for use by the theme.
    pub accent: Option<Color>,
    /// CSS passed to popups.
    pub popup_css: Option<String>,
    /// CSS passed to phantoms.
    pub phantom_css: Option<String>,

    /// Color of bracketed sections of text when the caret is in a bracketed section.
    /// Only applied when the `match_brackets` setting is set to `true`.
    pub bracket_contents_foreground: Option<Color>,
    /// Controls certain options when the caret is in a bracket section.
    /// Only applied when the `match_brackets` setting is set to `true`.
    pub bracket_contents_options: Option<UnderlineOption>,
    /// Foreground color of the brackets when the caret is next to a bracket.
    /// Only applied when the `match_brackets` setting is set to `true`.
    pub brackets_foreground: Option<Color>,
    /// Background color of the brackets when the caret is next to a bracket.
    /// Only applied when the `match_brackets` setting is set to `true`.
    pub brackets_background: Option<Color>,
    /// Controls certain options when the caret is next to a bracket.
    /// Only applied when the match_brackets setting is set to `true`.
    pub brackets_options: Option<UnderlineOption>,

    /// Color of tags when the caret is next to a tag.
    /// Only used when the `match_tags` setting is set to `true`.
    pub tags_foreground: Option<Color>,
    /// Controls certain options when the caret is next to a tag.
    /// Only applied when the match_tags setting is set to `true`.
    pub tags_options: Option<UnderlineOption>,

    /// The border color for "other" matches.
    pub highlight: Option<Color>,
    /// Background color of regions matching the current search.
    pub find_highlight: Option<Color>,
    /// Text color of regions matching the current search.
    pub find_highlight_foreground: Option<Color>,

    /// Background color of the gutter.
    pub gutter: Option<Color>,
    /// Foreground color of the gutter.
    pub gutter_foreground: Option<Color>,

    /// The background color of selected text.
    pub selection: Option<Color>,
    /// A color that will override the scope-based text color of the selection.
    pub selection_foreground: Option<Color>,

    /// Deprecated!
    ///
    /// This property is not part of the recognized tmTheme format. It may be
    /// removed in a future release.
    pub selection_background: Option<Color>,

    /// Color of the selection regions border.
    pub selection_border: Option<Color>,
    /// The background color of a selection in a view that is not currently focused.
    pub inactive_selection: Option<Color>,
    /// A color that will override the scope-based text color of the selection
    /// in a view that is not currently focused.
    pub inactive_selection_foreground: Option<Color>,

    /// Color of the guides displayed to indicate nesting levels.
    pub guide: Option<Color>,
    /// Color of the guide lined up with the caret.
    /// Only applied if the `indent_guide_options` setting is set to `draw_active`.
    pub active_guide: Option<Color>,
    /// Color of the current guide’s parent guide level.
    /// Only used if the `indent_guide_options` setting is set to `draw_active`.
    pub stack_guide: Option<Color>,

    /// Foreground color for regions added via `sublime.add_regions()`
    /// with the `sublime.DRAW_OUTLINED` flag added.
    ///
    /// Deprecated!
    /// This setting does not exist in any available documentation.
    /// Use is discouraged, and it may be removed in a future release.
    pub highlight_foreground: Option<Color>,

    /// The color of the shadow used when a text area can be horizontally scrolled.
    pub shadow: Option<Color>,
}

/// A component of a theme meant to highlight a specific thing (e.g string literals)
/// in a certain way.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct ThemeItem {
    /// Target scope name.
    pub scope: ScopeSelectors,
    pub style: StyleModifier,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum UnderlineOption {
    None,
    Underline,
    StippledUnderline,
    SquigglyUnderline,
}

#[derive(Debug)]
pub enum ParseThemeError {
    IncorrectUnderlineOption,
    IncorrectFontStyle(String),
    IncorrectColor,
    IncorrectSyntax,
    IncorrectSettings,
    UndefinedSettings,
    UndefinedScopeSettings(String),
    ColorShemeScopeIsNotObject,
    ColorShemeSettingsIsNotObject,
    ScopeSelectorIsNotString(String),
    DuplicateSettings,
    ScopeParse(ParseScopeError),
}

impl From<ParseScopeError> for ParseThemeError {
    fn from(error: ParseScopeError) -> ParseThemeError {
        ScopeParse(error)
    }
}

impl Default for UnderlineOption {
    fn default() -> UnderlineOption {
        UnderlineOption::None
    }
}

impl Default for FontStyle {
    fn default() -> FontStyle {
        FontStyle::empty()
    }
}

impl FromStr for UnderlineOption {
    type Err = ParseThemeError;

    fn from_str(s: &str) -> Result<UnderlineOption, Self::Err> {
        Ok(match s {
            "underline" => UnderlineOption::Underline,
            "stippled_underline" => UnderlineOption::StippledUnderline,
            "squiggly_underline" => UnderlineOption::SquigglyUnderline,
            _ => return Err(IncorrectUnderlineOption),
        })
    }
}

impl ParseSettings for UnderlineOption {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<UnderlineOption, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(UnderlineOption::from_str(&value))),
            _ => Err(IncorrectUnderlineOption),
        }
    }
}

impl FromStr for FontStyle {
    type Err = ParseThemeError;

    fn from_str(s: &str) -> Result<FontStyle, Self::Err> {
        let mut font_style = FontStyle::empty();
        for i in s.split_whitespace() {
            font_style.insert(match i {
                "bold" => FONT_STYLE_BOLD,
                "underline" => FONT_STYLE_UNDERLINE,
                "italic" => FONT_STYLE_ITALIC,
                "normal" |
                "regular" => FontStyle::empty(),
                s => return Err(IncorrectFontStyle(s.to_owned())),
            })
        }
        Ok(font_style)
    }
}

impl ParseSettings for FontStyle {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<FontStyle, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(FontStyle::from_str(&value))),
            c => Err(IncorrectFontStyle(c.to_string())),
        }
    }
}

impl FromStr for Color {
    type Err = ParseThemeError;

    fn from_str(s: &str) -> Result<Color, Self::Err> {
        let mut chars = s.chars();
        if chars.next() != Some('#') {
            return Err(IncorrectColor);
        }
        let mut d = Vec::new();
        for char in chars {
            d.push(try!(char.to_digit(16).ok_or(IncorrectColor)) as u8);
        }
        Ok(match d.len() {
            3 => {
                Color {
                    r: d[0],
                    g: d[1],
                    b: d[2],
                    a: 255,
                }
            }
            6 => {
                Color {
                    r: d[0] * 16 + d[1],
                    g: d[2] * 16 + d[3],
                    b: d[4] * 16 + d[5],
                    a: 255,
                }
            }
            8 => {
                Color {
                    r: d[0] * 16 + d[1],
                    g: d[2] * 16 + d[3],
                    b: d[4] * 16 + d[5],
                    a: d[6] * 16 + d[7],
                }
            }
            _ => return Err(IncorrectColor),
        })
    }
}

impl ParseSettings for Color {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<Color, Self::Error> {
        match settings {
            Settings::String(value) => Ok(try!(Color::from_str(&value))),
            _ => Err(IncorrectColor),
        }
    }
}

impl ParseSettings for StyleModifier {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<StyleModifier, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeScopeIsNotObject),
        };
        let font_style = match obj.remove("fontStyle") {
            Some(Settings::String(value)) => Some(try!(FontStyle::from_str(&value))),
            None => None,
            Some(c) => return Err(IncorrectFontStyle(c.to_string())),
        };
        let foreground = match obj.remove("foreground") {
            Some(Settings::String(value)) => Some(try!(Color::from_str(&value))),
            None => None,
            _ => return Err(IncorrectColor),
        };
        let background = match obj.remove("background") {
            Some(Settings::String(value)) => Some(try!(Color::from_str(&value))),
            None => None,
            _ => return Err(IncorrectColor),
        };

        Ok(StyleModifier {
            foreground: foreground,
            background: background,
            font_style: font_style,
        })
    }
}

impl ParseSettings for ThemeItem {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<ThemeItem, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeScopeIsNotObject),
        };
        let scope = match obj.remove("scope") {
            Some(Settings::String(value)) => try!(ScopeSelectors::from_str(&value)),
            _ => return Err(ScopeSelectorIsNotString(format!("{:?}", obj))),
        };
        let style = match obj.remove("settings") {
            Some(settings) => try!(StyleModifier::parse_settings(settings)),
            None => return Err(IncorrectSettings),
        };
        Ok(ThemeItem {
            scope: scope,
            style: style,
        })
    }
}

impl ParseSettings for ThemeSettings {
    type Error = ParseThemeError;

    fn parse_settings(json: Settings) -> Result<ThemeSettings, Self::Error> {
        let mut settings = ThemeSettings::default();

        let obj = match json {
            Settings::Object(obj) => obj,
            _ => return Err(ColorShemeSettingsIsNotObject),
        };

        for (key, value) in obj {
            match &key[..] {
                "foreground" => settings.foreground = Color::parse_settings(value).ok(),
                "background" => settings.background = Color::parse_settings(value).ok(),
                "caret" => settings.caret = Color::parse_settings(value).ok(),
                "lineHighlight" => settings.line_highlight = Color::parse_settings(value).ok(),
                "misspelling" => settings.misspelling = Color::parse_settings(value).ok(),
                "minimapBorder" => settings.minimap_border = Color::parse_settings(value).ok(),
                "accent" => settings.accent = Color::parse_settings(value).ok(),

                "popupCss" => settings.popup_css = value.as_str().map(|s| s.to_owned()),
                "phantomCss" => settings.phantom_css = value.as_str().map(|s| s.to_owned()),

                "bracketContentsForeground" => {
                    settings.bracket_contents_foreground = Color::parse_settings(value).ok()
                }
                "bracketContentsOptions" => {
                    settings.bracket_contents_options = UnderlineOption::parse_settings(value).ok()
                }
                "bracketsForeground" => {
                    settings.brackets_foreground = Color::parse_settings(value).ok()
                }
                "bracketsBackground" => {
                    settings.brackets_background = Color::parse_settings(value).ok()
                }
                "bracketsOptions" => {
                    settings.brackets_options = UnderlineOption::parse_settings(value).ok()
                }
                "tagsForeground" => settings.tags_foreground = Color::parse_settings(value).ok(),
                "tagsOptions" => {
                    settings.tags_options = UnderlineOption::parse_settings(value).ok()
                }
                "highlight" => settings.highlight = Color::parse_settings(value).ok(),
                "findHighlight" => settings.find_highlight = Color::parse_settings(value).ok(),
                "findHighlightForeground" => {
                    settings.find_highlight_foreground = Color::parse_settings(value).ok()
                }
                "gutter" => settings.gutter = Color::parse_settings(value).ok(),
                "gutterForeground" => {
                    settings.gutter_foreground = Color::parse_settings(value).ok()
                }
                "selection" => settings.selection = Color::parse_settings(value).ok(),
                "selectionForeground" => {
                    settings.selection_foreground = Color::parse_settings(value).ok()
                }
                "selectionBorder" => settings.selection_border = Color::parse_settings(value).ok(),
                "inactiveSelection" => {
                    settings.inactive_selection = Color::parse_settings(value).ok()
                }
                "inactiveSelectionForeground" => {
                    settings.inactive_selection_foreground = Color::parse_settings(value).ok()
                }
                "guide" => settings.guide = Color::parse_settings(value).ok(),
                "activeGuide" => settings.active_guide = Color::parse_settings(value).ok(),
                "stackGuide" => settings.stack_guide = Color::parse_settings(value).ok(),
                "shadow" => settings.shadow = Color::parse_settings(value).ok(),
                "shadowWidth"| // ignored
                "invisibles" | // ignored
                _ => (),
            }
        }
        Ok(settings)
    }
}

impl ParseSettings for Theme {
    type Error = ParseThemeError;

    fn parse_settings(settings: Settings) -> Result<Theme, Self::Error> {
        let mut obj = match settings {
            Settings::Object(obj) => obj,
            _ => return Err(IncorrectSyntax),
        };
        let name = match obj.remove("name") {
            Some(Settings::String(name)) => Some(name),
            None => None,
            _ => return Err(IncorrectSyntax),
        };
        let author = match obj.remove("author") {
            Some(Settings::String(author)) => Some(author),
            None => None,
            _ => return Err(IncorrectSyntax),
        };
        let items = match obj.remove("settings") {
            Some(Settings::Array(items)) => items,
            _ => return Err(IncorrectSyntax),
        };
        let mut iter = items.into_iter();
        let settings = match iter.next() {
            Some(Settings::Object(mut obj)) => {
                match obj.remove("settings") {
                    Some(settings) => try!(ThemeSettings::parse_settings(settings)),
                    None => return Err(UndefinedSettings),
                }
            }
            _ => return Err(UndefinedSettings),
        };
        let mut scopes = Vec::new();
        for json in iter {
            // TODO option to disable best effort parsing and bubble up warnings
            if let Ok(item) = ThemeItem::parse_settings(json) {
                scopes.push(item);
            }
        }
        Ok(Theme {
            name: name,
            author: author,
            settings: settings,
            scopes: scopes,
        })
    }
}
<|endoftext|>


<commit_before>extern crate telegram_bot_tokio;
extern crate tokio_core;
extern crate futures;

use std::env;

use futures::{Future, Stream};
use tokio_core::reactor::{Core, Handle};
use telegram_bot_tokio::{Api, Message, ParseMode, MessageKind, UpdateKind};
use telegram_bot_tokio::prelude::*;

fn message_test(api: Api, message: Message, handle: &Handle) {
    let simple = api.send(&message.text_reply("Simple message"));

    let markdown = api.send(&message.text_reply("`Markdown message`")
        .parse_mode(ParseMode::Markdown)
    );

    let html = api.send(&message.text_reply("Bold HTML message")
        .parse_mode(ParseMode::Html)
    );

    handle.spawn({
        let future = simple
            .and_then(|_| markdown)
            .and_then(|_| html);

        future.map_err(|_| ()).map(|_| ())
    })
}

fn preview_test(api: Api, message: Message, handle: &Handle) {
    let preview = api.send(&message.text_reply("Message with preview https://telegram.org"));

    let no_preview = api.send(&message.text_reply("Message without preview https://telegram.org")
        .disable_web_page_preview()
    );

    handle.spawn({
        let future = preview.and_then(|_| no_preview);

        future.map_err(|_| ()).map(|_| ())
    })
}

fn reply_test(api: Api, message: Message, handle: &Handle) {
    let msg = api.send(&message.text_reply("Reply to message"));
    let chat = api.send(&message.chat.text("Text to message chat"));

    let private = message.from.as_ref().map(|from| {
        api.send(&from.text("Private text"))
    });

    handle.spawn({
        let future = msg.and_then(|_| chat).and_then(|_| private);

        future.map_err(|_| ()).map(|_| ())
    })
}

fn test_forward(api: Api, message: Message, _handle: &Handle) {
    api.spawn(&message.forward(&message.chat));

    if let Some(ref from) = message.from {
        api.spawn(&message.forward(from))
    }
}

fn test_get_chat(api: Api, message: Message, handle: &Handle) {
    let chat = api.send(&message.chat.get_chat());
    let future = chat.and_then(move |chat| {
        api.send(&chat.text(format!("Chat id {}", chat.id())))
    });

    handle.spawn({
        future.map_err(|_| ()).map(|_| ())
    })
}

fn test_get_chat_administrators(api: Api, message: Message, handle: &Handle) {
    let administrators = api.send(&message.chat.get_chat_administrators());
    let future = administrators.and_then(move |administrators| {
        let mut response = Vec::new();
        for member in administrators {
            response.push(member.user.first_name.clone())
        }
        api.send(&message.text_reply(format!("Administrators: {}", response.join(", "))))
    });

    handle.spawn({
        future.map_err(|_| ()).map(|_| ())
    })
}

fn test_leave(api: Api, message: Message, _handle: &Handle) {
    api.spawn(&message.chat.leave_chat())
}

fn test(api: Api, message: Message, handle: &Handle) {

    let function: fn(Api, Message, &Handle) = match message.kind {
        MessageKind::Text {ref data, ..} => {
            match data.as_str() {
                "/message" => message_test,
                "/preview" => preview_test,
                "/reply" => reply_test,
                "/forward" => test_forward,
                "/get_chat" => test_get_chat,
                "/get_chat_administrators" => test_get_chat_administrators,
                "/leave" => test_leave,
                _ => return,
            }
        }
        _ => return
    };

    function(api, message, handle)
}

fn main() {
    let token = env::var("TELEGRAM_BOT_TOKEN").unwrap();

    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let api = Api::from_token(&handle, &token).unwrap();

    let future = api.stream().for_each(|update| {
        if let UpdateKind::Message(message) = update.kind {
            test(api.clone(), message, &handle)
        }
        Ok(())
    });

    core.run(future).unwrap();
}
<commit_msg>refactor: rename test methods<commit_after>extern crate telegram_bot_tokio;
extern crate tokio_core;
extern crate futures;

use std::env;

use futures::{Future, Stream};
use tokio_core::reactor::{Core, Handle};
use telegram_bot_tokio::{Api, Message, ParseMode, MessageKind, UpdateKind};
use telegram_bot_tokio::prelude::*;

fn test_message(api: Api, message: Message, handle: &Handle) {
    let simple = api.send(&message.text_reply("Simple message"));

    let markdown = api.send(&message.text_reply("`Markdown message`")
        .parse_mode(ParseMode::Markdown)
    );

    let html = api.send(&message.text_reply("Bold HTML message")
        .parse_mode(ParseMode::Html)
    );

    handle.spawn({
        let future = simple
            .and_then(|_| markdown)
            .and_then(|_| html);

        future.map_err(|_| ()).map(|_| ())
    })
}

fn test_preview(api: Api, message: Message, handle: &Handle) {
    let preview = api.send(&message.text_reply("Message with preview https://telegram.org"));

    let no_preview = api.send(&message.text_reply("Message without preview https://telegram.org")
        .disable_web_page_preview()
    );

    handle.spawn({
        let future = preview.and_then(|_| no_preview);

        future.map_err(|_| ()).map(|_| ())
    })
}

fn test_reply(api: Api, message: Message, handle: &Handle) {
    let msg = api.send(&message.text_reply("Reply to message"));
    let chat = api.send(&message.chat.text("Text to message chat"));

    let private = message.from.as_ref().map(|from| {
        api.send(&from.text("Private text"))
    });

    handle.spawn({
        let future = msg.and_then(|_| chat).and_then(|_| private);

        future.map_err(|_| ()).map(|_| ())
    })
}

fn test_forward(api: Api, message: Message, _handle: &Handle) {
    api.spawn(&message.forward(&message.chat));

    if let Some(ref from) = message.from {
        api.spawn(&message.forward(from))
    }
}

fn test_get_chat(api: Api, message: Message, handle: &Handle) {
    let chat = api.send(&message.chat.get_chat());
    let future = chat.and_then(move |chat| {
        api.send(&chat.text(format!("Chat id {}", chat.id())))
    });

    handle.spawn({
        future.map_err(|_| ()).map(|_| ())
    })
}

fn test_get_chat_administrators(api: Api, message: Message, handle: &Handle) {
    let administrators = api.send(&message.chat.get_chat_administrators());
    let future = administrators.and_then(move |administrators| {
        let mut response = Vec::new();
        for member in administrators {
            response.push(member.user.first_name.clone())
        }
        api.send(&message.text_reply(format!("Administrators: {}", response.join(", "))))
    });

    handle.spawn({
        future.map_err(|_| ()).map(|_| ())
    })
}

fn test_leave(api: Api, message: Message, _handle: &Handle) {
    api.spawn(&message.chat.leave_chat())
}

fn test(api: Api, message: Message, handle: &Handle) {

    let function: fn(Api, Message, &Handle) = match message.kind {
        MessageKind::Text {ref data, ..} => {
            match data.as_str() {
                "/message" => test_message,
                "/preview" => test_preview,
                "/reply" => test_reply,
                "/forward" => test_forward,
                "/get_chat" => test_get_chat,
                "/get_chat_administrators" => test_get_chat_administrators,
                "/leave" => test_leave,
                _ => return,
            }
        }
        _ => return
    };

    function(api, message, handle)
}

fn main() {
    let token = env::var("TELEGRAM_BOT_TOKEN").unwrap();

    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let api = Api::from_token(&handle, &token).unwrap();

    let future = api.stream().for_each(|update| {
        if let UpdateKind::Message(message) = update.kind {
            test(api.clone(), message, &handle)
        }
        Ok(())
    });

    core.run(future).unwrap();
}
<|endoftext|>