<commit_before>#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

//! Frippy is an IRC bot that runs plugins on each message
//! received.
//!
//! ## Examples
//! ```no_run
//! # extern crate irc;
//! # extern crate frippy;
//! # fn main() {
//! use frippy::{plugins, Config, Bot};
//! use irc::client::reactor::IrcReactor;
//!
//! let config = Config::load("config.toml").unwrap();
//! let mut reactor = IrcReactor::new().unwrap();
//! let mut bot = Bot::new();
//!
//! bot.add_plugin(plugins::Help::new());
//! bot.add_plugin(plugins::Emoji::new());
//! bot.add_plugin(plugins::Currency::new());
//!
//! bot.connect(&mut reactor, &config).unwrap();
//! reactor.run().unwrap();
//! # }
//! ```
//!
//! # Logging
//! Frippy uses the [log](https://docs.rs/log) crate so you can log events
//! which might be of interest.

#[cfg(feature = "mysql")]
#[macro_use]
extern crate diesel;
#[cfg(feature = "mysql")]
extern crate r2d2;
#[cfg(feature = "mysql")]
extern crate r2d2_diesel;

#[macro_use]
extern crate failure;
#[macro_use]
extern crate frippy_derive;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

extern crate chrono;
extern crate humantime;
extern crate irc;
extern crate reqwest;
extern crate time;

pub mod plugin;
pub mod plugins;
pub mod utils;
pub mod error;

use std::collections::HashMap;
use std::fmt;
use std::thread::spawn;
use std::sync::Arc;

pub use irc::client::prelude::*;
pub use irc::error::IrcError;
use error::*;
use failure::ResultExt;

use plugin::*;

/// The bot which contains the main logic.
#[derive(Default)]
pub struct Bot {
    plugins: ThreadedPlugins,
}

impl Bot {
    /// Creates a `Bot`.
    /// By itself the bot only responds to a few simple CTCP commands
    /// defined per config file.
    /// Any other functionality has to be provided by plugins
    /// which need to implement [`Plugin`](plugin/trait.Plugin.html).
    ///
    /// # Examples
    /// ```
    /// use frippy::Bot;
    /// let mut bot = Bot::new();
    /// ```
    pub fn new() -> Bot {
        Bot {
            plugins: ThreadedPlugins::new(),
        }
    }

    /// Adds the [`Plugin`](plugin/trait.Plugin.html).
    /// These plugins will be used to evaluate incoming messages from IRC.
    ///
    /// # Examples
    /// ```
    /// use frippy::{plugins, Bot};
    ///
    /// let mut bot = frippy::Bot::new();
    /// bot.add_plugin(plugins::Help::new());
    /// ```
    pub fn add_plugin<T: Plugin + 'static>(&mut self, plugin: T) {
        self.plugins.add(plugin);
    }

    /// Removes a [`Plugin`](plugin/trait.Plugin.html) based on its name.
    /// The binary currently uses this to disable plugins
    /// based on user configuration.
    ///
    /// # Examples
    /// ```
    /// use frippy::{plugins, Bot};
    ///
    /// let mut bot = frippy::Bot::new();
    /// bot.add_plugin(plugins::Help::new());
    /// bot.remove_plugin("Help");
    /// ```
    pub fn remove_plugin(&mut self, name: &str) -> Option<()> {
        self.plugins.remove(name)
    }

    /// This connects the `Bot` to IRC and creates a task on the
    /// [`IrcReactor`](../irc/client/reactor/struct.IrcReactor.html)
    /// which returns an Ok if the connection was cleanly closed and
    /// an Err if the connection was lost.
    ///
    /// You need to run the [`IrcReactor`](../irc/client/reactor/struct.IrcReactor.html),
    /// so that the `Bot`
    /// can actually do its work.
    ///
    /// # Examples
    /// ```no_run
    /// # extern crate irc;
    /// # extern crate frippy;
    /// # fn main() {
    /// use frippy::{Config, Bot};
    /// use irc::client::reactor::IrcReactor;
    ///
    /// let config = Config::load("config.toml").unwrap();
    /// let mut reactor = IrcReactor::new().unwrap();
    /// let mut bot = Bot::new();
    ///
    /// bot.connect(&mut reactor, &config).unwrap();
    /// reactor.run().unwrap();
    /// # }
    /// ```
    pub fn connect(&self, reactor: &mut IrcReactor, config: &Config) -> Result<(), FrippyError> {
        info!("Plugins loaded: {}", self.plugins);

        let client = reactor
            .prepare_client_and_connect(config)
            .context(ErrorKind::Connection)?;

        info!("Connected to IRC server");

        client.identify().context(ErrorKind::Connection)?;
        info!("Identified");

        // TODO Verify if we actually need to clone plugins twice
        let plugins = self.plugins.clone();

        reactor.register_client_with_handler(client, move |client, message| {
            process_msg(client, plugins.clone(), message)
        });

        Ok(())
    }
}

fn process_msg(
    client: &IrcClient,
    mut plugins: ThreadedPlugins,
    message: Message,
) -> Result<(), IrcError> {
    // Log any channels we join
    if let Command::JOIN(ref channel, _, _) = message.command {
        if message.source_nickname().unwrap() == client.current_nickname() {
            info!("Joined {}", channel);
        }
    }

    // Check for possible command and save the result for later
    let command = PluginCommand::from(&client.current_nickname().to_lowercase(), &message);

    plugins.execute_plugins(client, message);

    // If the message contained a command, handle it
    if let Some(command) = command {
        if let Err(e) = plugins.handle_command(client, command) {
            error!("Failed to handle command: {}", e);
        }
    }

    Ok(())
}

#[derive(Clone, Default, Debug)]
struct ThreadedPlugins {
    plugins: HashMap<String, Arc<Plugin>>,
}

impl ThreadedPlugins {
    pub fn new() -> ThreadedPlugins {
        ThreadedPlugins {
            plugins: HashMap::new(),
        }
    }

    pub fn add<T: Plugin + 'static>(&mut self, plugin: T) {
        let name = plugin.name().to_lowercase();
        let safe_plugin = Arc::new(plugin);

        self.plugins.insert(name, safe_plugin);
    }

    pub fn remove(&mut self, name: &str) -> Option<()> {
        self.plugins.remove(&name.to_lowercase()).map(|_| ())
    }

    pub fn execute_plugins(&mut self, client: &IrcClient, message: Message) {
        let message = Arc::new(message);

        for (name, plugin) in self.plugins.clone() {
            // Send the message to the plugin if the plugin needs it
            match plugin.execute(client, &message) {
                ExecutionStatus::Done => (),
                ExecutionStatus::Err(e) => error!("Error in {} - {}", name, e),
                ExecutionStatus::RequiresThread => {
                    debug!(
                        "Spawning thread to execute {} with {}",
                        name,
                        message.to_string().replace("\r\n", "")
                    );

                    // Clone everything before the move - the client uses an Arc internally too
                    let plugin = Arc::clone(&plugin);
                    let message = Arc::clone(&message);
                    let client = client.clone();

                    // Execute the plugin in another thread
                    spawn(move || {
                        if let Err(e) = plugin.execute_threaded(&client, &message) {
                            log_error(e);
                        };
                    });
                }
            }
        }
    }

    pub fn handle_command(
        &mut self,
        client: &IrcClient,
        mut command: PluginCommand,
    ) -> Result<(), FrippyError> {
        if !command.tokens.iter().any(|s| !s.is_empty()) {
            let help = format!("Use \"{} help\" to get help", client.current_nickname());
            client
                .send_notice(&command.source, &help)
                .context(ErrorKind::Connection)?;
        }

        // Check if the command is for this plugin
        if let Some(plugin) = self.plugins.get(&command.tokens[0].to_lowercase()) {
            // The first token contains the name of the plugin
            let name = command.tokens.remove(0);

            debug!("Sending command \"{:?}\" to {}", command, name);

            // Clone for the move - the client uses an Arc internally
            let client = client.clone();
            let plugin = Arc::clone(plugin);
            spawn(move || {
                if let Err(e) = plugin.command(&client, command) {
                    log_error(e);
                };
            });

            Ok(())
        } else {
            let help = format!(
                "\"{} {}\" is not a command, \
                 try \"{0} help\" instead.",
                client.current_nickname(),
                command.tokens[0]
            );

            Ok(client
                .send_notice(&command.source, &help)
                .context(ErrorKind::Connection)?)
        }
    }
}

impl fmt::Display for ThreadedPlugins {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let plugin_names = self.plugins
            .iter()
            .map(|(_, p)| p.name().to_owned())
            .collect::<Vec<String>>();
        write!(f, "{}", plugin_names.join(", "))
    }
}
<commit_msg>Fix doctests<commit_after>#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

//! Frippy is an IRC bot that runs plugins on each message
//! received.
//!
//! ## Examples
//! ```no_run
//! # extern crate irc;
//! # extern crate frippy;
//! # fn main() {
//! use frippy::{plugins, Config, Bot};
//! use irc::client::reactor::IrcReactor;
//!
//! let config = Config::load("config.toml").unwrap();
//! let mut reactor = IrcReactor::new().unwrap();
//! let mut bot = Bot::new();
//!
//! bot.add_plugin(plugins::help::Help::new());
//! bot.add_plugin(plugins::emoji::Emoji::new());
//! bot.add_plugin(plugins::currency::Currency::new());
//!
//! bot.connect(&mut reactor, &config).unwrap();
//! reactor.run().unwrap();
//! # }
//! ```
//!
//! # Logging
//! Frippy uses the [log](https://docs.rs/log) crate so you can log events
//! which might be of interest.

#[cfg(feature = "mysql")]
#[macro_use]
extern crate diesel;
#[cfg(feature = "mysql")]
extern crate r2d2;
#[cfg(feature = "mysql")]
extern crate r2d2_diesel;

#[macro_use]
extern crate failure;
#[macro_use]
extern crate frippy_derive;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;

