<commit_before>extern crate libc;
extern crate rust_base58;
extern crate serde_json;

use self::libc::c_int;
use self::rust_base58::FromBase58;
use std::cell::RefCell;
use std::collections::HashMap;
use std::{fmt, fs, io, thread};
use std::fmt::Debug;
use std::io::{BufRead, Write};
use rustc_serialize::json;
use zmq;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::pool::PoolError;
use utils::sequence::SequenceUtils;
use utils::environment::EnvironmentUtils;

pub struct PoolService {
    pools: RefCell<HashMap<i32, Pool>>,
}

struct Pool {
    name: String,
    id: i32,
    send_sock: zmq::Socket,
    worker: Option<thread::JoinHandle<()>>,
}

struct PoolWorker {
    cmd_sock: zmq::Socket,
    open_cmd_id: i32,
    pool_id: i32,
    nodes: Vec<RemoteNode>,
}

impl PoolWorker {
    fn connect_to_known_nodes(&mut self) {
        let f = fs::File::open("pool_transactions_sandbox").expect("open file"); //FIXME use file for the pool
        let reader = io::BufReader::new(&f);
        for line in reader.lines() {
            let line: String = line.expect("read transaction line");
            let mut rn: RemoteNode = RemoteNode::new(line.as_str());
            let ctx: zmq::Context = zmq::Context::new();
            rn.connect(&ctx);
            rn.zsock.as_ref().unwrap().send("pi".as_bytes(), 0).expect("send ping");
            self.nodes.push(rn);
        }
    }

    pub fn run(&mut self) {
        self.connect_to_known_nodes();
        let mut ss_to_poll: Vec<zmq::PollItem> = Vec::new();
        ss_to_poll.push(self.cmd_sock.as_poll_item(zmq::POLLIN));
        for ref node in &self.nodes {
            let s: &zmq::Socket = node.zsock.as_ref().unwrap();
            ss_to_poll.push(s.as_poll_item(zmq::POLLIN));
        }
        CommandExecutor::instance().send(Command::Pool(
            PoolCommand::OpenAck(self.open_cmd_id, Ok(self.pool_id)))).expect("send ack cmd"); //TODO send only after catch-up?
        loop {
            trace!("zmq poll loop >>");
            let r = zmq::poll(ss_to_poll.as_mut_slice(), -1).expect("poll");
            trace!("zmq poll loop ...");
            for ref node in &self.nodes {
                let s: &zmq::Socket = node.zsock.as_ref().unwrap();
                trace!("{:?}", s.recv_string(zmq::DONTWAIT));
            }
            let cmd = self.cmd_sock.recv_string(zmq::DONTWAIT);
            trace!("{:?}", cmd);
            if cmd.is_ok() {
                if "exit".eq(cmd.unwrap().expect("non-string command").as_str()) {
                    break;
                }
            }
            trace!("zmq poll loop << ret {:?}", r);
        }
        info!("zmq poll loop finished");
    }
}

impl Pool {
    pub fn new(name: &str, cmd_id: i32) -> Result<Pool, PoolError> {
        let zmq_ctx = zmq::Context::new();
        let zmq_ctx = zmq::Context::new();
        let recv_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let send_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let inproc_sock_name: String = format!("inproc://pool_{}", name);

        recv_cmd_sock.bind(inproc_sock_name.as_str())?;

        send_cmd_sock.connect(inproc_sock_name.as_str())?;
        let pool_id = SequenceUtils::get_next_id();
        let mut pool_worker: PoolWorker = PoolWorker {
            cmd_sock: recv_cmd_sock,
            open_cmd_id: cmd_id,
            pool_id: pool_id,
            nodes: Vec::new(),
        };

        Ok(Pool {
            name: name.to_string(),
            id: pool_id,
            send_sock: send_cmd_sock,
            worker: Some(thread::spawn(move || {
                pool_worker.run();
            })),
        })
    }
}

impl Drop for Pool {
    fn drop(&mut self) {
        let target = format!("pool{}", self.name);
        info!(target: target.as_str(), "Drop started");
        self.send_sock.send("exit".as_bytes(), 0).expect("send exit command"); //TODO
        info!(target: target.as_str(), "Drop wait worker");
        // Option worker type and this kludge is workaround for rust
        self.worker.take().unwrap().join().unwrap();
        info!(target: target.as_str(), "Drop finished");
    }
}

#[derive(RustcDecodable, RustcEncodable)]
struct PoolConfig {
    genesis_txn: String
}

impl PoolConfig {
    fn default(name: &str) -> PoolConfig {
        let mut txn = name.to_string();
        txn += ".txn";
        PoolConfig { genesis_txn: txn }
    }
}

#[derive(Serialize, Deserialize, Debug)]
struct NodeData {
    alias: String,
    client_ip: String,
    client_port: u32,
    node_ip: String,
    node_port: u32,
    services: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
struct GenTransaction {
    data: NodeData,
    dest: String,
    identifier: String,
    #[serde(rename = "txnId")]
    txn_id: String,
    #[serde(rename = "type")]
    txn_type: String,
}

struct RemoteNode {
    public_key: Vec<u8>,
    verify_key: Vec<u8>,
    zaddr: String,
    zsock: Option<zmq::Socket>,
}

impl Debug for RemoteNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RemoteNode: {{ public_key: {:?}, verify_key {:?}, zaddr {:?}, zsock is_some {} }}",
               self.public_key, self.verify_key, self.zaddr, self.zsock.is_some())
    }
}

impl RemoteNode {
    fn new(txn: &str) -> RemoteNode {
        let gen_tx: GenTransaction = serde_json::from_str(txn).expect("RemoteNode parsing");
        RemoteNode::from(gen_tx)
    }

    fn connect(&mut self, ctx: &zmq::Context) {
        let key_pair = zmq::CurveKeyPair::new().expect("create key pair");
        let s = ctx.socket(zmq::SocketType::DEALER).expect("socket for Node");
        s.set_curve_secretkey(key_pair.secret_key.as_str()).expect("set secret key");
        s.set_curve_publickey(key_pair.public_key.as_str()).expect("set public key");
        s.set_curve_serverkey(zmq::z85_encode(self.verify_key.as_slice()).unwrap().as_str()).expect("set verify key");
        s.connect(self.zaddr.as_str()).expect("connect to Node");
        self.zsock = Some(s);
    }
}

impl From<GenTransaction> for RemoteNode {
    fn from(tx: GenTransaction) -> RemoteNode {
        fn crypto_sign_ed25519_pk_to_curve25519(pk: &Vec<u8>) -> Vec<u8> {
            // TODO: fix hack:
            // this function isn't included to sodiumoxide rust wrappers,
            // temporary local binding is used to call libsodium-sys function
            extern {
                pub fn crypto_sign_ed25519_pk_to_curve25519(
                    curve25519_pk: *mut [u8; 32],
                    ed25519_pk: *const [u8; 32]) -> c_int;
            }
            let mut from: [u8; 32] = [0; 32];
            from.clone_from_slice(pk.as_slice());
            let mut to: [u8; 32] = [0; 32];
            unsafe {
                crypto_sign_ed25519_pk_to_curve25519(&mut to, &from);
            }
            to.iter().cloned().collect()
        }

        let public_key = tx.dest.as_str().from_base58().expect("dest field in GenTransaction isn't valid");
        RemoteNode {
            verify_key: crypto_sign_ed25519_pk_to_curve25519(&public_key),
            public_key: public_key,
            zaddr: format!("tcp://{}:{}", tx.data.client_ip, tx.data.client_port),
            zsock: None,
        }
    }
}

impl PoolService {
    pub fn new() -> PoolService {
        PoolService {
            pools: RefCell::new(HashMap::new()),
        }
    }

    pub fn create(&self, name: &str, config: Option<&str>) -> Result<(), PoolError> {
        let mut path = EnvironmentUtils::pool_path(name);
        let pool_config: PoolConfig = match config {
            Some(config) => json::decode(config)?,
            None => PoolConfig::default(name)
        };

        if path.as_path().exists() {
            return Err(PoolError::NotCreated("Already created".to_string()));
        }

        fs::create_dir_all(path.as_path())?;

        path.push(name);
        path.set_extension("txn");
        fs::copy(&pool_config.genesis_txn, path.as_path())?;
        path.pop();

        path.push("config");
        path.set_extension("json");
        let mut f: fs::File = fs::File::create(path.as_path())?;
        f.write(json::encode(&pool_config)?.as_bytes())?;
        f.flush()?;

        // TODO probably create another one file pool.json with pool description,
        // but now there is no info to save (except name witch equal to directory)

        Ok(())
    }

    pub fn delete(&self, name: &str) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn open(&self, name: &str, config: Option<&str>) -> Result<i32, PoolError> {
        for pool in self.pools.borrow().values() {
            if name.eq(pool.name.as_str()) {
                //TODO change error
                return Err(PoolError::InvalidHandle("Already opened".to_string()));
            }
        }

        let cmd_id: i32 = SequenceUtils::get_next_id();
        let new_pool = Pool::new(name, cmd_id)?;
        //FIXME process config: check None (use default), transfer to Pool instance

        self.pools.borrow_mut().insert(new_pool.id, new_pool);
        return Ok(cmd_id);
    }

    pub fn close(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn get_pool_name(&self, handle: i32) -> Result<String, PoolError> {
        self.pools.borrow().get(&handle).map_or(
            Err(PoolError::InvalidHandle("Doesn't exists".to_string())),
            |pool: &Pool| Ok(pool.name.clone()))
    }
}

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

    use std::cell::RefCell;