extern crate chrono;
extern crate humantime;
extern crate irc;
extern crate reqwest;
extern crate time;

pub mod plugin;
pub mod plugins;
pub mod utils;
pub mod error;

use std::collections::HashMap;
use std::fmt;
use std::thread::spawn;
use std::sync::Arc;

pub use irc::client::prelude::*;
pub use irc::error::IrcError;
use error::*;
use failure::ResultExt;

use plugin::*;

/// The bot which contains the main logic.
#[derive(Default)]
pub struct Bot {
    plugins: ThreadedPlugins,
}

impl Bot {
    /// Creates a `Bot`.
    /// By itself the bot only responds to a few simple CTCP commands
    /// defined per config file.
    /// Any other functionality has to be provided by plugins
    /// which need to implement [`Plugin`](plugin/trait.Plugin.html).
    ///
    /// # Examples
    /// ```
    /// use frippy::Bot;
    /// let mut bot = Bot::new();
    /// ```
    pub fn new() -> Bot {
        Bot {
            plugins: ThreadedPlugins::new(),
        }
    }

    /// Adds the [`Plugin`](plugin/trait.Plugin.html).
    /// These plugins will be used to evaluate incoming messages from IRC.
    ///
    /// # Examples
    /// ```
    /// use frippy::{plugins, Bot};
    ///
    /// let mut bot = frippy::Bot::new();
    /// bot.add_plugin(plugins::help::Help::new());
    /// ```
    pub fn add_plugin<T: Plugin + 'static>(&mut self, plugin: T) {
        self.plugins.add(plugin);
    }

    /// Removes a [`Plugin`](plugin/trait.Plugin.html) based on its name.
    /// The binary currently uses this to disable plugins
    /// based on user configuration.
    ///
    /// # Examples
    /// ```
    /// use frippy::{plugins, Bot};
    ///
    /// let mut bot = frippy::Bot::new();
    /// bot.add_plugin(plugins::help::Help::new());
    /// bot.remove_plugin("Help");
    /// ```
    pub fn remove_plugin(&mut self, name: &str) -> Option<()> {
        self.plugins.remove(name)
    }

    /// This connects the `Bot` to IRC and creates a task on the
    /// [`IrcReactor`](../irc/client/reactor/struct.IrcReactor.html)
    /// which returns an Ok if the connection was cleanly closed and
    /// an Err if the connection was lost.
    ///
    /// You need to run the [`IrcReactor`](../irc/client/reactor/struct.IrcReactor.html),
    /// so that the `Bot`
    /// can actually do its work.
    ///
    /// # Examples
    /// ```no_run
    /// # extern crate irc;
    /// # extern crate frippy;
    /// # fn main() {
    /// use frippy::{Config, Bot};
    /// use irc::client::reactor::IrcReactor;
    ///
    /// let config = Config::load("config.toml").unwrap();
    /// let mut reactor = IrcReactor::new().unwrap();
    /// let mut bot = Bot::new();
    ///
    /// bot.connect(&mut reactor, &config).unwrap();
    /// reactor.run().unwrap();
    /// # }
    /// ```
    pub fn connect(&self, reactor: &mut IrcReactor, config: &Config) -> Result<(), FrippyError> {
        info!("Plugins loaded: {}", self.plugins);

        let client = reactor
            .prepare_client_and_connect(config)
            .context(ErrorKind::Connection)?;

        info!("Connected to IRC server");

        client.identify().context(ErrorKind::Connection)?;
        info!("Identified");

        // TODO Verify if we actually need to clone plugins twice
        let plugins = self.plugins.clone();

        reactor.register_client_with_handler(client, move |client, message| {
            process_msg(client, plugins.clone(), message)
        });

        Ok(())
    }
}

fn process_msg(
    client: &IrcClient,
    mut plugins: ThreadedPlugins,
    message: Message,
) -> Result<(), IrcError> {
    // Log any channels we join
    if let Command::JOIN(ref channel, _, _) = message.command {
        if message.source_nickname().unwrap() == client.current_nickname() {
            info!("Joined {}", channel);
        }
    }

    // Check for possible command and save the result for later
    let command = PluginCommand::from(&client.current_nickname().to_lowercase(), &message);

    plugins.execute_plugins(client, message);

    // If the message contained a command, handle it
    if let Some(command) = command {
        if let Err(e) = plugins.handle_command(client, command) {
            error!("Failed to handle command: {}", e);
        }
    }

    Ok(())
}

#[derive(Clone, Default, Debug)]
struct ThreadedPlugins {
    plugins: HashMap<String, Arc<Plugin>>,
}

impl ThreadedPlugins {
    pub fn new() -> ThreadedPlugins {
        ThreadedPlugins {
            plugins: HashMap::new(),
        }
    }

    pub fn add<T: Plugin + 'static>(&mut self, plugin: T) {
        let name = plugin.name().to_lowercase();
        let safe_plugin = Arc::new(plugin);

        self.plugins.insert(name, safe_plugin);
    }

    pub fn remove(&mut self, name: &str) -> Option<()> {
        self.plugins.remove(&name.to_lowercase()).map(|_| ())
    }

    pub fn execute_plugins(&mut self, client: &IrcClient, message: Message) {
        let message = Arc::new(message);

        for (name, plugin) in self.plugins.clone() {
            // Send the message to the plugin if the plugin needs it
            match plugin.execute(client, &message) {
                ExecutionStatus::Done => (),
                ExecutionStatus::Err(e) => error!("Error in {} - {}", name, e),
                ExecutionStatus::RequiresThread => {
                    debug!(
                        "Spawning thread to execute {} with {}",
                        name,
                        message.to_string().replace("\r\n", "")
                    );

                    // Clone everything before the move - the client uses an Arc internally too
                    let plugin = Arc::clone(&plugin);
                    let message = Arc::clone(&message);
                    let client = client.clone();

                    // Execute the plugin in another thread
                    spawn(move || {
                        if let Err(e) = plugin.execute_threaded(&client, &message) {
                            log_error(e);
                        };
                    });
                }
            }
        }
    }

    pub fn handle_command(
        &mut self,
        client: &IrcClient,
        mut command: PluginCommand,
    ) -> Result<(), FrippyError> {
        if !command.tokens.iter().any(|s| !s.is_empty()) {
            let help = format!("Use \"{} help\" to get help", client.current_nickname());
            client
                .send_notice(&command.source, &help)
                .context(ErrorKind::Connection)?;
        }

        // Check if the command is for this plugin
        if let Some(plugin) = self.plugins.get(&command.tokens[0].to_lowercase()) {
            // The first token contains the name of the plugin
            let name = command.tokens.remove(0);

            debug!("Sending command \"{:?}\" to {}", command, name);

            // Clone for the move - the client uses an Arc internally
            let client = client.clone();
            let plugin = Arc::clone(plugin);
            spawn(move || {
                if let Err(e) = plugin.command(&client, command) {
                    log_error(e);
                };
            });

            Ok(())
        } else {
            let help = format!(
                "\"{} {}\" is not a command, \
                 try \"{0} help\" instead.",
                client.current_nickname(),
                command.tokens[0]
            );

            Ok(client
                .send_notice(&command.source, &help)
                .context(ErrorKind::Connection)?)
        }
    }
}

impl fmt::Display for ThreadedPlugins {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let plugin_names = self.plugins
            .iter()
            .map(|(_, p)| p.name().to_owned())
            .collect::<Vec<String>>();
        write!(f, "{}", plugin_names.join(", "))
    }
}
<|endoftext|>


<commit_before>use std::io::BufRead;
use std::io;
use std::str::SplitWhitespace;
use std::str::FromStr;

#[derive(Debug)]
pub enum LoadingError {
    InvalidLine(usize),
    WrongNumberOfArguments(usize),
    Parse(usize),
    Io(io::Error),
}

/// A struct containing all data store by wavefront.
pub struct ObjData {
    /// List of vertices `(x,y,z,w)`.
    /// Its coordinates are (x,y,z) and w is the weight for rational curves and surfaces.
    pub vertices : Vec<(f32,f32,f32,f32)>,
    /// List of normal vector with componetns `(x,y,z)`.
    pub normals : Vec<(f32,f32,f32)>,
    /// List of texture coordinates `(u,v,w)`.
    /// u and v are the value for the horizontal and vertical direction.
    /// w is the value for the depth of the texture.
    pub texcoords : Vec<(f32,f32,f32)>,
    /// List of faces.
    /// Each Face is a list of `(v,vt,vn)`.
    /// v is the index of vertex.
    /// vt is the index of its texture coordinate if it has one.
    /// vn is the index of its normal vector if it has one.
    pub faces : Vec<Vec<(usize,Option<usize>,Option<usize>)>>
}

impl From<io::Error> for LoadingError {
    fn from(err : io::Error) -> LoadingError {
        LoadingError::Io(err)
    }
}

fn parse<T : FromStr>(it : SplitWhitespace, nb : usize) -> Result<Vec<T>, LoadingError> {
    let mut vec : Vec<T> = Vec::new();
    for s in it {
        let val = match s.parse::<T>() {
            Ok(v) => v,
            Err(_) => return Err(LoadingError::Parse(nb)),
        };
        vec.push(val);
    }
    return Ok(vec);
}

impl ObjData {
    /// Constructs a new empty `ObjData`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwobj::ObjData;
    ///
    /// let data = ObjData::new();
    /// ```
    pub fn new() -> ObjData {
        ObjData {
            vertices : Vec::new(),
            normals : Vec::new(),
            texcoords : Vec::new(),
            faces : Vec::new(),
        }
    }

    /// Load an `ObjData` from a `BufReader`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::BufReader;
    /// use lwobj::ObjData;
    ///
    /// let f = File::open("cube.obj").unwrap();
    /// let mut input = BufReader::new(f);
    /// let data = ObjData::load(&mut input).ok().unwrap();
    /// ```
    pub fn load<R : io::Read>(input : &mut io::BufReader<R>) -> Result<ObjData,LoadingError> {
        let mut data = ObjData::new();
        let mut buf = String::new();
        let mut nb : usize = 0;
        while try!(input.read_line(&mut buf)) > 0 {
            // Skip comment
            if buf.chars().next().unwrap() != '#' {
                let mut iter = buf.split_whitespace();
                match iter.next() {
                    Some("v") => {
                        let args = try!(parse::<f32>(iter,nb));
                        if args.len() == 4 {
                            data.vertices.push((args[0],args[1],args[2],args[3]));
                        } else if args.len() == 3 {
                            data.vertices.push((args[0],args[1],args[2],1.0));
                        } else {
                            return Err(LoadingError::WrongNumberOfArguments(nb));
                        }
                    },
                    Some("vn") => {
                        let args = try!(parse::<f32>(iter,nb));
                        if args.len() == 3 {
                            data.normals.push((args[0],args[1],args[2]));
                        } else {
                            return Err(LoadingError::WrongNumberOfArguments(nb));
                        }
                    },
                    Some("vt") => {
                        let args = try!(parse::<f32>(iter,nb));
                        if args.len() == 3 {
                            data.texcoords.push((args[0],args[1],args[2]));
                        } else if args.len() == 2 {
                            data.texcoords.push((args[0],args[1],0.));
                        } else if args.len() == 1 {
                            data.texcoords.push((args[0],0.,0.));
                        } else {
                            return Err(LoadingError::WrongNumberOfArguments(nb));
                        }
                    },
                    Some("s") => {
                        // Not supported
                    },
                    Some("f") => {
                        let mut vec : Vec<(usize,Option<usize>,Option<usize>)> = Vec::new();
                        for arg in iter {
                            let index : Vec<_> = arg.split('/').collect();
                            if index.len() != 3 {
                                return Err(LoadingError::WrongNumberOfArguments(nb));
                            }
                            let v = match index[0].parse::<usize>() {
                                Ok(val) => val-1,
                                Err(_) => return Err(LoadingError::Parse(nb)),
                            };
                            let vt = match index[1].parse::<usize>().ok() {
                                    Some(val) => Some(val-1),
                                    None => None,
                            };
                            let vn = match index[2].parse::<usize>().ok() {
                                    Some(val) => Some(val-1),
                                    None => None,
                            };
                            vec.push((v,vt,vn));
                        }
                        data.faces.push(vec);
                    },
                    Some("o") => {
                        // Not supported
                    },
                    _ => return Err(LoadingError::InvalidLine(nb)),
                }
            }
            nb += 1;
            buf.clear();
        }
        return Ok(data);
    }
}

#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::io::BufReader;
    use obj::*;

    #[test]
    fn load() {
        let mut expected = ObjData::new();
        expected.vertices = vec![(1.,-1.,-1.,1.),
        (1.,-1.,1.,1.),
        (-1.,-1.,1.,1.),
        (-1.,-1.,-1.,1.),
        (1.,1.,-1.,1.),
        (1.,1.,1.,1.),
        (-1.,1.,1.,1.),
        (-1.,1.,-1.,1.)];
        expected.normals = vec![(0.,-1.,0.),
        (0.,1.,0.),
        (1.,0.,0.),
        (0.,0.,1.),
        (-1.,0.,0.),
        (0.,0.,-1.)];
        expected.faces = vec![ vec![(1,None,Some(0)), (3,None,Some(0)), (0,None,Some(0))],
        vec![(7,None,Some(1)), (5,None,Some(1)), (4,None,Some(1))],
        vec![(4,None,Some(2)), (1,None,Some(2)), (0,None,Some(2))],
        vec![(5,None,Some(3)), (2,None,Some(3)), (1,None,Some(3))],
        vec![(2,None,Some(4)), (7,None,Some(4)), (3,None,Some(4))],
        vec![(0,None,Some(5)), (7,None,Some(5)), (4,None,Some(5))],
        vec![(1,None,Some(0)), (2,None,Some(0)), (3,None,Some(0))],
        vec![(7,None,Some(1)), (6,None,Some(1)), (5,None,Some(1))],
        vec![(4,None,Some(2)), (5,None,Some(2)), (1,None,Some(2))],
        vec![(5,None,Some(3)), (6,None,Some(3)), (2,None,Some(3))],
        vec![(2,None,Some(4)), (6,None,Some(4)), (7,None,Some(4))],
        vec![(0,None,Some(5)), (3,None,Some(5)), (7,None,Some(5))],
        ];
        let f = File::open("cube.obj").unwrap();
        let mut input = BufReader::new(f);
        let data = ObjData::load(&mut input).ok().unwrap();
        assert_eq!(expected.vertices,data.vertices);
        assert_eq!(expected.normals,data.normals);
        assert_eq!(expected.texcoords,data.texcoords);
        assert_eq!(expected.faces,data.faces);
    }
}
<commit_msg>Adds write method<commit_after>use std::io::BufRead;
use std::io::Write;
use std::io;
use std::str::SplitWhitespace;
use std::str::FromStr;

#[derive(Debug)]
pub enum LoadingError {
    InvalidLine(usize),
    WrongNumberOfArguments(usize),
    Parse(usize),
    Io(io::Error),
}

/// A struct containing all data store by wavefront.
pub struct ObjData {
    /// List of vertices `(x,y,z,w)`.
    /// Its coordinates are (x,y,z) and w is the weight for rational curves and surfaces.
    pub vertices : Vec<(f32,f32,f32,f32)>,
    /// List of normal vector with componetns `(x,y,z)`.
    pub normals : Vec<(f32,f32,f32)>,
    /// List of texture coordinates `(u,v,w)`.
    /// u and v are the value for the horizontal and vertical direction.
    /// w is the value for the depth of the texture.
    pub texcoords : Vec<(f32,f32,f32)>,
    /// List of faces.
    /// Each Face is a list of `(v,vt,vn)`.
    /// v is the index of vertex.
    /// vt is the index of its texture coordinate if it has one.
    /// vn is the index of its normal vector if it has one.
    pub faces : Vec<Vec<(usize,Option<usize>,Option<usize>)>>
}

impl From<io::Error> for LoadingError {
    fn from(err : io::Error) -> LoadingError {
        LoadingError::Io(err)
    }
}

fn parse<T : FromStr>(it : SplitWhitespace, nb : usize) -> Result<Vec<T>, LoadingError> {
    let mut vec : Vec<T> = Vec::new();
    for s in it {
        let val = match s.parse::<T>() {
            Ok(v) => v,
            Err(_) => return Err(LoadingError::Parse(nb)),
        };
        vec.push(val);
    }
    return Ok(vec);
}

impl ObjData {
    /// Constructs a new empty `ObjData`.
    ///
    /// # Examples
    ///
    /// ```
    /// use lwobj::ObjData;
    ///
    /// let data = ObjData::new();
    /// ```
    pub fn new() -> ObjData {
        ObjData {
            vertices : Vec::new(),
            normals : Vec::new(),
            texcoords : Vec::new(),
            faces : Vec::new(),
        }
    }

    /// Load an `ObjData` from a `BufReader`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::BufReader;
    /// use lwobj::ObjData;
    ///
    /// let f = File::open("cube.obj").unwrap();
    /// let mut input = BufReader::new(f);
    /// let data = ObjData::load(&mut input).ok().unwrap();
    /// ```
    pub fn load<R : io::Read>(input : &mut io::BufReader<R>) -> Result<ObjData,LoadingError> {
        let mut data = ObjData::new();
        let mut buf = String::new();
        let mut nb : usize = 0;
        while try!(input.read_line(&mut buf)) > 0 {
            // Skip comment
            if buf.chars().next().unwrap() != '#' {
                let mut iter = buf.split_whitespace();
                match iter.next() {
                    Some("v") => {
                        let args = try!(parse::<f32>(iter,nb));
                        if args.len() == 4 {
                            data.vertices.push((args[0],args[1],args[2],args[3]));
                        } else if args.len() == 3 {
                            data.vertices.push((args[0],args[1],args[2],1.0));
                        } else {
                            return Err(LoadingError::WrongNumberOfArguments(nb));
                        }
                    },
                    Some("vn") => {
                        let args = try!(parse::<f32>(iter,nb));
                        if args.len() == 3 {
                            data.normals.push((args[0],args[1],args[2]));
                        } else {
                            return Err(LoadingError::WrongNumberOfArguments(nb));
                        }
                    },
                    Some("vt") => {
                        let args = try!(parse::<f32>(iter,nb));
                        if args.len() == 3 {
                            data.texcoords.push((args[0],args[1],args[2]));
                        } else if args.len() == 2 {
                            data.texcoords.push((args[0],args[1],0.));
                        } else if args.len() == 1 {
                            data.texcoords.push((args[0],0.,0.));
                        } else {
                            return Err(LoadingError::WrongNumberOfArguments(nb));
                        }
                    },
                    Some("s") => {
                        // Not supported
                    },
                    Some("f") => {
                        let mut vec : Vec<(usize,Option<usize>,Option<usize>)> = Vec::new();
                        for arg in iter {
                            let index : Vec<_> = arg.split('/').collect();
                            if index.len() != 3 {
                                return Err(LoadingError::WrongNumberOfArguments(nb));
                            }
                            let v = match index[0].parse::<usize>() {
                                Ok(val) => val-1,
                                Err(_) => return Err(LoadingError::Parse(nb)),
                            };
                            let vt = match index[1].parse::<usize>().ok() {
                                Some(val) => Some(val-1),
                                None => None,
                            };
                            let vn = match index[2].parse::<usize>().ok() {
                                Some(val) => Some(val-1),
                                None => None,
                            };
                            vec.push((v,vt,vn));
                        }
                        data.faces.push(vec);
                    },
                    Some("o") => {
                        // Not supported
                    },
                    _ => return Err(LoadingError::InvalidLine(nb)),
                }
            }
            nb += 1;
            buf.clear();
        }
        return Ok(data);
    }