    pub struct PoolService {
        create_results: RefCell<Vec<Result<(), PoolError>>>,
        delete_results: RefCell<Vec<Result<(), PoolError>>>,
        open_results: RefCell<Vec<Result<i32, PoolError>>>,
        close_results: RefCell<Vec<Result<(), PoolError>>>,
        refresh_results: RefCell<Vec<Result<(), PoolError>>>
    }

    impl PoolService {
        pub fn new() -> PoolService {
            PoolService {
                create_results: RefCell::new(Vec::new()),
                delete_results: RefCell::new(Vec::new()),
                open_results: RefCell::new(Vec::new()),
                close_results: RefCell::new(Vec::new()),
                refresh_results: RefCell::new(Vec::new())
            }
        }

        pub fn create(&self, name: &str, config: &str) -> Result<(), PoolError> {
            //self.create_results.pop().unwrap()
            unimplemented!()
        }

        pub fn delete(&self, name: &str) -> Result<(), PoolError> {
            //self.delete_results.pop().unwrap()
            unimplemented!()
        }

        pub fn open(&self, name: &str, config: &str) -> Result<i32, PoolError> {
            //self.open_results.pop().unwrap()
            unimplemented!()
        }

        pub fn close(&self, handle: i32) -> Result<(), PoolError> {
            //self.close_results.pop().unwrap()
            unimplemented!()
        }

        pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
            //self.refresh_results.pop().unwrap()
            unimplemented!()
        }
    }
}

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

    #[test]
    fn pool_service_can_be_created() {
        let pool_service = PoolService::new();
        assert!(true, "No crashes on PoolService::new");
    }

    #[test]
    fn pool_service_can_be_dropped() {
        fn drop_test() {
            let pool_service = PoolService::new();
        }

        drop_test();
        assert!(true, "No crashes on PoolService::drop");
    }
}<commit_msg>Refactor zmq poll loop, add method to preprocess message for RemoteNode.<commit_after>extern crate libc;
extern crate rust_base58;
extern crate serde_json;

use self::libc::c_int;
use self::rust_base58::FromBase58;
use std::cell::RefCell;
use std::collections::HashMap;
use std::{fmt, fs, io, thread};
use std::fmt::Debug;
use std::io::{BufRead, Write};
use rustc_serialize::json;
use zmq;

use commands::{Command, CommandExecutor};
use commands::pool::PoolCommand;
use errors::pool::PoolError;
use utils::sequence::SequenceUtils;
use utils::environment::EnvironmentUtils;

pub struct PoolService {
    pools: RefCell<HashMap<i32, Pool>>,
}

struct Pool {
    name: String,
    id: i32,
    send_sock: zmq::Socket,
    worker: Option<thread::JoinHandle<()>>,
}

struct PoolWorker {
    cmd_sock: zmq::Socket,
    open_cmd_id: i32,
    pool_id: i32,
    nodes: Vec<RemoteNode>,
}

impl PoolWorker {
    fn connect_to_known_nodes(&mut self) {
        let f = fs::File::open("pool_transactions_sandbox").expect("open file"); //FIXME use file for the pool
        let reader = io::BufReader::new(&f);
        for line in reader.lines() {
            let line: String = line.expect("read transaction line");
            let mut rn: RemoteNode = RemoteNode::new(line.as_str());
            let ctx: zmq::Context = zmq::Context::new();
            rn.connect(&ctx);
            rn.zsock.as_ref().unwrap().send("pi".as_bytes(), 0).expect("send ping");
            self.nodes.push(rn);
        }
    }

    pub fn run(&mut self) {
        self.connect_to_known_nodes();
        let mut ss_to_poll: Vec<zmq::PollItem> = Vec::new();
        ss_to_poll.push(self.cmd_sock.as_poll_item(zmq::POLLIN));
        for ref node in &self.nodes {
            let s: &zmq::Socket = node.zsock.as_ref().unwrap();
            ss_to_poll.push(s.as_poll_item(zmq::POLLIN));
        }
        CommandExecutor::instance().send(Command::Pool(
            PoolCommand::OpenAck(self.open_cmd_id, Ok(self.pool_id)))).expect("send ack cmd"); //TODO send only after catch-up?
        loop {
            trace!("zmq poll loop >>");
            let r = zmq::poll(ss_to_poll.as_mut_slice(), -1).expect("poll");
            trace!("zmq poll loop ... {:?}", r);
            for i in 0..self.nodes.len() {
                if ss_to_poll[1 + i].is_readable() {
                    self.nodes[i].recv_msg().expect("recv msg");
                }
            }
            if ss_to_poll[0].is_readable() {
                let cmd = self.cmd_sock.recv_string(zmq::DONTWAIT);
                trace!("cmd {:?}", cmd);
                if cmd.is_ok() {
                    if "exit".eq(cmd.unwrap().expect("non-string command").as_str()) {
                        break;
                    }
                }
            }
            trace!("zmq poll loop <<");
        }
        info!("zmq poll loop finished");
    }
}

impl Pool {
    pub fn new(name: &str, cmd_id: i32) -> Result<Pool, PoolError> {
        let zmq_ctx = zmq::Context::new();
        let recv_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let send_cmd_sock = zmq_ctx.socket(zmq::SocketType::PAIR)?;
        let inproc_sock_name: String = format!("inproc://pool_{}", name);

        recv_cmd_sock.bind(inproc_sock_name.as_str())?;

        send_cmd_sock.connect(inproc_sock_name.as_str())?;
        let pool_id = SequenceUtils::get_next_id();
        let mut pool_worker: PoolWorker = PoolWorker {
            cmd_sock: recv_cmd_sock,
            open_cmd_id: cmd_id,
            pool_id: pool_id,
            nodes: Vec::new(),
        };

        Ok(Pool {
            name: name.to_string(),
            id: pool_id,
            send_sock: send_cmd_sock,
            worker: Some(thread::spawn(move || {
                pool_worker.run();
            })),
        })
    }
}

impl Drop for Pool {
    fn drop(&mut self) {
        let target = format!("pool{}", self.name);
        info!(target: target.as_str(), "Drop started");
        self.send_sock.send("exit".as_bytes(), 0).expect("send exit command"); //TODO
        info!(target: target.as_str(), "Drop wait worker");
        // Option worker type and this kludge is workaround for rust
        self.worker.take().unwrap().join().unwrap();
        info!(target: target.as_str(), "Drop finished");
    }
}

#[derive(RustcDecodable, RustcEncodable)]
struct PoolConfig {
    genesis_txn: String
}

impl PoolConfig {
    fn default(name: &str) -> PoolConfig {
        let mut txn = name.to_string();
        txn += ".txn";
        PoolConfig { genesis_txn: txn }
    }
}

#[derive(Serialize, Deserialize, Debug)]
struct NodeData {
    alias: String,
    client_ip: String,
    client_port: u32,
    node_ip: String,
    node_port: u32,
    services: Vec<String>,
}

#[derive(Serialize, Deserialize, Debug)]
struct GenTransaction {
    data: NodeData,
    dest: String,
    identifier: String,
    #[serde(rename = "txnId")]
    txn_id: String,
    #[serde(rename = "type")]
    txn_type: String,
}

struct RemoteNode {
    name: String,
    public_key: Vec<u8>,
    verify_key: Vec<u8>,
    zaddr: String,
    zsock: Option<zmq::Socket>,
}

impl Debug for RemoteNode {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "RemoteNode: {{ public_key: {:?}, verify_key {:?}, zaddr {:?}, zsock is_some {} }}",
               self.public_key, self.verify_key, self.zaddr, self.zsock.is_some())
    }
}

impl RemoteNode {
    fn new(txn: &str) -> RemoteNode {
        let gen_tx: GenTransaction = serde_json::from_str(txn).expect("RemoteNode parsing");
        RemoteNode::from(gen_tx)
    }

    fn connect(&mut self, ctx: &zmq::Context) {
        let key_pair = zmq::CurveKeyPair::new().expect("create key pair");
        let s = ctx.socket(zmq::SocketType::DEALER).expect("socket for Node");
        s.set_curve_secretkey(key_pair.secret_key.as_str()).expect("set secret key");
        s.set_curve_publickey(key_pair.public_key.as_str()).expect("set public key");
        s.set_curve_serverkey(zmq::z85_encode(self.verify_key.as_slice()).unwrap().as_str()).expect("set verify key");
        s.connect(self.zaddr.as_str()).expect("connect to Node");
        self.zsock = Some(s);
    }

    fn recv_msg(&self) -> Result<Option<String>, PoolError> {
        impl From<Vec<u8>> for PoolError {
            fn from(_: Vec<u8>) -> Self {
                PoolError::Io(io::Error::from(io::ErrorKind::InvalidData))
            }
        }
        let msg: String = self.zsock.as_ref().unwrap().recv_string(zmq::DONTWAIT)??;
        info!(target: "RemoteNode_recv_msg", "{} {}", self.name, msg);

        match msg.as_ref() {
            "po" | "pi" => Ok(None), //TODO send pong, update last available?
            _ => Ok(Some(msg))
        }
    }
}

impl From<GenTransaction> for RemoteNode {
    fn from(tx: GenTransaction) -> RemoteNode {
        fn crypto_sign_ed25519_pk_to_curve25519(pk: &Vec<u8>) -> Vec<u8> {
            // TODO: fix hack:
            // this function isn't included to sodiumoxide rust wrappers,
            // temporary local binding is used to call libsodium-sys function
            extern {
                pub fn crypto_sign_ed25519_pk_to_curve25519(
                    curve25519_pk: *mut [u8; 32],
                    ed25519_pk: *const [u8; 32]) -> c_int;
            }
            let mut from: [u8; 32] = [0; 32];
            from.clone_from_slice(pk.as_slice());
            let mut to: [u8; 32] = [0; 32];
            unsafe {
                crypto_sign_ed25519_pk_to_curve25519(&mut to, &from);
            }
            to.iter().cloned().collect()
        }

        let public_key = tx.dest.as_str().from_base58().expect("dest field in GenTransaction isn't valid");
        RemoteNode {
            verify_key: crypto_sign_ed25519_pk_to_curve25519(&public_key),
            public_key: public_key,
            zaddr: format!("tcp://{}:{}", tx.data.client_ip, tx.data.client_port),
            zsock: None,
            name: tx.data.alias,
        }
    }
}

impl PoolService {
    pub fn new() -> PoolService {
        PoolService {
            pools: RefCell::new(HashMap::new()),
        }
    }

    pub fn create(&self, name: &str, config: Option<&str>) -> Result<(), PoolError> {
        let mut path = EnvironmentUtils::pool_path(name);
        let pool_config: PoolConfig = match config {
            Some(config) => json::decode(config)?,
            None => PoolConfig::default(name)
        };

        if path.as_path().exists() {
            return Err(PoolError::NotCreated("Already created".to_string()));
        }

        fs::create_dir_all(path.as_path())?;

        path.push(name);
        path.set_extension("txn");
        fs::copy(&pool_config.genesis_txn, path.as_path())?;
        path.pop();

        path.push("config");
        path.set_extension("json");
        let mut f: fs::File = fs::File::create(path.as_path())?;
        f.write(json::encode(&pool_config)?.as_bytes())?;
        f.flush()?;

        // TODO probably create another one file pool.json with pool description,
        // but now there is no info to save (except name witch equal to directory)

        Ok(())
    }

    pub fn delete(&self, name: &str) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn open(&self, name: &str, config: Option<&str>) -> Result<i32, PoolError> {
        for pool in self.pools.borrow().values() {
            if name.eq(pool.name.as_str()) {
                //TODO change error
                return Err(PoolError::InvalidHandle("Already opened".to_string()));
            }
        }

        let cmd_id: i32 = SequenceUtils::get_next_id();
        let new_pool = Pool::new(name, cmd_id)?;
        //FIXME process config: check None (use default), transfer to Pool instance

        self.pools.borrow_mut().insert(new_pool.id, new_pool);
        return Ok(cmd_id);
    }

    pub fn close(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
        unimplemented!()
    }

    pub fn get_pool_name(&self, handle: i32) -> Result<String, PoolError> {
        self.pools.borrow().get(&handle).map_or(
            Err(PoolError::InvalidHandle("Doesn't exists".to_string())),
            |pool: &Pool| Ok(pool.name.clone()))
    }
}

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

    use std::cell::RefCell;

    pub struct PoolService {
        create_results: RefCell<Vec<Result<(), PoolError>>>,
        delete_results: RefCell<Vec<Result<(), PoolError>>>,
        open_results: RefCell<Vec<Result<i32, PoolError>>>,
        close_results: RefCell<Vec<Result<(), PoolError>>>,
        refresh_results: RefCell<Vec<Result<(), PoolError>>>
    }

    impl PoolService {
        pub fn new() -> PoolService {
            PoolService {
                create_results: RefCell::new(Vec::new()),
                delete_results: RefCell::new(Vec::new()),
                open_results: RefCell::new(Vec::new()),
                close_results: RefCell::new(Vec::new()),
                refresh_results: RefCell::new(Vec::new())
            }
        }

        pub fn create(&self, name: &str, config: &str) -> Result<(), PoolError> {
            //self.create_results.pop().unwrap()
            unimplemented!()
        }

        pub fn delete(&self, name: &str) -> Result<(), PoolError> {
            //self.delete_results.pop().unwrap()
            unimplemented!()
        }

        pub fn open(&self, name: &str, config: &str) -> Result<i32, PoolError> {
            //self.open_results.pop().unwrap()
            unimplemented!()
        }

        pub fn close(&self, handle: i32) -> Result<(), PoolError> {
            //self.close_results.pop().unwrap()
            unimplemented!()
        }

        pub fn refresh(&self, handle: i32) -> Result<(), PoolError> {
            //self.refresh_results.pop().unwrap()
            unimplemented!()
        }
    }
}

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

    #[test]
    fn pool_service_can_be_created() {
        let pool_service = PoolService::new();
        assert!(true, "No crashes on PoolService::new");
    }

    #[test]
    fn pool_service_can_be_dropped() {
        fn drop_test() {
            let pool_service = PoolService::new();
        }

        drop_test();
        assert!(true, "No crashes on PoolService::drop");
    }
}<|endoftext|>


<commit_before>use std::ptr;
use std::sync::mpsc;

use Display;
use pixel_buffer::{self, PixelBuffer};
use texture::ClientFormat;

use fbo;
use texture;

use GlObject;
use libc;
use context;
use gl;

pub fn read_attachment<P, T>(attachment: &fbo::Attachment, dimensions: (u32, u32),
                             display: &Display) -> T          // TODO: remove Clone for P
                             where P: texture::PixelValue + Clone + Send,
                             T: texture::Texture2dDataSink<Data = P>
{
    let (fbo, atch) = display.context.framebuffer_objects.as_ref().unwrap()
                             .get_framebuffer_for_reading(attachment, &display.context.context);
    read_impl(fbo, atch, dimensions, None, &display.context.context).unwrap()
}

/// Panics if the pixel buffer is not big enough.
pub fn read_attachment_to_pb<P, T>(attachment: &fbo::Attachment, dimensions: (u32, u32),
                                   dest: &mut PixelBuffer<T>, display: &Display)          // TODO: remove Clone for P
                                   where P: texture::PixelValue + Clone + Send,
                                   T: texture::Texture2dDataSink<Data = P>
{
    let (fbo, atch) = display.context.framebuffer_objects.as_ref().unwrap()
                             .get_framebuffer_for_reading(attachment, &display.context.context);
    read_impl(fbo, atch, dimensions, Some(dest), &display.context.context);
}

pub fn read_from_default_fb<P, T>(attachment: gl::types::GLenum, display: &Display) -> T          // TODO: remove Clone for P
                                  where P: texture::PixelValue + Clone + Send,
                                  T: texture::Texture2dDataSink<Data = P>
{
    let (w, h) = display.get_framebuffer_dimensions();
    let (w, h) = (w as u32, h as u32);      // TODO: remove this conversion
    read_impl(0, attachment, (w, h), None, &display.context.context).unwrap()
}

/// Panics if the pixel buffer is not big enough.
pub fn read_from_default_fb_to_pb<P, T>(attachment: gl::types::GLenum,
                                        dest: &mut PixelBuffer<T>, display: &Display)          // TODO: remove Clone for P
                                        where P: texture::PixelValue + Clone + Send,
                                        T: texture::Texture2dDataSink<Data = P>
{
    let (w, h) = display.get_framebuffer_dimensions();
    let (w, h) = (w as u32, h as u32);      // TODO: remove this conversion
    read_impl(0, attachment, (w, h), Some(dest), &display.context.context);
}

fn read_impl<P, T>(fbo: gl::types::GLuint, readbuffer: gl::types::GLenum,
                   dimensions: (u32, u32), target: Option<&mut PixelBuffer<T>>,
                   context: &context::Context) -> Option<T>          // TODO: remove Clone for P
                   where P: texture::PixelValue + Clone + Send,
                   T: texture::Texture2dDataSink<Data = P>
{
    use std::mem;

    let pixels_count = dimensions.0 * dimensions.1;

    let chosen_format = <T as texture::Texture2dDataSink>::get_preferred_formats()[0];
    let pixels_size = chosen_format.get_size();
    let (format, gltype) = client_format_to_gl_enum(&chosen_format);

    let total_data_size = pixels_count as usize * pixels_size;

    let (tx, rx) = if target.is_none() {
        let (tx, rx) = mpsc::channel();
        (Some(tx), Some(rx))
    } else {
        (None, None)
    };

    let pixel_buffer = target.as_ref().map(|buf| buf.get_id()).unwrap_or(0);

    if let Some(pixel_buffer) = target {
        assert!(pixel_buffer.get_size() >= total_data_size);
        pixel_buffer::store_infos(pixel_buffer, dimensions, chosen_format);
    }

    context.exec(move |: mut ctxt| {
        unsafe {
            // binding framebuffer
            fbo::bind_framebuffer(&mut ctxt, fbo, false, true);

            // adjusting glReadBuffer
            ctxt.gl.ReadBuffer(readbuffer);

            // adjusting data alignement
            if ctxt.state.pixel_store_pack_alignment != 1 {
                ctxt.state.pixel_store_pack_alignment = 1;
                ctxt.gl.PixelStorei(gl::PACK_ALIGNMENT, 1);
            }

            // binding buffer
            if ctxt.state.pixel_pack_buffer_binding != pixel_buffer {
                ctxt.gl.BindBuffer(gl::PIXEL_PACK_BUFFER, pixel_buffer);
                ctxt.state.pixel_pack_buffer_binding = pixel_buffer;
            }

            // reading
            if pixel_buffer == 0 {
                let data_size = pixels_count as usize * pixels_size / mem::size_of::<P>();
                let mut data: Vec<P> = Vec::with_capacity(data_size);
                ctxt.gl.ReadPixels(0, 0, dimensions.0 as gl::types::GLint,
                                   dimensions.1 as gl::types::GLint, format, gltype,
                                   data.as_mut_ptr() as *mut libc::c_void);
                data.set_len(data_size);
                tx.unwrap().send(data).ok();

            } else {
                ctxt.gl.ReadPixels(0, 0, dimensions.0 as gl::types::GLint,
                                   dimensions.1 as gl::types::GLint, format, gltype,
                                   ptr::null_mut());
            }
        }
    });

    rx.map(|rx| {
        let data = texture::RawImage2d {
            data: ::std::borrow::Cow::Owned(rx.recv().unwrap()),
            width: dimensions.0 as u32,
            height: dimensions.1 as u32,
            format: chosen_format,
        };

        texture::Texture2dDataSink::from_raw(data)
    })
}