    /// Write in wavefront format in file.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::fs::File;
    /// use std::io::BufWriter;
    /// use std::io::BufReader;
    /// use lwobj::ObjData;
    ///
    /// let f1 = File::open("cube.obj").unwrap();
    /// let mut input = BufReader::new(f1);
    /// let data = ObjData::load(&mut input).ok().unwrap();
    /// let f2 = File::create("tmp.obj").unwrap();
    /// let mut output = BufWriter::new(f2);
    /// assert!(data.write(&mut output).is_ok());
    /// ```
    pub fn write<W : io::Write>(&self, output : &mut io::BufWriter<W>) -> Result<(),LoadingError> {
        // Write vertices
        for &(x,y,z,w) in &self.vertices {
            let line : String = format!("v {} {} {} {}\n",x,y,z,w);
            try!(output.write_all(line.as_bytes()));
        }

        // Write normals
        for &(x,y,z) in &self.normals {
            let line : String = format!("vn {} {} {}\n",x,y,z);
            try!(output.write_all(line.as_bytes()));
        }

        // Write texcoords
        for &(u,v,w) in &self.texcoords {
            let line : String = format!("vt {} {} {}\n",u,v,w);
            try!(output.write_all(line.as_bytes()));
        }

        // Write faces
        for indexes in &self.faces {
            try!(output.write_all("f".as_bytes()));
            for &(v,vt,vn) in indexes {
                let vt_str = match vt {
                    Some(val) => (val+1).to_string(),
                    None => "".to_string(),
                };
                let vn_str = match vn {
                    Some(val) => (val+1).to_string(),
                    None => "".to_string(),
                };
                let arg : String = format!(" {}/{}/{}",v+1,vt_str,vn_str);
                try!(output.write_all(arg.as_bytes()));
            }
            try!(output.write_all("\n".as_bytes()));
        }
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use std::fs::File;
    use std::io::BufReader;
    use std::io::BufWriter;
    use obj::*;

    #[test]
    fn load() {
        let mut expected = ObjData::new();
        expected.vertices = vec![(1.,-1.,-1.,1.),
        (1.,-1.,1.,1.),
        (-1.,-1.,1.,1.),
        (-1.,-1.,-1.,1.),
        (1.,1.,-1.,1.),
        (1.,1.,1.,1.),
        (-1.,1.,1.,1.),
        (-1.,1.,-1.,1.)];
        expected.normals = vec![(0.,-1.,0.),
        (0.,1.,0.),
        (1.,0.,0.),
        (0.,0.,1.),
        (-1.,0.,0.),
        (0.,0.,-1.)];
        expected.faces = vec![ vec![(1,None,Some(0)), (3,None,Some(0)), (0,None,Some(0))],
        vec![(7,None,Some(1)), (5,None,Some(1)), (4,None,Some(1))],
        vec![(4,None,Some(2)), (1,None,Some(2)), (0,None,Some(2))],
        vec![(5,None,Some(3)), (2,None,Some(3)), (1,None,Some(3))],
        vec![(2,None,Some(4)), (7,None,Some(4)), (3,None,Some(4))],
        vec![(0,None,Some(5)), (7,None,Some(5)), (4,None,Some(5))],
        vec![(1,None,Some(0)), (2,None,Some(0)), (3,None,Some(0))],
        vec![(7,None,Some(1)), (6,None,Some(1)), (5,None,Some(1))],
        vec![(4,None,Some(2)), (5,None,Some(2)), (1,None,Some(2))],
        vec![(5,None,Some(3)), (6,None,Some(3)), (2,None,Some(3))],
        vec![(2,None,Some(4)), (6,None,Some(4)), (7,None,Some(4))],
        vec![(0,None,Some(5)), (3,None,Some(5)), (7,None,Some(5))],
        ];
        let f = File::open("cube.obj").unwrap();
        let mut input = BufReader::new(f);
        let data = ObjData::load(&mut input).ok().unwrap();
        assert_eq!(expected.vertices,data.vertices);
        assert_eq!(expected.normals,data.normals);
        assert_eq!(expected.texcoords,data.texcoords);
        assert_eq!(expected.faces,data.faces);
    }

    #[test]
    fn write() {
        let mut expected = ObjData::new();
        expected.vertices = vec![(1.,-1.,-1.,1.),
        (1.,-1.,1.,1.),
        (-1.,-1.,1.,1.),
        (-1.,-1.,-1.,1.),
        (1.,1.,-1.,1.),
        (1.,1.,1.,1.),
        (-1.,1.,1.,1.),
        (-1.,1.,-1.,1.)];
        expected.normals = vec![(0.,-1.,0.),
        (0.,1.,0.),
        (1.,0.,0.),
        (0.,0.,1.),
        (-1.,0.,0.),
        (0.,0.,-1.)];
        expected.faces = vec![ vec![(1,None,Some(0)), (3,None,Some(0)), (0,None,Some(0))],
        vec![(7,None,Some(1)), (5,None,Some(1)), (4,None,Some(1))],
        vec![(4,None,Some(2)), (1,None,Some(2)), (0,None,Some(2))],
        vec![(5,None,Some(3)), (2,None,Some(3)), (1,None,Some(3))],
        vec![(2,None,Some(4)), (7,None,Some(4)), (3,None,Some(4))],
        vec![(0,None,Some(5)), (7,None,Some(5)), (4,None,Some(5))],
        vec![(1,None,Some(0)), (2,None,Some(0)), (3,None,Some(0))],
        vec![(7,None,Some(1)), (6,None,Some(1)), (5,None,Some(1))],
        vec![(4,None,Some(2)), (5,None,Some(2)), (1,None,Some(2))],
        vec![(5,None,Some(3)), (6,None,Some(3)), (2,None,Some(3))],
        vec![(2,None,Some(4)), (6,None,Some(4)), (7,None,Some(4))],
        vec![(0,None,Some(5)), (3,None,Some(5)), (7,None,Some(5))],
        ];
        {
            let f2 = File::create("tmp.obj").unwrap();
            let mut output = BufWriter::new(f2);
            assert!(expected.write(&mut output).is_ok());
        }
        let f1 = File::open("tmp.obj").unwrap();
        let mut input = BufReader::new(f1);
        let data = ObjData::load(&mut input).ok().unwrap();
        assert_eq!(expected.vertices,data.vertices);
        assert_eq!(expected.normals,data.normals);
        assert_eq!(expected.texcoords,data.texcoords);
        assert_eq!(expected.faces,data.faces);
    }

}
<|endoftext|>


<commit_before>//! socketCAN support.
//!
//! The Linux kernel supports using CAN-devices through a network-like API
//! (see https://www.kernel.org/doc/Documentation/networking/can.txt). This
//! crate allows easy access to this functionality without having to wrestle
//! libc calls.

extern crate itertools;
extern crate libc;
extern crate nix;

use libc::{c_int, c_short, c_void, c_uint, socket, SOCK_RAW, close, bind,
           sockaddr, read, write, setsockopt, SOL_SOCKET, SO_RCVTIMEO,
           timeval, EINPROGRESS, SO_SNDTIMEO};
use itertools::Itertools;
use std::{error, fmt, io, time};
use std::mem::size_of;
use nix::net::if_::if_nametoindex;

/// Check an error return value for timeouts.
///
/// Due to the fact that timeouts are reported as errors, calling `read_frame`
/// on a socket with a timeout that does not receive a frame in time will
/// result in an error being returned. This trait adds a `should_retry` method
/// to `Error` and `Result` to check for this condition.
trait ShouldRetry {
    /// Check for timeout
    ///
    /// If `true`, the error is probably due to a timeout.
    fn should_retry(&self) -> bool;
}

impl ShouldRetry for io::Error {
    fn should_retry(&self) -> bool {
        match self.kind() {
            // EAGAIN, EINPROGRESS and EWOULDBLOCK are the three possible codes
            // returned when a timeout occurs. the stdlib already maps EAGAIN
            // and EWOULDBLOCK os WouldBlock
            io::ErrorKind::WouldBlock => true,
            // however, EINPROGRESS is also valid
            io::ErrorKind::Other => {
                if let Some(i) = self.raw_os_error() {
                    i == EINPROGRESS
                } else {
                    false
                }
            },
            _ => false,
        }
    }
}

impl<E: fmt::Debug> ShouldRetry for io::Result<E> {
    fn should_retry(&self) -> bool {
        if let &Err(ref e) = self {
            e.should_retry()
        } else {
            false
        }
    }
}

// constants stolen from C headers
const AF_CAN: c_int = 29;
const PF_CAN: c_int = 29;
const CAN_RAW: c_int = 1;

/// if set, indicate 29 bit extended format
pub const EFF_FLAG: u32 = 0x80000000;

/// remote transmission request flag
pub const RTR_FLAG: u32 = 0x40000000;

/// error flag
pub const ERR_FLAG: u32 = 0x20000000;

/// valid bits in standard frame id
pub const SFF_MASK: u32 = 0x000007ff;

/// valid bits in extended frame id
pub const EFF_MASK: u32 = 0x1fffffff;

/// valid bits in error frame
pub const ERR_MASK: u32 = 0x1fffffff;

#[cfg(target_pointer_width = "64")]
fn c_timeval_new(t: time::Duration) -> timeval {
    timeval {
        tv_sec: t.as_secs() as i64,
        tv_usec: (t.subsec_nanos() / 1000) as i64,
    }
}

#[cfg(target_pointer_width = "32")]
fn c_timeval_new(t: time::Duration) -> timeval {
    timeval {
        tv_sec: t.as_secs() as i32,
        tv_usec: (t.subsec_nanos() / 1000) as i32,
    }
}

#[derive(Debug)]
#[repr(C)]
struct CANAddr {
    _af_can: c_short,
    if_index: c_int,  // address familiy,
    rx_id: u32,
    tx_id: u32,
}

#[derive(Debug)]
/// Errors opening socket
pub enum CANSocketOpenError {
    /// Device could not be found
    LookupError(nix::Error),