fn client_format_to_gl_enum(format: &ClientFormat) -> (gl::types::GLenum, gl::types::GLenum) {
    match *format {
        ClientFormat::U8 => (gl::RED, gl::UNSIGNED_BYTE),
        ClientFormat::U8U8 => (gl::RG, gl::UNSIGNED_BYTE),
        ClientFormat::U8U8U8 => (gl::RGB, gl::UNSIGNED_BYTE),
        ClientFormat::U8U8U8U8 => (gl::RGBA, gl::UNSIGNED_BYTE),
        ClientFormat::I8 => (gl::RED, gl::BYTE),
        ClientFormat::I8I8 => (gl::RG, gl::BYTE),
        ClientFormat::I8I8I8 => (gl::RGB, gl::BYTE),
        ClientFormat::I8I8I8I8 => (gl::RGBA, gl::BYTE),
        ClientFormat::U16 => (gl::RED, gl::UNSIGNED_SHORT),
        ClientFormat::U16U16 => (gl::RG, gl::UNSIGNED_SHORT),
        ClientFormat::U16U16U16 => (gl::RGB, gl::UNSIGNED_SHORT),
        ClientFormat::U16U16U16U16 => (gl::RGBA, gl::UNSIGNED_SHORT),
        ClientFormat::I16 => (gl::RED, gl::SHORT),
        ClientFormat::I16I16 => (gl::RG, gl::SHORT),
        ClientFormat::I16I16I16 => (gl::RGB, gl::SHORT),
        ClientFormat::I16I16I16I16 => (gl::RGBA, gl::SHORT),
        ClientFormat::U32 => (gl::RED, gl::UNSIGNED_INT),
        ClientFormat::U32U32 => (gl::RG, gl::UNSIGNED_INT),
        ClientFormat::U32U32U32 => (gl::RGB, gl::UNSIGNED_INT),
        ClientFormat::U32U32U32U32 => (gl::RGBA, gl::UNSIGNED_INT),
        ClientFormat::I32 => (gl::RED, gl::INT),
        ClientFormat::I32I32 => (gl::RG, gl::INT),
        ClientFormat::I32I32I32 => (gl::RGB, gl::INT),
        ClientFormat::I32I32I32I32 => (gl::RGBA, gl::INT),
        ClientFormat::U3U3U2 => (gl::RGB, gl::UNSIGNED_BYTE_3_3_2),
        ClientFormat::U5U6U5 => (gl::RGB, gl::UNSIGNED_SHORT_5_6_5),
        ClientFormat::U4U4U4U4 => (gl::RGBA, gl::UNSIGNED_SHORT_4_4_4_4),
        ClientFormat::U5U5U5U1 => (gl::RGBA, gl::UNSIGNED_SHORT_5_5_5_1),
        ClientFormat::U10U10U10U2 => (gl::RGBA, gl::UNSIGNED_INT_10_10_10_2),
        ClientFormat::F16 => (gl::RED, gl::HALF_FLOAT),
        ClientFormat::F16F16 => (gl::RG, gl::HALF_FLOAT),
        ClientFormat::F16F16F16 => (gl::RGB, gl::HALF_FLOAT),
        ClientFormat::F16F16F16F16 => (gl::RGBA, gl::HALF_FLOAT),
        ClientFormat::F32 => (gl::RED, gl::FLOAT),
        ClientFormat::F32F32 => (gl::RG, gl::FLOAT),
        ClientFormat::F32F32F32 => (gl::RGB, gl::FLOAT),
        ClientFormat::F32F32F32F32 => (gl::RGBA, gl::FLOAT),
    }
}
<commit_msg>Remove unnecessary conversion<commit_after>use std::ptr;
use std::sync::mpsc;

use Display;
use pixel_buffer::{self, PixelBuffer};
use texture::ClientFormat;

use fbo;
use texture;

use GlObject;
use libc;
use context;
use gl;

pub fn read_attachment<P, T>(attachment: &fbo::Attachment, dimensions: (u32, u32),
                             display: &Display) -> T          // TODO: remove Clone for P
                             where P: texture::PixelValue + Clone + Send,
                             T: texture::Texture2dDataSink<Data = P>
{
    let (fbo, atch) = display.context.framebuffer_objects.as_ref().unwrap()
                             .get_framebuffer_for_reading(attachment, &display.context.context);
    read_impl(fbo, atch, dimensions, None, &display.context.context).unwrap()
}

/// Panics if the pixel buffer is not big enough.
pub fn read_attachment_to_pb<P, T>(attachment: &fbo::Attachment, dimensions: (u32, u32),
                                   dest: &mut PixelBuffer<T>, display: &Display)          // TODO: remove Clone for P
                                   where P: texture::PixelValue + Clone + Send,
                                   T: texture::Texture2dDataSink<Data = P>
{
    let (fbo, atch) = display.context.framebuffer_objects.as_ref().unwrap()
                             .get_framebuffer_for_reading(attachment, &display.context.context);
    read_impl(fbo, atch, dimensions, Some(dest), &display.context.context);
}

pub fn read_from_default_fb<P, T>(attachment: gl::types::GLenum, display: &Display) -> T          // TODO: remove Clone for P
                                  where P: texture::PixelValue + Clone + Send,
                                  T: texture::Texture2dDataSink<Data = P>
{
    let (w, h) = display.get_framebuffer_dimensions();
    let (w, h) = (w as u32, h as u32);      // TODO: remove this conversion
    read_impl(0, attachment, (w, h), None, &display.context.context).unwrap()
}

/// Panics if the pixel buffer is not big enough.
pub fn read_from_default_fb_to_pb<P, T>(attachment: gl::types::GLenum,
                                        dest: &mut PixelBuffer<T>, display: &Display)          // TODO: remove Clone for P
                                        where P: texture::PixelValue + Clone + Send,
                                        T: texture::Texture2dDataSink<Data = P>
{
    let (w, h) = display.get_framebuffer_dimensions();
    read_impl(0, attachment, (w, h), Some(dest), &display.context.context);
}

fn read_impl<P, T>(fbo: gl::types::GLuint, readbuffer: gl::types::GLenum,
                   dimensions: (u32, u32), target: Option<&mut PixelBuffer<T>>,
                   context: &context::Context) -> Option<T>          // TODO: remove Clone for P
                   where P: texture::PixelValue + Clone + Send,
                   T: texture::Texture2dDataSink<Data = P>
{
    use std::mem;

    let pixels_count = dimensions.0 * dimensions.1;

    let chosen_format = <T as texture::Texture2dDataSink>::get_preferred_formats()[0];
    let pixels_size = chosen_format.get_size();
    let (format, gltype) = client_format_to_gl_enum(&chosen_format);

    let total_data_size = pixels_count as usize * pixels_size;

    let (tx, rx) = if target.is_none() {
        let (tx, rx) = mpsc::channel();
        (Some(tx), Some(rx))
    } else {
        (None, None)
    };

    let pixel_buffer = target.as_ref().map(|buf| buf.get_id()).unwrap_or(0);

    if let Some(pixel_buffer) = target {
        assert!(pixel_buffer.get_size() >= total_data_size);
        pixel_buffer::store_infos(pixel_buffer, dimensions, chosen_format);
    }

    context.exec(move |: mut ctxt| {
        unsafe {
            // binding framebuffer
            fbo::bind_framebuffer(&mut ctxt, fbo, false, true);

            // adjusting glReadBuffer
            ctxt.gl.ReadBuffer(readbuffer);

            // adjusting data alignement
            if ctxt.state.pixel_store_pack_alignment != 1 {
                ctxt.state.pixel_store_pack_alignment = 1;
                ctxt.gl.PixelStorei(gl::PACK_ALIGNMENT, 1);
            }

            // binding buffer
            if ctxt.state.pixel_pack_buffer_binding != pixel_buffer {
                ctxt.gl.BindBuffer(gl::PIXEL_PACK_BUFFER, pixel_buffer);
                ctxt.state.pixel_pack_buffer_binding = pixel_buffer;
            }

            // reading
            if pixel_buffer == 0 {
                let data_size = pixels_count as usize * pixels_size / mem::size_of::<P>();
                let mut data: Vec<P> = Vec::with_capacity(data_size);
                ctxt.gl.ReadPixels(0, 0, dimensions.0 as gl::types::GLint,
                                   dimensions.1 as gl::types::GLint, format, gltype,
                                   data.as_mut_ptr() as *mut libc::c_void);
                data.set_len(data_size);
                tx.unwrap().send(data).ok();

            } else {
                ctxt.gl.ReadPixels(0, 0, dimensions.0 as gl::types::GLint,
                                   dimensions.1 as gl::types::GLint, format, gltype,
                                   ptr::null_mut());
            }
        }
    });

    rx.map(|rx| {
        let data = texture::RawImage2d {
            data: ::std::borrow::Cow::Owned(rx.recv().unwrap()),
            width: dimensions.0 as u32,
            height: dimensions.1 as u32,
            format: chosen_format,
        };

        texture::Texture2dDataSink::from_raw(data)
    })
}