    /// System error while trying to look up device name
    IOError(io::Error),
}

impl fmt::Display for CANSocketOpenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CANSocketOpenError::LookupError(ref e) =>
                write!(f, "CAN Device not found: {}", e),
            CANSocketOpenError::IOError(ref e) =>
                write!(f, "IO: {}", e),
        }
    }
}

impl error::Error for CANSocketOpenError {
    fn description(&self) -> &str {
        match *self {
            CANSocketOpenError::LookupError(_)
                => "can device not found",
            CANSocketOpenError::IOError(ref e)
                => e.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            CANSocketOpenError::LookupError(ref e) => Some(e),
            CANSocketOpenError::IOError(ref e) => Some(e),
        }
    }
}

#[derive(Debug, Copy, Clone)]
/// Error that occurs when creating CAN packets
pub enum ConstructionError {
    /// CAN ID was outside the range of valid IDs
    IDTooLarge,
    /// More than 8 Bytes of payload data were passed in
    TooMuchData,
}

impl fmt::Display for ConstructionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConstructionError::IDTooLarge
                => write!(f, "CAN ID too large"),
            ConstructionError::TooMuchData
                => write!(f, "Payload is larger than CAN maximum of 8 bytes")
        }
    }
}

impl error::Error for ConstructionError {
    fn description(&self) -> &str {
        match *self {
            ConstructionError::IDTooLarge => "can id too large",
            ConstructionError::TooMuchData => "too much data",
        }
    }
}

impl From<nix::Error> for CANSocketOpenError {
    fn from(e: nix::Error) -> CANSocketOpenError {
        CANSocketOpenError::LookupError(e)
    }
}

impl From<io::Error> for CANSocketOpenError {
    fn from(e: io::Error) -> CANSocketOpenError {
        CANSocketOpenError::IOError(e)
    }
}

/// A socket for a CAN device.
///
/// Will be closed upon deallocation. To close manually, use std::drop::Drop.
/// Internally this is just a wrapped file-descriptor.
pub struct CANSocket {
    fd: c_int,
}

impl CANSocket {
    /// Open a named CAN device.
    ///
    /// Usually the more common case, opens a socket can device by name, such
    /// as "vcan0" or "socan0".
    pub fn open(ifname: &str) -> Result<CANSocket, CANSocketOpenError> {
        let if_index = try!(if_nametoindex(ifname));
        CANSocket::open_if(if_index)
    }

    /// Open CAN device by interface number.
    ///
    /// Opens a CAN device by kernel interface number.
    pub fn open_if(if_index: c_uint) -> Result<CANSocket, CANSocketOpenError> {
        let addr = CANAddr{
            _af_can: AF_CAN as c_short,
            if_index: if_index as c_int,
            rx_id: 0, // ?
            tx_id: 0, // ?
        };

        // open socket
        let sock_fd;
        unsafe {
            sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
        }

        if sock_fd == -1 {
            return Err(CANSocketOpenError::from(io::Error::last_os_error()));
        }

        // bind it
        let bind_rv;
        unsafe {
            let sockaddr_ptr = &addr as *const CANAddr;
            bind_rv = bind(sock_fd, sockaddr_ptr as *const sockaddr,
                size_of::<CANAddr> () as u32);
        }

        if bind_rv == -1 {
            let e = io::Error::last_os_error();
            unsafe {
                close(sock_fd);
            }
            return Err(CANSocketOpenError::from(e))
        }

        Ok(CANSocket{fd: sock_fd})
    }

    fn close(&mut self) -> io::Result<()> {
        unsafe {
            let rv = close(self.fd);
            if rv != -1 {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(())
    }

    /// Sets the read timeout on the socket
    ///
    /// For convenience, the result value can be checked using
    /// `ShouldRetry::should_retry` when a timeout is set.
    pub fn set_read_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_RCVTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Sets the write timeout on the socket
    pub fn set_write_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_SNDTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Blocking read a single can frame.
    pub fn read_frame(&self) -> io::Result<CANFrame> {
        let mut frame = CANFrame{
            _id: 0,
            _data_len: 0,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: [0; 8],
        };

        let read_rv = unsafe {
            let frame_ptr = &mut frame as *mut CANFrame;
            read(self.fd, frame_ptr as *mut c_void, size_of::<CANFrame>())
        };

        if read_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(frame)
    }

    /// Blocking write a single can frame.
    pub fn write_frame(&self, frame: &CANFrame) -> io::Result<()> {
        // not a mutable reference needed (see std::net::UdpSocket) for
        // a comparison
        // debug!("Sending: {:?}", frame);

        let write_rv = unsafe {
            let frame_ptr = frame as *const CANFrame;
            write(self.fd, frame_ptr as *const c_void, size_of::<CANFrame>())
        };

        if write_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }
}

impl Drop for CANSocket {
    fn drop(&mut self) {
        self.close().ok();  // ignore result
    }
}

/// CANFrame
///
/// Uses the same memory layout as the underlying kernel struct for performance
/// reasons.
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct CANFrame {
    /// 32 bit CAN_ID + EFF/RTR/ERR flags
    _id: u32,

    /// data length. Bytes beyond are not valid
    _data_len: u8,

    /// padding
    _pad: u8,

    /// reserved
    _res0: u8,

    /// reserved
    _res1: u8,

    /// buffer for data
    _data: [u8; 8],
}

impl CANFrame {
    pub fn new(id: u32, data: &[u8], rtr: bool, err: bool) -> Result<CANFrame,
    ConstructionError> {
        let mut _id = id;

        if data.len() > 8 {
            return Err(ConstructionError::TooMuchData)
        }

        if id > EFF_MASK {
            return Err(ConstructionError::IDTooLarge)
        }

        // set EFF_FLAG on large message
        if id > SFF_MASK {
            _id |= EFF_FLAG;
        }

        if rtr {
            _id |= RTR_FLAG;
        }

        if err {
            _id |= ERR_FLAG;
        }

        let mut full_data = [0; 8];

        // not cool =/
        for (n, c) in data.iter().enumerate() {
            full_data[n] = *c;
        }

        Ok(CANFrame{
            _id: _id,
            _data_len: data.len() as u8,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: full_data,
        })
    }

    /// Return the actual CAN ID (without EFF/RTR/ERR flags)
    pub fn id(&self) -> u32 {
        if self.is_extended() {
            self._id & EFF_MASK
        } else {
            self._id & SFF_MASK
        }
    }

    /// Return the error message
    pub fn err(&self) -> u32 {
        return self._id & ERR_MASK
    }

    /// Check if frame uses 29 bit extended frame format
    pub fn is_extended(&self) -> bool {
        self._id & EFF_FLAG != 0
    }

    /// Check if frame is an error message
    pub fn is_error(&self) -> bool {
        self._id & ERR_FLAG != 0
    }

    /// Check if frame is a remote transmission request
    pub fn is_rtr(&self) -> bool {
        self._id & RTR_FLAG != 0
    }

    /// A slice into the actual data. Slice will always be <= 8 bytes in length
    pub fn data(&self) -> &[u8] {
        &self._data[..(self._data_len as usize)]
    }
}

impl fmt::UpperHex for CANFrame {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "{:X}#", self.id()));

        let mut parts = self.data().iter().map(|v| format!("{:02X}", v));

        let sep = if f.alternate() { " " } else { " " };
        write!(f, "{}", parts.join(sep))
    }
}
<commit_msg>Make ShouldRetry public<commit_after>//! socketCAN support.
//!
//! The Linux kernel supports using CAN-devices through a network-like API
//! (see https://www.kernel.org/doc/Documentation/networking/can.txt). This
//! crate allows easy access to this functionality without having to wrestle
//! libc calls.

extern crate itertools;
extern crate libc;
extern crate nix;

use libc::{c_int, c_short, c_void, c_uint, socket, SOCK_RAW, close, bind,
           sockaddr, read, write, setsockopt, SOL_SOCKET, SO_RCVTIMEO,
           timeval, EINPROGRESS, SO_SNDTIMEO};
use itertools::Itertools;
use std::{error, fmt, io, time};
use std::mem::size_of;
use nix::net::if_::if_nametoindex;

/// Check an error return value for timeouts.
///
/// Due to the fact that timeouts are reported as errors, calling `read_frame`
/// on a socket with a timeout that does not receive a frame in time will
/// result in an error being returned. This trait adds a `should_retry` method
/// to `Error` and `Result` to check for this condition.
pub trait ShouldRetry {
    /// Check for timeout
    ///
    /// If `true`, the error is probably due to a timeout.
    fn should_retry(&self) -> bool;
}

impl ShouldRetry for io::Error {
    fn should_retry(&self) -> bool {
        match self.kind() {
            // EAGAIN, EINPROGRESS and EWOULDBLOCK are the three possible codes
            // returned when a timeout occurs. the stdlib already maps EAGAIN
            // and EWOULDBLOCK os WouldBlock
            io::ErrorKind::WouldBlock => true,
            // however, EINPROGRESS is also valid
            io::ErrorKind::Other => {
                if let Some(i) = self.raw_os_error() {
                    i == EINPROGRESS
                } else {
                    false
                }
            },
            _ => false,
        }
    }
}

impl<E: fmt::Debug> ShouldRetry for io::Result<E> {
    fn should_retry(&self) -> bool {
        if let &Err(ref e) = self {
            e.should_retry()
        } else {
            false
        }
    }
}

// constants stolen from C headers
const AF_CAN: c_int = 29;
const PF_CAN: c_int = 29;
const CAN_RAW: c_int = 1;

/// if set, indicate 29 bit extended format
pub const EFF_FLAG: u32 = 0x80000000;

/// remote transmission request flag
pub const RTR_FLAG: u32 = 0x40000000;

/// error flag
pub const ERR_FLAG: u32 = 0x20000000;

/// valid bits in standard frame id
pub const SFF_MASK: u32 = 0x000007ff;