fn client_format_to_gl_enum(format: &ClientFormat) -> (gl::types::GLenum, gl::types::GLenum) {
    match *format {
        ClientFormat::U8 => (gl::RED, gl::UNSIGNED_BYTE),
        ClientFormat::U8U8 => (gl::RG, gl::UNSIGNED_BYTE),
        ClientFormat::U8U8U8 => (gl::RGB, gl::UNSIGNED_BYTE),
        ClientFormat::U8U8U8U8 => (gl::RGBA, gl::UNSIGNED_BYTE),
        ClientFormat::I8 => (gl::RED, gl::BYTE),
        ClientFormat::I8I8 => (gl::RG, gl::BYTE),
        ClientFormat::I8I8I8 => (gl::RGB, gl::BYTE),
        ClientFormat::I8I8I8I8 => (gl::RGBA, gl::BYTE),
        ClientFormat::U16 => (gl::RED, gl::UNSIGNED_SHORT),
        ClientFormat::U16U16 => (gl::RG, gl::UNSIGNED_SHORT),
        ClientFormat::U16U16U16 => (gl::RGB, gl::UNSIGNED_SHORT),
        ClientFormat::U16U16U16U16 => (gl::RGBA, gl::UNSIGNED_SHORT),
        ClientFormat::I16 => (gl::RED, gl::SHORT),
        ClientFormat::I16I16 => (gl::RG, gl::SHORT),
        ClientFormat::I16I16I16 => (gl::RGB, gl::SHORT),
        ClientFormat::I16I16I16I16 => (gl::RGBA, gl::SHORT),
        ClientFormat::U32 => (gl::RED, gl::UNSIGNED_INT),
        ClientFormat::U32U32 => (gl::RG, gl::UNSIGNED_INT),
        ClientFormat::U32U32U32 => (gl::RGB, gl::UNSIGNED_INT),
        ClientFormat::U32U32U32U32 => (gl::RGBA, gl::UNSIGNED_INT),
        ClientFormat::I32 => (gl::RED, gl::INT),
        ClientFormat::I32I32 => (gl::RG, gl::INT),
        ClientFormat::I32I32I32 => (gl::RGB, gl::INT),
        ClientFormat::I32I32I32I32 => (gl::RGBA, gl::INT),
        ClientFormat::U3U3U2 => (gl::RGB, gl::UNSIGNED_BYTE_3_3_2),
        ClientFormat::U5U6U5 => (gl::RGB, gl::UNSIGNED_SHORT_5_6_5),
        ClientFormat::U4U4U4U4 => (gl::RGBA, gl::UNSIGNED_SHORT_4_4_4_4),
        ClientFormat::U5U5U5U1 => (gl::RGBA, gl::UNSIGNED_SHORT_5_5_5_1),
        ClientFormat::U10U10U10U2 => (gl::RGBA, gl::UNSIGNED_INT_10_10_10_2),
        ClientFormat::F16 => (gl::RED, gl::HALF_FLOAT),
        ClientFormat::F16F16 => (gl::RG, gl::HALF_FLOAT),
        ClientFormat::F16F16F16 => (gl::RGB, gl::HALF_FLOAT),
        ClientFormat::F16F16F16F16 => (gl::RGBA, gl::HALF_FLOAT),
        ClientFormat::F32 => (gl::RED, gl::FLOAT),
        ClientFormat::F32F32 => (gl::RG, gl::FLOAT),
        ClientFormat::F32F32F32 => (gl::RGB, gl::FLOAT),
        ClientFormat::F32F32F32F32 => (gl::RGBA, gl::FLOAT),
    }
}
<|endoftext|>


<commit_before>extern crate crowbook;
use crowbook::{Book, InfoLevel};
use std::io;

#[test]
fn test_book() {
    let book = Book::new_from_file(&format!("{}/{}", env!("CARGO_MANIFEST_DIR"), "tests/test.book"), InfoLevel::Error, &[]).unwrap();
    book.render_html(&mut io::sink()).unwrap();
    book.render_tex(&mut io::sink()).unwrap();
}

#[test]
fn book_example() {
    let book = Book::new_from_file(&format!("{}/{}", env!("CARGO_MANIFEST_DIR"), "guide.book"), InfoLevel::Error, &[]).unwrap();
    book.render_html(&mut io::sink()).unwrap();
    book.render_tex(&mut io::sink()).unwrap();
}
<commit_msg>Comment a test failing on windows to investigate<commit_after>extern crate crowbook;
use crowbook::{Book, InfoLevel};
use std::io;

#[test]
fn test_book() {
    let book = Book::new_from_file(&format!("{}/{}", env!("CARGO_MANIFEST_DIR"), "tests/test.book"), InfoLevel::Error, &[]).unwrap();
    book.render_html(&mut io::sink()).unwrap();
    book.render_tex(&mut io::sink()).unwrap();
}

// #[test]
// fn book_example() {
//     let book = Book::new_from_file(&format!("{}/{}", env!("CARGO_MANIFEST_DIR"), "guide.book"), InfoLevel::Error, &[]).unwrap();
//     book.render_html(&mut io::sink()).unwrap();
//     book.render_tex(&mut io::sink()).unwrap();
// }
<|endoftext|>


<commit_before>#![cfg(feature = "alloc")]
//#![feature(trace_macros)]

#[macro_use]
extern crate nom;

use nom::{is_alphanumeric, recognize_float};

use std::str;
use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub enum JsonValue {
  Str(String),
  Num(f32),
  Array(Vec<JsonValue>),
  Object(HashMap<String, JsonValue>),
}

named!(float<f32>, flat_map!(recognize_float, parse_to!(f32)));

//FIXME: verify how json strings are formatted
named!(
  string<&str>,
  delimited!(
    char!('"'),
    //map_res!(escaped!(call!(alphanumeric), '\\', is_a!("\"n\\")), str::from_utf8),
    map_res!(
      escaped!(take_while1!(is_alphanumeric), '\\', one_of!("\"n\\")),
      str::from_utf8
    ),
    char!('"')
  )
);

named!(
  array<Vec<JsonValue>>,
  ws!(delimited!(
    char!('['),
    separated_list!(char!(','), value),
    char!(']')
  ))
);

named!(
  key_value<(&str, JsonValue)>,
  ws!(separated_pair!(string, char!(':'), value))
);

named!(
  hash<HashMap<String, JsonValue>>,
  ws!(map!(
    delimited!(char!('{'), separated_list!(char!(','), key_value), char!('}')),
    |tuple_vec| {
      let mut h: HashMap<String, JsonValue> = HashMap::new();
      for (k, v) in tuple_vec {
        h.insert(String::from(k), v);
      }
      h
    }
  ))
);

named!(
  value<JsonValue>,
  ws!(alt!(
      hash   => { |h|   JsonValue::Object(h)            } |
      array  => { |v|   JsonValue::Array(v)             } |
      string => { |s|   JsonValue::Str(String::from(s)) } |
      float  => { |num| JsonValue::Num(num)             }
    ))
);

#[test]
fn hash_test() {
  let input =
    r#"{
      "a": 42,
      "b": "x"
    }\0"#;

  let mut expected_map = HashMap::new();
  expected_map.insert(String::from("a"), JsonValue::Num(42f32));
  expected_map.insert(String::from("b"), JsonValue::Str(String::from("x")));
  let expected = JsonValue::Object(expected_map);

  assert_eq!(expected, value(input.as_bytes()).unwrap().1);
}
<commit_msg>Added array test<commit_after>#![cfg(feature = "alloc")]
//#![feature(trace_macros)]

#[macro_use]
extern crate nom;

use nom::{is_alphanumeric, recognize_float};

use std::str;
use std::collections::HashMap;

#[derive(Debug, PartialEq)]
pub enum JsonValue {
  Str(String),
  Num(f32),
  Array(Vec<JsonValue>),
  Object(HashMap<String, JsonValue>),
}

named!(float<f32>, flat_map!(recognize_float, parse_to!(f32)));

//FIXME: verify how json strings are formatted
named!(
  string<&str>,
  delimited!(
    char!('"'),
    //map_res!(escaped!(call!(alphanumeric), '\\', is_a!("\"n\\")), str::from_utf8),
    map_res!(
      escaped!(take_while1!(is_alphanumeric), '\\', one_of!("\"n\\")),
      str::from_utf8
    ),
    char!('"')
  )
);

named!(
  array<Vec<JsonValue>>,
  ws!(delimited!(
    char!('['),
    separated_list!(char!(','), value),
    char!(']')
  ))
);

named!(
  key_value<(&str, JsonValue)>,
  ws!(separated_pair!(string, char!(':'), value))
);

named!(
  hash<HashMap<String, JsonValue>>,
  ws!(map!(
    delimited!(char!('{'), separated_list!(char!(','), key_value), char!('}')),
    |tuple_vec| {
      let mut h: HashMap<String, JsonValue> = HashMap::new();
      for (k, v) in tuple_vec {
        h.insert(String::from(k), v);
      }
      h
    }
  ))
);

named!(
  value<JsonValue>,
  ws!(alt!(
      hash   => { |h|   JsonValue::Object(h)            } |
      array  => { |v|   JsonValue::Array(v)             } |
      string => { |s|   JsonValue::Str(String::from(s)) } |
      float  => { |num| JsonValue::Num(num)             }
    ))
);

#[test]
fn json_object() {
  let input =
    r#"{
      "a": 42,
      "b": "x"
    }\0"#;

  let mut expected_map = HashMap::new();
  expected_map.insert(String::from("a"), JsonValue::Num(42f32));
  expected_map.insert(String::from("b"), JsonValue::Str(String::from("x")));
  let expected = JsonValue::Object(expected_map);

  assert_eq!(expected, value(input.as_bytes()).unwrap().1);
}

#[test]
fn json_array() {
  let input =
    r#"[
      42,
      "x"
    ]\0"#;

  let expected_vec = vec![
    JsonValue::Num(42f32),
    JsonValue::Str(String::from("x"))
  ];
  let expected = JsonValue::Array(expected_vec);

  assert_eq!(expected, value(input.as_bytes()).unwrap().1);
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::error::Error;
use std::fmt;
use std::str::FromStr;

#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// A MAC address
#[derive(PartialEq, Eq, Clone, Copy, Hash, Ord, PartialOrd)]
pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);