/// valid bits in extended frame id
pub const EFF_MASK: u32 = 0x1fffffff;

/// valid bits in error frame
pub const ERR_MASK: u32 = 0x1fffffff;

#[cfg(target_pointer_width = "64")]
fn c_timeval_new(t: time::Duration) -> timeval {
    timeval {
        tv_sec: t.as_secs() as i64,
        tv_usec: (t.subsec_nanos() / 1000) as i64,
    }
}

#[cfg(target_pointer_width = "32")]
fn c_timeval_new(t: time::Duration) -> timeval {
    timeval {
        tv_sec: t.as_secs() as i32,
        tv_usec: (t.subsec_nanos() / 1000) as i32,
    }
}

#[derive(Debug)]
#[repr(C)]
struct CANAddr {
    _af_can: c_short,
    if_index: c_int,  // address familiy,
    rx_id: u32,
    tx_id: u32,
}

#[derive(Debug)]
/// Errors opening socket
pub enum CANSocketOpenError {
    /// Device could not be found
    LookupError(nix::Error),

    /// System error while trying to look up device name
    IOError(io::Error),
}

impl fmt::Display for CANSocketOpenError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            CANSocketOpenError::LookupError(ref e) =>
                write!(f, "CAN Device not found: {}", e),
            CANSocketOpenError::IOError(ref e) =>
                write!(f, "IO: {}", e),
        }
    }
}

impl error::Error for CANSocketOpenError {
    fn description(&self) -> &str {
        match *self {
            CANSocketOpenError::LookupError(_)
                => "can device not found",
            CANSocketOpenError::IOError(ref e)
                => e.description(),
        }
    }

    fn cause(&self) -> Option<&error::Error> {
        match *self {
            CANSocketOpenError::LookupError(ref e) => Some(e),
            CANSocketOpenError::IOError(ref e) => Some(e),
        }
    }
}

#[derive(Debug, Copy, Clone)]
/// Error that occurs when creating CAN packets
pub enum ConstructionError {
    /// CAN ID was outside the range of valid IDs
    IDTooLarge,
    /// More than 8 Bytes of payload data were passed in
    TooMuchData,
}

impl fmt::Display for ConstructionError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ConstructionError::IDTooLarge
                => write!(f, "CAN ID too large"),
            ConstructionError::TooMuchData
                => write!(f, "Payload is larger than CAN maximum of 8 bytes")
        }
    }
}

impl error::Error for ConstructionError {
    fn description(&self) -> &str {
        match *self {
            ConstructionError::IDTooLarge => "can id too large",
            ConstructionError::TooMuchData => "too much data",
        }
    }
}

impl From<nix::Error> for CANSocketOpenError {
    fn from(e: nix::Error) -> CANSocketOpenError {
        CANSocketOpenError::LookupError(e)
    }
}

impl From<io::Error> for CANSocketOpenError {
    fn from(e: io::Error) -> CANSocketOpenError {
        CANSocketOpenError::IOError(e)
    }
}

/// A socket for a CAN device.
///
/// Will be closed upon deallocation. To close manually, use std::drop::Drop.
/// Internally this is just a wrapped file-descriptor.
pub struct CANSocket {
    fd: c_int,
}

impl CANSocket {
    /// Open a named CAN device.
    ///
    /// Usually the more common case, opens a socket can device by name, such
    /// as "vcan0" or "socan0".
    pub fn open(ifname: &str) -> Result<CANSocket, CANSocketOpenError> {
        let if_index = try!(if_nametoindex(ifname));
        CANSocket::open_if(if_index)
    }

    /// Open CAN device by interface number.
    ///
    /// Opens a CAN device by kernel interface number.
    pub fn open_if(if_index: c_uint) -> Result<CANSocket, CANSocketOpenError> {
        let addr = CANAddr{
            _af_can: AF_CAN as c_short,
            if_index: if_index as c_int,
            rx_id: 0, // ?
            tx_id: 0, // ?
        };

        // open socket
        let sock_fd;
        unsafe {
            sock_fd = socket(PF_CAN, SOCK_RAW, CAN_RAW);
        }

        if sock_fd == -1 {
            return Err(CANSocketOpenError::from(io::Error::last_os_error()));
        }

        // bind it
        let bind_rv;
        unsafe {
            let sockaddr_ptr = &addr as *const CANAddr;
            bind_rv = bind(sock_fd, sockaddr_ptr as *const sockaddr,
                size_of::<CANAddr> () as u32);
        }

        if bind_rv == -1 {
            let e = io::Error::last_os_error();
            unsafe {
                close(sock_fd);
            }
            return Err(CANSocketOpenError::from(e))
        }

        Ok(CANSocket{fd: sock_fd})
    }

    fn close(&mut self) -> io::Result<()> {
        unsafe {
            let rv = close(self.fd);
            if rv != -1 {
                return Err(io::Error::last_os_error());
            }
        }
        Ok(())
    }

    /// Sets the read timeout on the socket
    ///
    /// For convenience, the result value can be checked using
    /// `ShouldRetry::should_retry` when a timeout is set.
    pub fn set_read_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_RCVTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Sets the write timeout on the socket
    pub fn set_write_timeout(&self, duration: time::Duration) -> io::Result<()> {
        let rv = unsafe {
            let tv = c_timeval_new(duration);
            let tv_ptr: *const timeval = &tv as *const timeval;
            setsockopt(self.fd, SOL_SOCKET, SO_SNDTIMEO,
                       tv_ptr as *const c_void, size_of::<timeval>() as u32)
        };

        if rv != 0 {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }

    /// Blocking read a single can frame.
    pub fn read_frame(&self) -> io::Result<CANFrame> {
        let mut frame = CANFrame{
            _id: 0,
            _data_len: 0,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: [0; 8],
        };

        let read_rv = unsafe {
            let frame_ptr = &mut frame as *mut CANFrame;
            read(self.fd, frame_ptr as *mut c_void, size_of::<CANFrame>())
        };

        if read_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(frame)
    }

    /// Blocking write a single can frame.
    pub fn write_frame(&self, frame: &CANFrame) -> io::Result<()> {
        // not a mutable reference needed (see std::net::UdpSocket) for
        // a comparison
        // debug!("Sending: {:?}", frame);

        let write_rv = unsafe {
            let frame_ptr = frame as *const CANFrame;
            write(self.fd, frame_ptr as *const c_void, size_of::<CANFrame>())
        };

        if write_rv as usize != size_of::<CANFrame>() {
            return Err(io::Error::last_os_error());
        }

        Ok(())
    }
}

impl Drop for CANSocket {
    fn drop(&mut self) {
        self.close().ok();  // ignore result
    }
}

/// CANFrame
///
/// Uses the same memory layout as the underlying kernel struct for performance
/// reasons.
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct CANFrame {
    /// 32 bit CAN_ID + EFF/RTR/ERR flags
    _id: u32,

    /// data length. Bytes beyond are not valid
    _data_len: u8,

    /// padding
    _pad: u8,

    /// reserved
    _res0: u8,

    /// reserved
    _res1: u8,

    /// buffer for data
    _data: [u8; 8],
}

impl CANFrame {
    pub fn new(id: u32, data: &[u8], rtr: bool, err: bool) -> Result<CANFrame,
    ConstructionError> {
        let mut _id = id;

        if data.len() > 8 {
            return Err(ConstructionError::TooMuchData)
        }

        if id > EFF_MASK {
            return Err(ConstructionError::IDTooLarge)
        }

        // set EFF_FLAG on large message
        if id > SFF_MASK {
            _id |= EFF_FLAG;
        }

        if rtr {
            _id |= RTR_FLAG;
        }

        if err {
            _id |= ERR_FLAG;
        }

        let mut full_data = [0; 8];

        // not cool =/
        for (n, c) in data.iter().enumerate() {
            full_data[n] = *c;
        }

        Ok(CANFrame{
            _id: _id,
            _data_len: data.len() as u8,
            _pad: 0,
            _res0: 0,
            _res1: 0,
            _data: full_data,
        })
    }

    /// Return the actual CAN ID (without EFF/RTR/ERR flags)
    pub fn id(&self) -> u32 {
        if self.is_extended() {
            self._id & EFF_MASK
        } else {
            self._id & SFF_MASK
        }
    }

    /// Return the error message
    pub fn err(&self) -> u32 {
        return self._id & ERR_MASK
    }

    /// Check if frame uses 29 bit extended frame format
    pub fn is_extended(&self) -> bool {
        self._id & EFF_FLAG != 0
    }

    /// Check if frame is an error message
    pub fn is_error(&self) -> bool {
        self._id & ERR_FLAG != 0
    }

    /// Check if frame is a remote transmission request
    pub fn is_rtr(&self) -> bool {
        self._id & RTR_FLAG != 0
    }

    /// A slice into the actual data. Slice will always be <= 8 bytes in length
    pub fn data(&self) -> &[u8] {
        &self._data[..(self._data_len as usize)]
    }
}

impl fmt::UpperHex for CANFrame {
    fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        try!(write!(f, "{:X}#", self.id()));

        let mut parts = self.data().iter().map(|v| format!("{:02X}", v));