impl MacAddr {
    /// Construct a new MacAddr
    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
        MacAddr(a, b, c, d, e, f)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt,
               "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
               self.0,
               self.1,
               self.2,
               self.3,
               self.4,
               self.5)
    }
}

#[cfg(feature = "serde")]
impl Serialize for MacAddr {
    /// Serializes the MAC address.
    ///
    /// It serializes either to a string or its binary representation, depending on what the format
    /// prefers.
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        if serializer.is_human_readable() {
            serializer.serialize_str(&format!("{}", self))
        } else {
            serializer.serialize_bytes(&[self.0, self.1, self.2, self.3, self.4, self.5])
        }
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for MacAddr {
    /// Deserializes the MAC address.
    ///
    /// It deserializes it from either a byte array (of size 6) or a string. If the format is
    /// self-descriptive (like JSON or MessagePack), it auto-detects it. If not, it obeys the
    /// human-readable property of the deserializer.
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct MacAddrVisitor;
        impl<'de> de::Visitor<'de> for MacAddrVisitor {
            type Value = MacAddr;

            fn visit_str<E: de::Error>(self, value: &str) -> Result<MacAddr, E> {
                value
                    .parse()
                    .map_err(|err| E::custom(&format!("{}", err)))
            }

            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<MacAddr, E> {
                if v.len() == 6 {
                    Ok(MacAddr::new(v[0], v[1], v[2], v[3], v[4], v[5]))
                } else {
                    Err(E::invalid_length(v.len(), &self))
                }
            }

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(
                    formatter,
                    "either a string representation of a MAC address or 6-element byte array"
                )
            }
        }

        // Decide what hint to provide to the deserializer based on if it is human readable or not
        if deserializer.is_human_readable() {
            deserializer.deserialize_str(MacAddrVisitor)
        } else {
            deserializer.deserialize_bytes(MacAddrVisitor)
        }
    }
}

impl fmt::Debug for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, fmt)
    }
}

/// Represents an error which occurred whilst parsing a MAC address
#[derive(Copy, Debug, PartialEq, Eq, Clone)]
pub enum ParseMacAddrErr {
    /// The MAC address has too many components, eg. 00:11:22:33:44:55:66
    TooManyComponents,
    /// The MAC address has too few components, eg. 00:11
    TooFewComponents,
    /// One of the components contains an invalid value, eg. 00:GG:22:33:44:55
    InvalidComponent,
}

impl Error for ParseMacAddrErr {
    fn description(&self) -> &str {
        match *self {
            ParseMacAddrErr::TooManyComponents => "Too many components in a MAC address string",
            ParseMacAddrErr::TooFewComponents => "Too few components in a MAC address string",
            ParseMacAddrErr::InvalidComponent => "Invalid component in a MAC address string",
        }
    }
}

impl fmt::Display for ParseMacAddrErr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl FromStr for MacAddr {
    type Err = ParseMacAddrErr;
    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrErr> {
        let mut parts = [0u8; 6];
        let splits = s.split(':');
        let mut i = 0;
        for split in splits {
            if i == 6 {
                return Err(ParseMacAddrErr::TooManyComponents);
            }
            match u8::from_str_radix(split, 16) {
                Ok(b) if split.len() != 0 => parts[i] = b,
                _ => return Err(ParseMacAddrErr::InvalidComponent),
            }
            i += 1;
        }

        if i == 6 {
            Ok(MacAddr(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]))
        } else {
            Err(ParseMacAddrErr::TooFewComponents)
        }
    }
}

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

    #[test]
    fn mac_addr_from_str() {
        assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr(0, 0, 0, 0, 0, 0)));
        assert_eq!("ff:ff:ff:ff:ff:ff".parse(),
                   Ok(MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)));
        assert_eq!("12:34:56:78:90:ab".parse(),
                   Ok(MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB)));
        assert_eq!("::::::".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("0::::::".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("::::0::".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("12:34:56:78".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::TooFewComponents));
        assert_eq!("12:34:56:78:".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("12:34:56:78:90".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::TooFewComponents));
        assert_eq!("12:34:56:78:90:".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("12:34:56:78:90:00:00".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::TooManyComponents));
        assert_eq!("xx:xx:xx:xx:xx:xx".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
    }

    #[test]
    fn str_from_mac_addr() {
        assert_eq!(format!("{}", MacAddr(0, 0, 0, 0, 0, 0)),
                   "00:00:00:00:00:00");
        assert_eq!(format!("{}", MacAddr(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF)),
                   "ff:ff:ff:ff:ff:ff");
        assert_eq!(format!("{}", MacAddr(0x12, 0x34, 0x56, 0x78, 0x09, 0xAB)),
                   "12:34:56:78:09:ab");
    }

    #[cfg(feature = "serde")]
    mod serde {
        extern crate serde_test;
        use self::serde_test::{
            assert_de_tokens, assert_de_tokens_error, assert_tokens, Compact, Configure, Readable,
            Token
        };
        use super::*;

        #[test]
        fn string() {
            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
            assert_tokens(&mac.readable(), &[Token::Str("11:22:33:44:55:66")]);
            assert_de_tokens(&mac.readable(), &[Token::String("11:22:33:44:55:66")]);
            assert_de_tokens(&mac.readable(), &[Token::BorrowedStr("11:22:33:44:55:66")]);
            assert_de_tokens_error::<Readable<MacAddr>>(
                &[Token::Str("not an address")],
                "Invalid component in a MAC address string",
            );
            // It still can detect bytes if provided
            assert_de_tokens(
                &mac.readable(),
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
        }

        #[test]
        fn bytes() {
            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
            assert_tokens(&mac.compact(), &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])]);
            assert_de_tokens(
                &mac.compact(),
                &[Token::BorrowedBytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
            let err = "invalid length 2, expected either a string representation of a MAC address or 6-element byte array";
            assert_de_tokens_error::<Compact<MacAddr>>(&[Token::Bytes(&[0x11, 0x33])], err);
            let err = "invalid length 7, expected either a string representation of a MAC address or 6-element byte array";
            assert_de_tokens_error::<Compact<MacAddr>>(
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77])],
                err,
            );
            // Still can decode string in the compact mode
            assert_de_tokens(&mac.compact(), &[Token::Str("11:22:33:44:55:66")]);
        }
    }
}
<commit_msg>add type and conversion methods<commit_after>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::error::Error;
use std::fmt;
use std::str::FromStr;

#[cfg(feature = "serde")]
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};

/// The number of bytes in an ethernet (MAC) address.
pub const ETHER_ADDR_LEN: usize = 6;

/// Structure of a 48-bit Ethernet address.
pub type EtherAddr = [u8; ETHER_ADDR_LEN];

const LOCAL_ADDR_BIT: u8 = 0x02;
const MULTICAST_ADDR_BIT: u8 = 0x01;

/// A MAC address
#[derive(PartialEq, Eq, Clone, Copy, Default, Hash, Ord, PartialOrd)]
pub struct MacAddr(pub u8, pub u8, pub u8, pub u8, pub u8, pub u8);

impl MacAddr {
    /// Construct a new MacAddr
    pub fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> MacAddr {
        MacAddr(a, b, c, d, e, f)
    }

    /// Construct a empty MacAddr
    pub fn empty() -> MacAddr {
        Default::default()
    }

    /// Construct a broadcast MacAddr
    pub fn broadcast() -> MacAddr {
        [0xff; ETHER_ADDR_LEN].into()
    }

    /// Returns true if the MacAddr is a empty address
    pub fn is_empty(&self) -> bool {
        *self == Self::empty()
    }

    /// Returns true if the MacAddr is a universally administered addresses (UAA)
    pub fn is_universal(&self) -> bool {
        !self.is_local()
    }

    /// Returns true if the MacAddr is a locally administered addresses (LAA)
    pub fn is_local(&self) -> bool {
        (self.0 & LOCAL_ADDR_BIT) == LOCAL_ADDR_BIT
    }

    /// Returns true if the MacAddr is a unicast address
    pub fn is_unicast(&self) -> bool {
        !self.is_multicast()
    }

    /// Returns true if the MacAddr is a multicast address
    pub fn is_multicast(&self) -> bool {
        (self.0 & MULTICAST_ADDR_BIT) == MULTICAST_ADDR_BIT
    }

    /// Returns true if the MacAddr is a broadcast address
    pub fn is_broadcast(&self) -> bool {
        *self == Self::broadcast()
    }
}

impl From<EtherAddr> for MacAddr {
    fn from(addr: EtherAddr) -> MacAddr {
        MacAddr(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5])
    }
}

impl From<MacAddr> for EtherAddr {
    fn from(addr: MacAddr) -> Self {
        [addr.0, addr.1, addr.2, addr.3, addr.4, addr.5]
    }
}

impl PartialEq<EtherAddr> for MacAddr {
    fn eq(&self, other: &EtherAddr) -> bool {
        *self == MacAddr::from(*other)
    }
}

impl fmt::Display for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt,
               "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
               self.0,
               self.1,
               self.2,
               self.3,
               self.4,
               self.5)
    }
}

#[cfg(feature = "serde")]
impl Serialize for MacAddr {
    /// Serializes the MAC address.
    ///
    /// It serializes either to a string or its binary representation, depending on what the format
    /// prefers.
    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        if serializer.is_human_readable() {
            serializer.serialize_str(&format!("{}", self))
        } else {
            serializer.serialize_bytes(&[self.0, self.1, self.2, self.3, self.4, self.5])
        }
    }
}