        let sep = if f.alternate() { " " } else { " " };
        write!(f, "{}", parts.join(sep))
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Import crates with necessary types into a new project.

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

// Import necessary types from crates.

use exonum::blockchain::{Blockchain, Service, Transaction, ApiContext};
use exonum::encoding::serialize::FromHex;
use exonum::node::{TransactionSend, ApiSender};
use exonum::messages::{RawTransaction, Message};
use exonum::storage::{Fork, MapIndex};
use exonum::crypto::{PublicKey, Hash};
use exonum::encoding;
use exonum::api::{Api, ApiError};
use iron::prelude::*;
use iron::Handler;
use router::Router;
use serde::Deserialize;

// // // // // // // // // // CONSTANTS // // // // // // // // // //

// Define service ID for the service trait.

const SERVICE_ID: u16 = 1;

// Define constants for transaction types within the service.

const TX_CREATE_WALLET_ID: u16 = 1;

const TX_TRANSFER_ID: u16 = 2;

// Define initial balance of a newly created wallet.

const INIT_BALANCE: u64 = 100;

// // // // // // // // // // PERSISTENT DATA // // // // // // // // // //

// Declare the data to be stored in the blockchain. In the present case,
// declare a type for storing information about the wallet and its balance.

/// Declare a [serializable][1] struct and determine bounds of its fields
/// with `encoding_struct!` macro.
///
/// [1]: https://exonum.com/doc/architecture/serialization
encoding_struct! {
    struct Wallet {
        const SIZE = 48;

        field pub_key:            &PublicKey  [00 => 32]
        field name:               &str        [32 => 40]
        field balance:            u64         [40 => 48]
    }
}

/// Add methods to the `Wallet` type for changing balance.
impl Wallet {
    pub fn increase(self, amount: u64) -> Self {
        let balance = self.balance() + amount;
        Self::new(self.pub_key(), self.name(), balance)
    }

    pub fn decrease(self, amount: u64) -> Self {
        let balance = self.balance() - amount;
        Self::new(self.pub_key(), self.name(), balance)
    }
}

// // // // // // // // // // DATA LAYOUT // // // // // // // // // //

/// Create schema of the key-value storage implemented by `MemoryDB`. In the
/// present case a `Fork` of the database is used.
pub struct CurrencySchema<'a> {
    view: &'a mut Fork,
}

/// Declare layout of the data. Use an instance of [`MapIndex`]
/// to keep wallets in storage. Index values are serialized `Wallet` structs.
///
/// Isolate the wallets map into a separate entity by adding a unique prefix,
/// i.e. the first argument to the `MapIndex::new` call.
///
/// [`MapIndex`]: https://exonum.com/doc/architecture/storage#mapindex
impl<'a> CurrencySchema<'a> {
    pub fn wallets(&mut self) -> MapIndex<&mut Fork, PublicKey, Wallet> {
        MapIndex::new("cryptocurrency.wallets", self.view)
    }

    /// Get a separate wallet from the storage.
    pub fn wallet(&mut self, pub_key: &PublicKey) -> Option<Wallet> {
        self.wallets().get(pub_key)
    }
}

// // // // // // // // // // TRANSACTIONS // // // // // // // // // //

/// Create a new wallet.
message! {
    struct TxCreateWallet {
        const TYPE = SERVICE_ID;
        const ID = TX_CREATE_WALLET_ID;
        const SIZE = 40;

        field pub_key:     &PublicKey  [00 => 32]
        field name:        &str        [32 => 40]
    }
}

/// Transfer coins between the wallets.
message! {
    struct TxTransfer {
        const TYPE = SERVICE_ID;
        const ID = TX_TRANSFER_ID;
        const SIZE = 80;

        field from:        &PublicKey  [00 => 32]
        field to:          &PublicKey  [32 => 64]
        field amount:      u64         [64 => 72]
        field seed:        u64         [72 => 80]
    }
}

// // // // // // // // // // CONTRACTS // // // // // // // // // //

/// Execute a transaction.
impl Transaction for TxCreateWallet {
    /// Verify integrity of the transaction by checking the transaction
    /// signature.
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    /// Apply logic to the storage when executing the transaction.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        if schema.wallet(self.pub_key()).is_none() {
            let wallet = Wallet::new(self.pub_key(), self.name(), INIT_BALANCE);
            println!("Create the wallet: {:?}", wallet);
            schema.wallets().put(self.pub_key(), wallet)
        }
    }

    /// Provide information about the transaction to be used in the blockchain explorer.
    fn info(&self) -> serde_json::Value {
        serde_json::to_value(&self).expect("Cannot serialize transaction to JSON")
    }
}

impl Transaction for TxTransfer {
    /// Check if the sender is not the receiver. Check correctness of the
    /// sender's signature.
    fn verify(&self) -> bool {
        (*self.from() != *self.to()) && self.verify_signature(self.from())
    }

    /// Retrieve two wallets to apply the transfer. Check the sender's
    /// balance and apply changes to the balances of the wallets.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema { view };
        let sender = schema.wallet(self.from());
        let receiver = schema.wallet(self.to());
        if let (Some(sender), Some(receiver)) = (sender, receiver) {
            let amount = self.amount();
            if sender.balance() >= amount {
                let sender = sender.decrease(amount);
                let receiver = receiver.increase(amount);
                println!("Transfer between wallets: {:?} => {:?}", sender, receiver);
                let mut wallets = schema.wallets();
                wallets.put(self.from(), sender);
                wallets.put(self.to(), receiver);
            }
        }
    }

    /// Provide information about the transaction to be used in the blockchain explorer.
    fn info(&self) -> serde_json::Value {
        serde_json::to_value(&self).expect("Cannot serialize transaction to JSON")
    }
}

// // // // // // // // // // REST API // // // // // // // // // //

/// Implement the node API.
#[derive(Clone)]
struct CryptocurrencyApi {
    channel: ApiSender,
    blockchain: Blockchain,
}

/// The structure returned by the REST API.
#[derive(Serialize, Deserialize)]
pub struct TransactionResponse {
    pub tx_hash: Hash,
}

/// Shortcut to get data on wallets.
impl CryptocurrencyApi {
    /// Endpoint for getting a single wallet.
    fn get_wallet(&self, req: &mut Request) -> IronResult<Response> {
        let path = req.url.path();
        let wallet_key = path.last().unwrap();
        let public_key = PublicKey::from_hex(wallet_key).map_err(ApiError::FromHex)?;

        let wallet = {
            let mut view = self.blockchain.fork();
            let mut schema = CurrencySchema { view: &mut view };
            schema.wallet(&public_key)
        };

        if let Some(wallet) = wallet {
            self.ok_response(&serde_json::to_value(wallet).unwrap())
        } else {
            self.not_found_response(&serde_json::to_value("Wallet not found").unwrap())
        }
    }

    /// Endpoint for dumping all wallets from the storage.
    fn get_wallets(&self, _: &mut Request) -> IronResult<Response> {
        let mut view = self.blockchain.fork();
        let mut schema = CurrencySchema { view: &mut view };
        let idx = schema.wallets();
        let wallets: Vec<Wallet> = idx.values().collect();

        self.ok_response(&serde_json::to_value(&wallets).unwrap())
    }

    /// Common processing for transaction-accepting endpoints.
    fn post_transaction<T>(&self, req: &mut Request) -> IronResult<Response>
    where
        T: Transaction + Clone + for<'de> Deserialize<'de>,
    {
        match req.get::<bodyparser::Struct<T>>() {
            Ok(Some(transaction)) => {
                let transaction: Box<Transaction> = Box::new(transaction);
                let tx_hash = transaction.hash();
                self.channel.send(transaction).map_err(ApiError::from)?;
                let json = TransactionResponse { tx_hash };
                self.ok_response(&serde_json::to_value(&json).unwrap())
            }
            Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
            Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
        }
    }
}

/// Implement the `Api` trait.
/// `Api` facilitates conversion between transactions/read requests and REST
/// endpoints; for example, it parses `POSTed` JSON into the binary transaction
/// representation used in Exonum internally.
impl Api for CryptocurrencyApi {
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let post_create_wallet =
            move |req: &mut Request| self_.post_transaction::<TxCreateWallet>(req);
        let self_ = self.clone();
        let post_transfer = move |req: &mut Request| self_.post_transaction::<TxTransfer>(req);
        let self_ = self.clone();
        let get_wallets = move |req: &mut Request| self_.get_wallets(req);
        let self_ = self.clone();
        let get_wallet = move |req: &mut Request| self_.get_wallet(req);

        // Bind handlers to specific routes.
        router.post("/v1/wallets", post_create_wallet, "post_create_wallet");
        router.post("/v1/wallets/transfer", post_transfer, "post_transfer");
        router.get("/v1/wallets", get_wallets, "get_wallets");
        router.get("/v1/wallet/:pub_key", get_wallet, "get_wallet");
    }
}

// // // // // // // // // // SERVICE DECLARATION // // // // // // // // // //

/// Define the service.
pub struct CurrencyService;

/// Implement a `Service` trait for the service.
impl Service for CurrencyService {
    fn service_name(&self) -> &'static str {
        "cryptocurrency"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    /// Implement a method to deserialize transactions coming to the node.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        let trans: Box<Transaction> = match raw.message_type() {
            TX_TRANSFER_ID => Box::new(TxTransfer::from_raw(raw)?),
            TX_CREATE_WALLET_ID => Box::new(TxCreateWallet::from_raw(raw)?),
            _ => {
                return Err(encoding::Error::IncorrectMessageType {
                    message_type: raw.message_type(),
                });
            }
        };
        Ok(trans)
    }

    /// Create a REST `Handler` to process web requests to the node.
    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = CryptocurrencyApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}
<commit_msg>Make service schema generic<commit_after>// Copyright 2017 The Exonum Team
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Import crates with necessary types into a new project.

extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate exonum;
extern crate router;
extern crate bodyparser;
extern crate iron;

// Import necessary types from crates.

use exonum::blockchain::{Blockchain, Service, Transaction, ApiContext};
use exonum::encoding::serialize::FromHex;
use exonum::node::{TransactionSend, ApiSender};
use exonum::messages::{RawTransaction, Message};
use exonum::storage::{Fork, MapIndex, Snapshot};
use exonum::crypto::{PublicKey, Hash};
use exonum::encoding;
use exonum::api::{Api, ApiError};
use iron::prelude::*;
use iron::Handler;
use router::Router;
use serde::Deserialize;

// // // // // // // // // // CONSTANTS // // // // // // // // // //

// Define service ID for the service trait.

const SERVICE_ID: u16 = 1;

// Define constants for transaction types within the service.

const TX_CREATE_WALLET_ID: u16 = 1;

const TX_TRANSFER_ID: u16 = 2;