#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for MacAddr {
    /// Deserializes the MAC address.
    ///
    /// It deserializes it from either a byte array (of size 6) or a string. If the format is
    /// self-descriptive (like JSON or MessagePack), it auto-detects it. If not, it obeys the
    /// human-readable property of the deserializer.
    fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        struct MacAddrVisitor;
        impl<'de> de::Visitor<'de> for MacAddrVisitor {
            type Value = MacAddr;

            fn visit_str<E: de::Error>(self, value: &str) -> Result<MacAddr, E> {
                value
                    .parse()
                    .map_err(|err| E::custom(&format!("{}", err)))
            }

            fn visit_bytes<E: de::Error>(self, v: &[u8]) -> Result<MacAddr, E> {
                if v.len() == 6 {
                    Ok(MacAddr::new(v[0], v[1], v[2], v[3], v[4], v[5]))
                } else {
                    Err(E::invalid_length(v.len(), &self))
                }
            }

            fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
                write!(
                    formatter,
                    "either a string representation of a MAC address or 6-element byte array"
                )
            }
        }

        // Decide what hint to provide to the deserializer based on if it is human readable or not
        if deserializer.is_human_readable() {
            deserializer.deserialize_str(MacAddrVisitor)
        } else {
            deserializer.deserialize_bytes(MacAddrVisitor)
        }
    }
}

impl fmt::Debug for MacAddr {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, fmt)
    }
}

/// Represents an error which occurred whilst parsing a MAC address
#[derive(Copy, Debug, PartialEq, Eq, Clone)]
pub enum ParseMacAddrErr {
    /// The MAC address has too many components, eg. 00:11:22:33:44:55:66
    TooManyComponents,
    /// The MAC address has too few components, eg. 00:11
    TooFewComponents,
    /// One of the components contains an invalid value, eg. 00:GG:22:33:44:55
    InvalidComponent,
}

impl Error for ParseMacAddrErr {
    fn description(&self) -> &str {
        match *self {
            ParseMacAddrErr::TooManyComponents => "Too many components in a MAC address string",
            ParseMacAddrErr::TooFewComponents => "Too few components in a MAC address string",
            ParseMacAddrErr::InvalidComponent => "Invalid component in a MAC address string",
        }
    }
}

impl fmt::Display for ParseMacAddrErr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.description())
    }
}

impl FromStr for MacAddr {
    type Err = ParseMacAddrErr;
    fn from_str(s: &str) -> Result<MacAddr, ParseMacAddrErr> {
        let mut parts = [0u8; 6];
        let splits = s.split(':');
        let mut i = 0;
        for split in splits {
            if i == 6 {
                return Err(ParseMacAddrErr::TooManyComponents);
            }
            match u8::from_str_radix(split, 16) {
                Ok(b) if split.len() != 0 => parts[i] = b,
                _ => return Err(ParseMacAddrErr::InvalidComponent),
            }
            i += 1;
        }

        if i == 6 {
            Ok(MacAddr(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5]))
        } else {
            Err(ParseMacAddrErr::TooFewComponents)
        }
    }
}

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

    #[test]
    fn mac_addr_from_str() {
        assert_eq!("00:00:00:00:00:00".parse(), Ok(MacAddr::empty()));
        assert_eq!("ff:ff:ff:ff:ff:ff".parse(),
                   Ok(MacAddr::broadcast()));
        assert_eq!("12:34:56:78:90:ab".parse(),
                   Ok(MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB)));
        assert_eq!("::::::".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("0::::::".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("::::0::".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("12:34:56:78".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::TooFewComponents));
        assert_eq!("12:34:56:78:".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("12:34:56:78:90".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::TooFewComponents));
        assert_eq!("12:34:56:78:90:".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
        assert_eq!("12:34:56:78:90:00:00".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::TooManyComponents));
        assert_eq!("xx:xx:xx:xx:xx:xx".parse::<MacAddr>(),
                   Err(ParseMacAddrErr::InvalidComponent));
    }

    #[test]
    fn str_from_mac_addr() {
        assert_eq!(format!("{}", MacAddr::empty()),
                   "00:00:00:00:00:00");
        assert_eq!(format!("{}", MacAddr::broadcast()),
                   "ff:ff:ff:ff:ff:ff");
        assert_eq!(format!("{}", MacAddr(0x12, 0x34, 0x56, 0x78, 0x09, 0xAB)),
                   "12:34:56:78:09:ab");
    }

    #[test]
    fn type_of_addr() {
        assert!(MacAddr::empty().is_empty());
        assert!(MacAddr::broadcast().is_broadcast());

        let mac = MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB);
        assert!(mac.is_local());
        assert!(mac.is_unicast());

        let mac = MacAddr(0xac, 0x87, 0xa3, 0x07, 0x32, 0xb8);
        assert!(mac.is_universal());
        assert!(mac.is_unicast());
    }

    #[test]
    fn convertion() {
        let mac = MacAddr(0x12, 0x34, 0x56, 0x78, 0x90, 0xAB);
        let addr = [0x12, 0x34, 0x56, 0x78, 0x90, 0xAB];

        assert_eq!(mac, MacAddr::from(addr));
        assert_eq!(addr, EtherAddr::from(mac));
        assert!(mac == addr);
    }

    #[cfg(feature = "serde")]
    mod serde {
        extern crate serde_test;
        use self::serde_test::{
            assert_de_tokens, assert_de_tokens_error, assert_tokens, Compact, Configure, Readable,
            Token
        };
        use super::*;

        #[test]
        fn string() {
            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
            assert_tokens(&mac.readable(), &[Token::Str("11:22:33:44:55:66")]);
            assert_de_tokens(&mac.readable(), &[Token::String("11:22:33:44:55:66")]);
            assert_de_tokens(&mac.readable(), &[Token::BorrowedStr("11:22:33:44:55:66")]);
            assert_de_tokens_error::<Readable<MacAddr>>(
                &[Token::Str("not an address")],
                "Invalid component in a MAC address string",
            );
            // It still can detect bytes if provided
            assert_de_tokens(
                &mac.readable(),
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
        }

        #[test]
        fn bytes() {
            let mac = MacAddr::new(0x11, 0x22, 0x33, 0x44, 0x55, 0x66);
            assert_tokens(&mac.compact(), &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])]);
            assert_de_tokens(
                &mac.compact(),
                &[Token::BorrowedBytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66])],
            );
            let err = "invalid length 2, expected either a string representation of a MAC address or 6-element byte array";
            assert_de_tokens_error::<Compact<MacAddr>>(&[Token::Bytes(&[0x11, 0x33])], err);
            let err = "invalid length 7, expected either a string representation of a MAC address or 6-element byte array";
            assert_de_tokens_error::<Compact<MacAddr>>(
                &[Token::Bytes(&[0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77])],
                err,
            );
            // Still can decode string in the compact mode
            assert_de_tokens(&mac.compact(), &[Token::Str("11:22:33:44:55:66")]);
        }
    }
}
<|endoftext|>


<commit_before>use types::Validator;

/// Trait to implement if one wants to make the `length` validator
/// work for more types
///
/// A bit sad it's not there by default in Rust
pub trait HasLen {
    fn length(&self) -> u64;
}

impl<'a> HasLen for &'a String {
    fn length(&self) -> u64 {
        self.len() as u64
    }
}

impl<'a> HasLen for &'a str {
    fn length(&self) -> u64 {
        self.len() as u64
    }
}

impl<T> HasLen for Vec<T> {
    fn length(&self) -> u64 {
        self.len() as u64
    }
}
impl<'a, T> HasLen for &'a Vec<T> {
    fn length(&self) -> u64 {
        self.len() as u64
    }
}

/// Validates the length of the value given.
/// If the validator has `equal` set, it will ignore any `min` and `max` value.
///
/// If you apply it on String, don't forget that the length can be different
/// from the number of visual characters for Unicode
pub fn validate_length<T: HasLen>(length: Validator, val: T) -> bool {
    match length {
        Validator::Length { min, max, equal } => {
            let val_length = val.length();
            if let Some(eq) = equal {
                return val_length == eq;
            }
            if let Some(m) = min {
                if val_length < m {
                    return false;
                }
            }
            if let Some(m) = max {
                if val_length > m {
                    return false;
                }
            }
        },
        _ => unreachable!()
    }

    true
}

#[cfg(test)]
mod tests {
    use super::{validate_length, Validator};

    #[test]
    fn test_validate_length_equal_overrides_min_max() {
        let validator = Validator::Length { min: Some(1), max: Some(2), equal: Some(5) };
        assert_eq!(validate_length(validator, "hello"), true);
    }

    #[test]
    fn test_validate_length_string_min_max() {
        let validator = Validator::Length { min: Some(1), max: Some(10), equal: None };
        assert_eq!(validate_length(validator, "hello"), true);
    }

    #[test]
    fn test_validate_length_string_min_only() {
        let validator = Validator::Length { min: Some(10), max: None, equal: None };
        assert_eq!(validate_length(validator, "hello"), false);
    }

    #[test]
    fn test_validate_length_string_max_only() {
        let validator = Validator::Length { min: None, max: Some(1), equal: None };
        assert_eq!(validate_length(validator, "hello"), false);
    }

    #[test]
    fn test_validate_length_vec() {
        let validator = Validator::Length { min: None, max: None, equal: Some(3) };
        assert_eq!(validate_length(validator, vec![1, 2, 3]), true);
    }
}
<commit_msg>Use chars().count() instead of len()<commit_after>use types::Validator;

/// Trait to implement if one wants to make the `length` validator
/// work for more types
///
/// A bit sad it's not there by default in Rust
pub trait HasLen {
    fn length(&self) -> u64;
}

impl HasLen for String {
    fn length(&self) -> u64 {
        self.chars().count() as u64
    }
}

impl<'a> HasLen for &'a String {
    fn length(&self) -> u64 {
        self.chars().count() as u64
    }
}

impl<'a> HasLen for &'a str {
    fn length(&self) -> u64 {
        self.chars().count() as u64
    }
}

impl<T> HasLen for Vec<T> {
    fn length(&self) -> u64 {
        self.len() as u64
    }
}
impl<'a, T> HasLen for &'a Vec<T> {
    fn length(&self) -> u64 {
        self.len() as u64
    }
}

/// Validates the length of the value given.
/// If the validator has `equal` set, it will ignore any `min` and `max` value.
///
/// If you apply it on String, don't forget that the length can be different
/// from the number of visual characters for Unicode
pub fn validate_length<T: HasLen>(length: Validator, val: T) -> bool {
    match length {
        Validator::Length { min, max, equal } => {
            let val_length = val.length();
            if let Some(eq) = equal {
                return val_length == eq;
            }
            if let Some(m) = min {
                if val_length < m {
                    return false;
                }
            }
            if let Some(m) = max {
                if val_length > m {
                    return false;
                }
            }
        },
        _ => unreachable!()
    }

    true
}

#[cfg(test)]
mod tests {
    use super::{validate_length, Validator};

    #[test]
    fn test_validate_length_equal_overrides_min_max() {
        let validator = Validator::Length { min: Some(1), max: Some(2), equal: Some(5) };
        assert_eq!(validate_length(validator, "hello"), true);
    }

    #[test]
    fn test_validate_length_string_min_max() {
        let validator = Validator::Length { min: Some(1), max: Some(10), equal: None };
        assert_eq!(validate_length(validator, "hello"), true);
    }

    #[test]
    fn test_validate_length_string_min_only() {
        let validator = Validator::Length { min: Some(10), max: None, equal: None };
        assert_eq!(validate_length(validator, "hello"), false);
    }

    #[test]
    fn test_validate_length_string_max_only() {
        let validator = Validator::Length { min: None, max: Some(1), equal: None };
        assert_eq!(validate_length(validator, "hello"), false);
    }

    #[test]
    fn test_validate_length_vec() {
        let validator = Validator::Length { min: None, max: None, equal: Some(3) };
        assert_eq!(validate_length(validator, vec![1, 2, 3]), true);
    }

    #[test]
    fn test_validate_length_unicode_chars() {
        let validator = Validator::Length { min: None, max: None, equal: Some(2) };
        assert_eq!(validate_length(validator, "日本"), true);
    }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="font" sub_properties="font-style font-variant font-weight font-stretch
                                                font-size line-height font-family"
                    spec="https://drafts.csswg.org/css-fonts-3/#propdef-font">
    use properties::longhands::{font_style, font_variant, font_weight, font_stretch};
    use properties::longhands::{font_size, line_height, font_family};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut nb_normals = 0;
        let mut style = None;
        let mut variant = None;
        let mut weight = None;
        let mut stretch = None;
        let size;
        loop {
            // Special-case 'normal' because it is valid in each of
            // font-style, font-weight, font-variant and font-stretch.
            // Leaves the values to None, 'normal' is the initial value for each of them.
            if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
                nb_normals += 1;
                continue;
            }
            if style.is_none() {
                if let Ok(value) = input.try(|input| font_style::parse(context, input)) {
                    style = Some(value);
                    continue
                }
            }
            if weight.is_none() {
                if let Ok(value) = input.try(|input| font_weight::parse(context, input)) {
                    weight = Some(value);
                    continue
                }
            }
            if variant.is_none() {
                if let Ok(value) = input.try(|input| font_variant::parse(context, input)) {
                    variant = Some(value);
                    continue
                }
            }
            if stretch.is_none() {
                if let Ok(value) = input.try(|input| font_stretch::parse(context, input)) {
                    stretch = Some(value);
                    continue
                }
            }
            size = Some(try!(font_size::parse(context, input)));
            break
        }
        #[inline]
        fn count<T>(opt: &Option<T>) -> u8 {
            if opt.is_some() { 1 } else { 0 }
        }
        if size.is_none() || (count(&style) + count(&weight) + count(&variant) + count(&stretch) + nb_normals) > 4 {
            return Err(())
        }
        let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
            Some(try!(line_height::parse(context, input)))
        } else {
            None
        };
        let family = try!(input.parse_comma_separated(font_family::parse_one_family));
        Ok(Longhands {
            font_style: style,
            font_variant: variant,
            font_weight: weight,
            font_stretch: stretch,
            font_size: size,
            line_height: line_height,
            font_family: Some(font_family::SpecifiedValue(family))
        })
    }

    // This may be a bit off, unsure, possibly needs changes
    impl<'a> LonghandsToSerialize<'a>  {
        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if let DeclaredValue::Value(ref style) = *self.font_style {
                try!(style.to_css(dest));
                try!(write!(dest, " "));
            }

            if let DeclaredValue::Value(ref variant) = *self.font_variant {
                try!(variant.to_css(dest));
                try!(write!(dest, " "));
            }

            if let DeclaredValue::Value(ref weight) = *self.font_weight {
                try!(weight.to_css(dest));
                try!(write!(dest, " "));
            }

            if let DeclaredValue::Value(ref stretch) = *self.font_stretch {
                try!(stretch.to_css(dest));
                try!(write!(dest, " "));
            }

            try!(self.font_size.to_css(dest));
            if let DeclaredValue::Value(ref height) = *self.line_height {
                match *height {
                    line_height::SpecifiedValue::Normal => {},
                    _ => {
                        try!(write!(dest, "/"));
                        try!(height.to_css(dest));
                    }
                }
            }

            try!(write!(dest, " "));

            self.font_family.to_css(dest)
        }
    }
</%helpers:shorthand>
<commit_msg>Make additional properties be reset by the 'font' shorthand.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

<%namespace name="helpers" file="/helpers.mako.rs" />

<%helpers:shorthand name="font" sub_properties="font-style font-variant font-weight font-stretch
                                                font-size line-height font-family
                                                ${'font-size-adjust' if product == 'gecko' else ''}
                                                ${'font-kerning' if product == 'gecko' else ''}
                                                ${'font-variant-caps' if product == 'gecko' else ''}
                                                ${'font-variant-position' if product == 'gecko' else ''}
                                                ${'font-language-override' if product == 'none' else ''}"
                    spec="https://drafts.csswg.org/css-fonts-3/#propdef-font">
    use properties::longhands::{font_style, font_variant, font_weight, font_stretch};
    use properties::longhands::{font_size, line_height, font_family};

    pub fn parse_value(context: &ParserContext, input: &mut Parser) -> Result<Longhands, ()> {
        let mut nb_normals = 0;
        let mut style = None;
        let mut variant = None;
        let mut weight = None;
        let mut stretch = None;
        let size;
        loop {
            // Special-case 'normal' because it is valid in each of
            // font-style, font-weight, font-variant and font-stretch.
            // Leaves the values to None, 'normal' is the initial value for each of them.
            if input.try(|input| input.expect_ident_matching("normal")).is_ok() {
                nb_normals += 1;
                continue;
            }
            if style.is_none() {
                if let Ok(value) = input.try(|input| font_style::parse(context, input)) {
                    style = Some(value);
                    continue
                }
            }
            if weight.is_none() {
                if let Ok(value) = input.try(|input| font_weight::parse(context, input)) {
                    weight = Some(value);
                    continue
                }
            }
            if variant.is_none() {
                if let Ok(value) = input.try(|input| font_variant::parse(context, input)) {
                    variant = Some(value);
                    continue
                }
            }
            if stretch.is_none() {
                if let Ok(value) = input.try(|input| font_stretch::parse(context, input)) {
                    stretch = Some(value);
                    continue
                }
            }
            size = Some(try!(font_size::parse(context, input)));
            break
        }
        #[inline]
        fn count<T>(opt: &Option<T>) -> u8 {
            if opt.is_some() { 1 } else { 0 }
        }
        if size.is_none() || (count(&style) + count(&weight) + count(&variant) + count(&stretch) + nb_normals) > 4 {
            return Err(())
        }
        let line_height = if input.try(|input| input.expect_delim('/')).is_ok() {
            Some(try!(line_height::parse(context, input)))
        } else {
            None
        };
        let family = try!(input.parse_comma_separated(font_family::parse_one_family));
        Ok(Longhands {
            font_style: style,
            font_variant: variant,
            font_weight: weight,
            font_stretch: stretch,
            font_size: size,
            line_height: line_height,
            font_family: Some(font_family::SpecifiedValue(family)),
    % if product == "gecko":
            font_size_adjust: None,
            font_kerning: None,
            font_variant_caps: None,
            font_variant_position: None,
    % endif
    % if product == "none":
            font_language_override: None,
    % endif
        })
    }

    // This may be a bit off, unsure, possibly needs changes
    impl<'a> LonghandsToSerialize<'a>  {
        fn to_css_declared<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
            if let DeclaredValue::Value(ref style) = *self.font_style {
                try!(style.to_css(dest));
                try!(write!(dest, " "));
            }

            if let DeclaredValue::Value(ref variant) = *self.font_variant {
                try!(variant.to_css(dest));
                try!(write!(dest, " "));
            }

            if let DeclaredValue::Value(ref weight) = *self.font_weight {
                try!(weight.to_css(dest));
                try!(write!(dest, " "));
            }

            if let DeclaredValue::Value(ref stretch) = *self.font_stretch {
                try!(stretch.to_css(dest));
                try!(write!(dest, " "));
            }

            try!(self.font_size.to_css(dest));
            if let DeclaredValue::Value(ref height) = *self.line_height {
                match *height {
                    line_height::SpecifiedValue::Normal => {},
                    _ => {
                        try!(write!(dest, "/"));
                        try!(height.to_css(dest));
                    }
                }
            }

            try!(write!(dest, " "));

            self.font_family.to_css(dest)
        }
    }
</%helpers:shorthand>
<|endoftext|>