// Define initial balance of a newly created wallet.

const INIT_BALANCE: u64 = 100;

// // // // // // // // // // PERSISTENT DATA // // // // // // // // // //

// Declare the data to be stored in the blockchain. In the present case,
// declare a type for storing information about the wallet and its balance.

/// Declare a [serializable][1] struct and determine bounds of its fields
/// with `encoding_struct!` macro.
///
/// [1]: https://exonum.com/doc/architecture/serialization
encoding_struct! {
    struct Wallet {
        const SIZE = 48;

        field pub_key:            &PublicKey  [00 => 32]
        field name:               &str        [32 => 40]
        field balance:            u64         [40 => 48]
    }
}

/// Add methods to the `Wallet` type for changing balance.
impl Wallet {
    pub fn increase(self, amount: u64) -> Self {
        let balance = self.balance() + amount;
        Self::new(self.pub_key(), self.name(), balance)
    }

    pub fn decrease(self, amount: u64) -> Self {
        let balance = self.balance() - amount;
        Self::new(self.pub_key(), self.name(), balance)
    }
}

// // // // // // // // // // DATA LAYOUT // // // // // // // // // //

/// Schema of the key-value storage used by the demo cryptocurrency service.
pub struct CurrencySchema<T> {
    view: T,
}

/// Declare layout of the data. Use an instance of [`MapIndex`]
/// to keep wallets in storage. Index values are serialized `Wallet` structs.
///
/// [`MapIndex`]: https://exonum.com/doc/architecture/storage#mapindex
impl<T: AsRef<Snapshot>> CurrencySchema<T> {
    /// Creates the schema instance.
    pub fn new(view: T) -> Self {
        CurrencySchema { view }
    }

    /// Returns an immutable version of the wallets table.
    pub fn wallets(&self) -> MapIndex<&Snapshot, PublicKey, Wallet> {
        MapIndex::new("cryptocurrency.wallets", self.view.as_ref())
    }

    /// Gets a separate wallet from the storage.
    pub fn wallet(&self, pub_key: &PublicKey) -> Option<Wallet> {
        self.wallets().get(pub_key)
    }
}

/// A mutable version of the schema declaring an additional method to persist wallets
/// to the storage.
impl<'a> CurrencySchema<&'a mut Fork> {
    /// Returns a mutable version of the wallets table.
    pub fn wallets_mut(&mut self) -> MapIndex<&mut Fork, PublicKey, Wallet> {
        MapIndex::new("cryptocurrency.wallets", &mut self.view)
    }
}

// // // // // // // // // // TRANSACTIONS // // // // // // // // // //

/// Create a new wallet.
message! {
    struct TxCreateWallet {
        const TYPE = SERVICE_ID;
        const ID = TX_CREATE_WALLET_ID;
        const SIZE = 40;

        field pub_key:     &PublicKey  [00 => 32]
        field name:        &str        [32 => 40]
    }
}

/// Transfer coins between the wallets.
message! {
    struct TxTransfer {
        const TYPE = SERVICE_ID;
        const ID = TX_TRANSFER_ID;
        const SIZE = 80;

        field from:        &PublicKey  [00 => 32]
        field to:          &PublicKey  [32 => 64]
        field amount:      u64         [64 => 72]
        field seed:        u64         [72 => 80]
    }
}

// // // // // // // // // // CONTRACTS // // // // // // // // // //

/// Execute a transaction.
impl Transaction for TxCreateWallet {
    /// Verify integrity of the transaction by checking the transaction
    /// signature.
    fn verify(&self) -> bool {
        self.verify_signature(self.pub_key())
    }

    /// Apply logic to the storage when executing the transaction.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema::new(view);
        if schema.wallet(self.pub_key()).is_none() {
            let wallet = Wallet::new(self.pub_key(), self.name(), INIT_BALANCE);
            println!("Create the wallet: {:?}", wallet);
            schema.wallets_mut().put(self.pub_key(), wallet)
        }
    }

    /// Provide information about the transaction to be used in the blockchain explorer.
    fn info(&self) -> serde_json::Value {
        serde_json::to_value(&self).expect("Cannot serialize transaction to JSON")
    }
}

impl Transaction for TxTransfer {
    /// Check if the sender is not the receiver. Check correctness of the
    /// sender's signature.
    fn verify(&self) -> bool {
        (*self.from() != *self.to()) && self.verify_signature(self.from())
    }

    /// Retrieve two wallets to apply the transfer. Check the sender's
    /// balance and apply changes to the balances of the wallets.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema::new(view);
        let sender = schema.wallet(self.from());
        let receiver = schema.wallet(self.to());
        if let (Some(sender), Some(receiver)) = (sender, receiver) {
            let amount = self.amount();
            if sender.balance() >= amount {
                let sender = sender.decrease(amount);
                let receiver = receiver.increase(amount);
                println!("Transfer between wallets: {:?} => {:?}", sender, receiver);
                let mut wallets = schema.wallets_mut();
                wallets.put(self.from(), sender);
                wallets.put(self.to(), receiver);
            }
        }
    }

    /// Provide information about the transaction to be used in the blockchain explorer.
    fn info(&self) -> serde_json::Value {
        serde_json::to_value(&self).expect("Cannot serialize transaction to JSON")
    }
}

// // // // // // // // // // REST API // // // // // // // // // //

/// Implement the node API.
#[derive(Clone)]
struct CryptocurrencyApi {
    channel: ApiSender,
    blockchain: Blockchain,
}

/// The structure returned by the REST API.
#[derive(Serialize, Deserialize)]
pub struct TransactionResponse {
    pub tx_hash: Hash,
}

/// Shortcut to get data on wallets.
impl CryptocurrencyApi {
    /// Endpoint for getting a single wallet.
    fn get_wallet(&self, req: &mut Request) -> IronResult<Response> {
        let path = req.url.path();
        let wallet_key = path.last().unwrap();
        let public_key = PublicKey::from_hex(wallet_key).map_err(ApiError::FromHex)?;

        let wallet = {
            let snapshot = self.blockchain.snapshot();
            let schema = CurrencySchema::new(snapshot);
            schema.wallet(&public_key)
        };

        if let Some(wallet) = wallet {
            self.ok_response(&serde_json::to_value(wallet).unwrap())
        } else {
            self.not_found_response(&serde_json::to_value("Wallet not found").unwrap())
        }
    }

    /// Endpoint for dumping all wallets from the storage.
    fn get_wallets(&self, _: &mut Request) -> IronResult<Response> {
        let snapshot = self.blockchain.snapshot();
        let schema = CurrencySchema::new(snapshot);
        let idx = schema.wallets();
        let wallets: Vec<Wallet> = idx.values().collect();

        self.ok_response(&serde_json::to_value(&wallets).unwrap())
    }

    /// Common processing for transaction-accepting endpoints.
    fn post_transaction<T>(&self, req: &mut Request) -> IronResult<Response>
    where
        T: Transaction + Clone + for<'de> Deserialize<'de>,
    {
        match req.get::<bodyparser::Struct<T>>() {
            Ok(Some(transaction)) => {
                let transaction: Box<Transaction> = Box::new(transaction);
                let tx_hash = transaction.hash();
                self.channel.send(transaction).map_err(ApiError::from)?;
                let json = TransactionResponse { tx_hash };
                self.ok_response(&serde_json::to_value(&json).unwrap())
            }
            Ok(None) => Err(ApiError::IncorrectRequest("Empty request body".into()))?,
            Err(e) => Err(ApiError::IncorrectRequest(Box::new(e)))?,
        }
    }
}

/// Implement the `Api` trait.
/// `Api` facilitates conversion between transactions/read requests and REST
/// endpoints; for example, it parses `POSTed` JSON into the binary transaction
/// representation used in Exonum internally.
impl Api for CryptocurrencyApi {
    fn wire(&self, router: &mut Router) {
        let self_ = self.clone();
        let post_create_wallet =
            move |req: &mut Request| self_.post_transaction::<TxCreateWallet>(req);
        let self_ = self.clone();
        let post_transfer = move |req: &mut Request| self_.post_transaction::<TxTransfer>(req);
        let self_ = self.clone();
        let get_wallets = move |req: &mut Request| self_.get_wallets(req);
        let self_ = self.clone();
        let get_wallet = move |req: &mut Request| self_.get_wallet(req);

        // Bind handlers to specific routes.
        router.post("/v1/wallets", post_create_wallet, "post_create_wallet");
        router.post("/v1/wallets/transfer", post_transfer, "post_transfer");
        router.get("/v1/wallets", get_wallets, "get_wallets");
        router.get("/v1/wallet/:pub_key", get_wallet, "get_wallet");
    }
}

// // // // // // // // // // SERVICE DECLARATION // // // // // // // // // //

/// Define the service.
pub struct CurrencyService;

/// Implement a `Service` trait for the service.
impl Service for CurrencyService {
    fn service_name(&self) -> &'static str {
        "cryptocurrency"
    }

    fn service_id(&self) -> u16 {
        SERVICE_ID
    }

    /// Implement a method to deserialize transactions coming to the node.
    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, encoding::Error> {
        let trans: Box<Transaction> = match raw.message_type() {
            TX_TRANSFER_ID => Box::new(TxTransfer::from_raw(raw)?),
            TX_CREATE_WALLET_ID => Box::new(TxCreateWallet::from_raw(raw)?),
            _ => {
                return Err(encoding::Error::IncorrectMessageType {
                    message_type: raw.message_type(),
                });
            }
        };
        Ok(trans)
    }

    /// Create a REST `Handler` to process web requests to the node.
    fn public_api_handler(&self, ctx: &ApiContext) -> Option<Box<Handler>> {
        let mut router = Router::new();
        let api = CryptocurrencyApi {
            channel: ctx.node_channel().clone(),
            blockchain: ctx.blockchain().clone(),
        };
        api.wire(&mut router);
        Some(Box::new(router))
    }
}
<|endoftext|>