<commit_before>use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::io::stderr;
use std::io::Write;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use logger::ImagLogger;

use libimagstore::store::Store;

#[derive(Debug)]
pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Option<Configuration>,
    cli_matches: ArgMatches<'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /**
     * Gets the CLI spec for the program and retreives the config file path (or uses the default on
     * in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
     * and builds the Runtime object with it.
     *
     * The cli_spec object should be initially build with the ::get_default_cli_builder() function.
     *
     */
    pub fn new(cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;

        use libimagstore::hook::position::HookPosition;
        use libimagstore::error::StoreErrorKind;
        use libimagstorestdhook::debug::DebugHook;
        use libimagerror::trace::trace_error;
        use libimagerror::trace::trace_error_dbg;
        use libimagerror::into::IntoError;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.get_matches();

        let is_debugging = matches.is_present("debugging");
        let is_verbose   = matches.is_present("verbosity");

        Runtime::init_logger(is_debugging, is_verbose);

        let rtp : PathBuf = matches.value_of("runtimepath")
            .map_or_else(|| {
                env::var("HOME")
                    .map(PathBuf::from)
                    .map(|mut p| { p.push(".imag"); p})
                    .unwrap_or_else(|_| {
                        panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
                    })
            }, PathBuf::from);
        let storepath = matches.value_of("storepath")
                                .map_or_else(|| {
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                }, PathBuf::from);

        let cfg = match Configuration::new(&rtp) {
            Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
                return Err(RuntimeErrorKind::Instantiate.into_error_with_cause(Box::new(e)));
            } else {
                warn!("No config file found.");
                warn!("Continuing without configuration file");
                None
            },

            Ok(cfg) => Some(cfg),
        };

        let store_config = match cfg {
            Some(ref c) => c.store_config().cloned(),
            None        => None,
        };

        if is_debugging {
            write!(stderr(), "Config: {:?}\n", cfg).ok();
            write!(stderr(), "Store-config: {:?}\n", store_config).ok();
        }

        Store::new(storepath, store_config).map(|mut store| {
            // If we are debugging, generate hooks for all positions
            if is_debugging {
                let hooks = vec![
                    (DebugHook::new(HookPosition::PreCreate), HookPosition::PreCreate),
                    (DebugHook::new(HookPosition::PostCreate), HookPosition::PostCreate),
                    (DebugHook::new(HookPosition::PreRetrieve), HookPosition::PreRetrieve),
                    (DebugHook::new(HookPosition::PostRetrieve), HookPosition::PostRetrieve),
                    (DebugHook::new(HookPosition::PreUpdate), HookPosition::PreUpdate),
                    (DebugHook::new(HookPosition::PostUpdate), HookPosition::PostUpdate),
                    (DebugHook::new(HookPosition::PreDelete), HookPosition::PreDelete),
                    (DebugHook::new(HookPosition::PostDelete), HookPosition::PostDelete),
                ];

                // Put all debug hooks into the aspect "debug".
                // If it fails, trace the error and warn, but continue.
                for (hook, position) in hooks {
                    if let Err(e) = store.register_hook(position, &String::from("debug"), Box::new(hook)) {
                        if e.err_type() == StoreErrorKind::HookRegisterError {
                            trace_error_dbg(&e);
                            warn!("Registering debug hook with store failed");
                        } else {
                            trace_error(&e);
                        };
                    }
                }
            }

            Runtime {
                cli_matches: matches,
                configuration: cfg,
                rtp: rtp,
                store: store,
            }
        })
        .map_err(Box::new)
        .map_err(|e| RuntimeErrorKind::Instantiate.into_error_with_cause(e))
    }

    /**
     * Get a commandline-interface builder object from `clap`
     *
     * This commandline interface builder object already contains some predefined interface flags:
     *   * -v | --verbose for verbosity
     *   * --debug for debugging
     *   * -c <file> | --config <file> for alternative configuration file
     *   * -r <path> | --rtp <path> for alternative runtimepath
     *   * --store <path> for alternative store path
     * Each has the appropriate help text included.
     *
     * The `appname` shall be "imag-<command>".
     */
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name("verbosity")
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("debugging")
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("config")
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("runtimepath")
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("storepath")
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("editor")
                .long("editor")
                .help("Set editor")
                .required(false)
                .takes_value(true))
    }

    /**
     * Initialize the internal logger
     */
    fn init_logger(is_debugging: bool, is_verbose: bool) {
        use std::env::var as env_var;
        use env_logger;

        if env_var("IMAG_LOG_ENV").is_ok() {
            env_logger::init().unwrap();
        } else {
            let lvl = if is_debugging {
                LogLevelFilter::Debug
            } else if is_verbose {
                LogLevelFilter::Info
            } else {
                LogLevelFilter::Error
            };

            log::set_logger(|max_log_lvl| {
                max_log_lvl.set(lvl);
                debug!("Init logger with {}", lvl);
                Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
            })
            .map_err(|_| {
                panic!("Could not setup logger");
            })
            .ok();
        }
    }

    /**
     * Get the verbosity flag value
     */
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /**
     * Get the debugging flag value
     */
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /**
     * Get the runtimepath
     */
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /**
     * Get the commandline interface matches
     */
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /**
     * Get the configuration object
     */
    pub fn config(&self) -> Option<&Configuration> {
        self.configuration.as_ref()
    }

    /**
     * Get the store object
     */
    pub fn store(&self) -> &Store {
        &self.store
    }

    pub fn editor(&self) -> Option<Command> {
        self.cli()
            .value_of("editor")
            .map(String::from)
            .or({
                match self.configuration {
                    Some(ref c) => c.editor().cloned(),
                    _ => None,
                }
            })
            .or(env::var("EDITOR").ok())
            .map(Command::new)
    }
}

<commit_msg>Fix: Pass the confguration path as parameter for Configuration::new()<commit_after>use std::path::PathBuf;
use std::process::Command;
use std::env;
use std::io::stderr;
use std::io::Write;

pub use clap::App;

use clap::{Arg, ArgMatches};
use log;
use log::LogLevelFilter;

use configuration::Configuration;
use error::RuntimeError;
use error::RuntimeErrorKind;
use logger::ImagLogger;

use libimagstore::store::Store;

#[derive(Debug)]
pub struct Runtime<'a> {
    rtp: PathBuf,
    configuration: Option<Configuration>,
    cli_matches: ArgMatches<'a>,
    store: Store,
}

impl<'a> Runtime<'a> {

    /**
     * Gets the CLI spec for the program and retreives the config file path (or uses the default on
     * in $HOME/.imag/config, $XDG_CONFIG_DIR/imag/config or from env("$IMAG_CONFIG")
     * and builds the Runtime object with it.
     *
     * The cli_spec object should be initially build with the ::get_default_cli_builder() function.
     *
     */
    pub fn new(cli_spec: App<'a, 'a>) -> Result<Runtime<'a>, RuntimeError> {
        use std::env;

        use libimagstore::hook::position::HookPosition;
        use libimagstore::error::StoreErrorKind;
        use libimagstorestdhook::debug::DebugHook;
        use libimagerror::trace::trace_error;
        use libimagerror::trace::trace_error_dbg;
        use libimagerror::into::IntoError;

        use configuration::error::ConfigErrorKind;

        let matches = cli_spec.get_matches();

        let is_debugging = matches.is_present("debugging");
        let is_verbose   = matches.is_present("verbosity");

        Runtime::init_logger(is_debugging, is_verbose);

        let rtp : PathBuf = matches.value_of("runtimepath")
            .map_or_else(|| {
                env::var("HOME")
                    .map(PathBuf::from)
                    .map(|mut p| { p.push(".imag"); p})
                    .unwrap_or_else(|_| {
                        panic!("You seem to be $HOME-less. Please get a $HOME before using this software. We are sorry for you and hope you have some accommodation anyways.");
                    })
            }, PathBuf::from);
        let storepath = matches.value_of("storepath")
                                .map_or_else(|| {
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                }, PathBuf::from);

        let configpath = matches.value_of("config")
                                .map_or_else(|| {
                                    let mut spath = rtp.clone();
                                    spath.push("store");
                                    spath
                                }, PathBuf::from);

        let cfg = match Configuration::new(&configpath) {
            Err(e) => if e.err_type() != ConfigErrorKind::NoConfigFileFound {
                return Err(RuntimeErrorKind::Instantiate.into_error_with_cause(Box::new(e)));
            } else {
                warn!("No config file found.");
                warn!("Continuing without configuration file");
                None
            },

            Ok(cfg) => Some(cfg),
        };

        let store_config = match cfg {
            Some(ref c) => c.store_config().cloned(),
            None        => None,
        };

        if is_debugging {
            write!(stderr(), "Config: {:?}\n", cfg).ok();
            write!(stderr(), "Store-config: {:?}\n", store_config).ok();
        }

        Store::new(storepath, store_config).map(|mut store| {
            // If we are debugging, generate hooks for all positions
            if is_debugging {
                let hooks = vec![
                    (DebugHook::new(HookPosition::PreCreate), HookPosition::PreCreate),
                    (DebugHook::new(HookPosition::PostCreate), HookPosition::PostCreate),
                    (DebugHook::new(HookPosition::PreRetrieve), HookPosition::PreRetrieve),
                    (DebugHook::new(HookPosition::PostRetrieve), HookPosition::PostRetrieve),
                    (DebugHook::new(HookPosition::PreUpdate), HookPosition::PreUpdate),
                    (DebugHook::new(HookPosition::PostUpdate), HookPosition::PostUpdate),
                    (DebugHook::new(HookPosition::PreDelete), HookPosition::PreDelete),
                    (DebugHook::new(HookPosition::PostDelete), HookPosition::PostDelete),
                ];

                // Put all debug hooks into the aspect "debug".
                // If it fails, trace the error and warn, but continue.
                for (hook, position) in hooks {
                    if let Err(e) = store.register_hook(position, &String::from("debug"), Box::new(hook)) {
                        if e.err_type() == StoreErrorKind::HookRegisterError {
                            trace_error_dbg(&e);
                            warn!("Registering debug hook with store failed");
                        } else {
                            trace_error(&e);
                        };
                    }
                }
            }

            Runtime {
                cli_matches: matches,
                configuration: cfg,
                rtp: rtp,
                store: store,
            }
        })
        .map_err(Box::new)
        .map_err(|e| RuntimeErrorKind::Instantiate.into_error_with_cause(e))
    }

    /**
     * Get a commandline-interface builder object from `clap`
     *
     * This commandline interface builder object already contains some predefined interface flags:
     *   * -v | --verbose for verbosity
     *   * --debug for debugging
     *   * -c <file> | --config <file> for alternative configuration file
     *   * -r <path> | --rtp <path> for alternative runtimepath
     *   * --store <path> for alternative store path
     * Each has the appropriate help text included.
     *
     * The `appname` shall be "imag-<command>".
     */
    pub fn get_default_cli_builder(appname: &'a str,
                                   version: &'a str,
                                   about: &'a str)
        -> App<'a, 'a>
    {
        App::new(appname)
            .version(version)
            .author("Matthias Beyer <mail@beyermatthias.de>")
            .about(about)
            .arg(Arg::with_name("verbosity")
                .short("v")
                .long("verbose")
                .help("Enables verbosity")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("debugging")
                .long("debug")
                .help("Enables debugging output")
                .required(false)
                .takes_value(false))

            .arg(Arg::with_name("config")
                .long("config")
                .help("Path to alternative config file")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("runtimepath")
                .long("rtp")
                .help("Alternative runtimepath")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("storepath")
                .long("store")
                .help("Alternative storepath. Must be specified as full path, can be outside of the RTP")
                .required(false)
                .takes_value(true))

            .arg(Arg::with_name("editor")
                .long("editor")
                .help("Set editor")
                .required(false)
                .takes_value(true))
    }

    /**
     * Initialize the internal logger
     */
    fn init_logger(is_debugging: bool, is_verbose: bool) {
        use std::env::var as env_var;
        use env_logger;

        if env_var("IMAG_LOG_ENV").is_ok() {
            env_logger::init().unwrap();
        } else {
            let lvl = if is_debugging {
                LogLevelFilter::Debug
            } else if is_verbose {
                LogLevelFilter::Info
            } else {
                LogLevelFilter::Error
            };

            log::set_logger(|max_log_lvl| {
                max_log_lvl.set(lvl);
                debug!("Init logger with {}", lvl);
                Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
            })
            .map_err(|_| {
                panic!("Could not setup logger");
            })
            .ok();
        }
    }

    /**
     * Get the verbosity flag value
     */
    pub fn is_verbose(&self) -> bool {
        self.cli_matches.is_present("verbosity")
    }

    /**
     * Get the debugging flag value
     */
    pub fn is_debugging(&self) -> bool {
        self.cli_matches.is_present("debugging")
    }

    /**
     * Get the runtimepath
     */
    pub fn rtp(&self) -> &PathBuf {
        &self.rtp
    }

    /**
     * Get the commandline interface matches
     */
    pub fn cli(&self) -> &ArgMatches {
        &self.cli_matches
    }

    /**
     * Get the configuration object
     */
    pub fn config(&self) -> Option<&Configuration> {
        self.configuration.as_ref()
    }

    /**
     * Get the store object
     */
    pub fn store(&self) -> &Store {
        &self.store
    }

    pub fn editor(&self) -> Option<Command> {
        self.cli()
            .value_of("editor")
            .map(String::from)
            .or({
                match self.configuration {
                    Some(ref c) => c.editor().cloned(),
                    _ => None,
                }
            })
            .or(env::var("EDITOR").ok())
            .map(Command::new)
    }
}

<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

#[allow(unused_variables)]
pub mod storeid {
    use std::path::PathBuf;

    use ruru::{Class, Object, AnyObject, Boolean, RString, NilClass};

    use libimagstore::storeid::StoreId;

    wrappable_struct!(StoreId, StoreIdWrapper, STOREID_WRAPPER);
    class!(RStoreId);

    methods!(
        RStoreId,
        itself,

        fn r_storeid_new(base: RString, id: RString) -> AnyObject {
            let base = match base.map(|b| b.to_string()).map(PathBuf::from) {
                Ok(base) => base,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            let id = match id.map(|id| id.to_string()).map(PathBuf::from) {
                Ok(id) => id,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            match StoreId::new(Some(base), id) {
                Ok(sid) => Class::from_existing("RStoreId").wrap_data(sid, &*STOREID_WRAPPER),
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            }
        }

        fn r_storeid_new_baseless(id: RString) -> AnyObject {
            let id = match id.map(|id| id.to_string()).map(PathBuf::from) {
                Ok(id) => id,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            match StoreId::new(None, id) {
                Ok(sid) => Class::from_existing("RStoreId").wrap_data(sid, &*STOREID_WRAPPER),
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            }
        }

        fn r_storeid_without_base() -> RStoreId {
            let withoutbase : StoreId = itself.get_data(&*STOREID_WRAPPER).clone().without_base();
            Class::from_existing("RStoreId").wrap_data(withoutbase, &*STOREID_WRAPPER)
        }

        fn r_storeid_with_base(base: RString) -> AnyObject {
            let base : PathBuf = match base.map(|b| b.to_string()).map(PathBuf::from) {
                Ok(pb) => pb,
                Err(e) => {
                    // TODO: Exception!
                    error!("Error: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };
            let withoutbase : StoreId = itself.get_data(&*STOREID_WRAPPER).clone().with_base(base);
            Class::from_existing("RStoreId").wrap_data(withoutbase, &*STOREID_WRAPPER)
        }

        fn r_storeid_into_pathbuf() -> AnyObject {
            itself.get_data(&*STOREID_WRAPPER)
                .clone()
                .into_pathbuf()
                // TODO: No unwraps
                .map(|pb| pb.to_str().map(String::from).unwrap())
                .as_ref()
                .map(|s| AnyObject::from(RString::new(s).value()))
                // TODO: Exception!
                .unwrap_or(AnyObject::from(NilClass::new().value()))
        }

        fn r_storeid_exists() -> Boolean {
            Boolean::new(itself.get_data(&*STOREID_WRAPPER).exists())
        }

        fn r_storeid_to_str() -> AnyObject {
            itself.get_data(&*STOREID_WRAPPER)
                .to_str()
                .as_ref()
                .map(|s| AnyObject::from(RString::new(s).value()))
                // TODO: Exception!
                .unwrap_or(AnyObject::from(NilClass::new().value()))
        }

        fn r_storeid_local() -> RString {
            let local = itself.get_data(&*STOREID_WRAPPER).local();
            let local = local.to_str().unwrap(); // TODO: No unwraps
            RString::new(local)
        }

    );

    pub fn setup() -> Class {
        let mut class = Class::new("RStoreId", None);
        class.define(|itself| {
            itself.def_self("new"          , r_storeid_new);
            itself.def_self("new_baseless" , r_storeid_new_baseless);

            itself.def("without_base"      , r_storeid_without_base);
            itself.def("with_base"         , r_storeid_with_base);
            itself.def("into_pathbuf"      , r_storeid_into_pathbuf);
            itself.def("exists"            , r_storeid_exists);
            itself.def("to_str"            , r_storeid_to_str);
            itself.def("local"             , r_storeid_local);
        });
        class
    }

}

#[allow(unused_variables)]
pub mod store {
    pub mod entry {
        use std::collections::BTreeMap;
        use std::error::Error;
        use std::ops::Deref;
        use std::ops::DerefMut;

        use ruru::{Class, Object, AnyObject, Boolean, RString, VM};

        use libimagstore::store::FileLockEntry as FLE;
        use libimagstore::store::EntryHeader;
        use libimagstore::store::EntryContent;
        use libimagstore::store::Entry;

        use ruby_utils::IntoToml;
        use toml_utils::IntoRuby;

        pub struct FLECustomWrapper(Box<FLE<'static>>);

        impl Deref for FLECustomWrapper {
            type Target = Box<FLE<'static>>;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl DerefMut for FLECustomWrapper {
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.0
            }
        }

        wrappable_struct!(FLECustomWrapper, FileLockEntryWrapper, FLE_WRAPPER);
        class!(RFileLockEntry);

        wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER);
        class!(REntryHeader);
        methods!(
            REntryHeader,
            itself,

            fn r_entry_header_new() -> AnyObject {
                Class::from_existing("REntryHeader")
                    .wrap_data(EntryHeader::new(), &*ENTRY_HEADER_WRAPPER)
            }

            fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false);
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.
                let obj = obj.unwrap(); // possibly not safe... TODO

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) {
                    Ok(b) => Boolean::new(b),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false);
                    }
                }
            }

            fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject {
                use ruru::NilClass;

                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false).to_any_object();
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.
                let obj = obj.unwrap(); // possibly not safe... TODO

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) {
                    Ok(Some(v)) => v.into_ruby(),
                    Ok(None)    => NilClass::new().to_any_object(),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false).to_any_object();
                    }
                }
            }

            fn r_entry_header_get(spec: RString) -> AnyObject {
                use ruru::NilClass;

                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false).to_any_object();
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) {
                    Ok(Some(v)) => v.into_ruby(),
                    Ok(None)    => NilClass::new().to_any_object(),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false).to_any_object();
                    }
                }
            }

        );

        wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER);
        class!(REntryContent);

        wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER);
        class!(REntry);

        pub fn setup_filelockentry() -> Class {
            let mut class = Class::new("RFileLockEntry", None);
            class
        }

        pub fn setup_entryheader() -> Class {
            let mut class = Class::new("REntryHeader", None);
            class.define(|itself| {
                itself.def("insert", r_entry_header_insert);
                itself.def("set"   , r_entry_header_set);
                itself.def("read"  , r_entry_header_get);
            });
            class
        }

        pub fn setup_entrycontent() -> Class {
            let string = Class::from_existing("String");
            let mut class = Class::new("REntryContent", Some(&string));
            class
        }
    }

}

<commit_msg>Add r_get_location() for RFileLockEntry<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

use ruru::AnyObject;
trait Wrap {
    fn wrap(self) -> AnyObject;
}

#[allow(unused_variables)]
pub mod storeid {
    use std::path::PathBuf;

    use ruru::{Class, Object, AnyObject, Boolean, RString, NilClass};

    use libimagstore::storeid::StoreId;

    wrappable_struct!(StoreId, StoreIdWrapper, STOREID_WRAPPER);
    class!(RStoreId);

    use store::Wrap;
    impl Wrap for StoreId {
        fn wrap(self) -> AnyObject {
            Class::from_existing("RStoreId").wrap_data(self, &*STOREID_WRAPPER)
        }
    }

    methods!(
        RStoreId,
        itself,

        fn r_storeid_new(base: RString, id: RString) -> AnyObject {
            let base = match base.map(|b| b.to_string()).map(PathBuf::from) {
                Ok(base) => base,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            let id = match id.map(|id| id.to_string()).map(PathBuf::from) {
                Ok(id) => id,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            match StoreId::new(Some(base), id) {
                Ok(sid) => Class::from_existing("RStoreId").wrap_data(sid, &*STOREID_WRAPPER),
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            }
        }

        fn r_storeid_new_baseless(id: RString) -> AnyObject {
            let id = match id.map(|id| id.to_string()).map(PathBuf::from) {
                Ok(id) => id,
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };

            match StoreId::new(None, id) {
                Ok(sid) => Class::from_existing("RStoreId").wrap_data(sid, &*STOREID_WRAPPER),
                Err(e) => {
                    // TODO: Exception!
                    error!("Building StoreId object failed: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            }
        }

        fn r_storeid_without_base() -> RStoreId {
            let withoutbase : StoreId = itself.get_data(&*STOREID_WRAPPER).clone().without_base();
            Class::from_existing("RStoreId").wrap_data(withoutbase, &*STOREID_WRAPPER)
        }

        fn r_storeid_with_base(base: RString) -> AnyObject {
            let base : PathBuf = match base.map(|b| b.to_string()).map(PathBuf::from) {
                Ok(pb) => pb,
                Err(e) => {
                    // TODO: Exception!
                    error!("Error: {:?}", e);
                    return AnyObject::from(NilClass::new().value());
                },
            };
            let withoutbase : StoreId = itself.get_data(&*STOREID_WRAPPER).clone().with_base(base);
            Class::from_existing("RStoreId").wrap_data(withoutbase, &*STOREID_WRAPPER)
        }

        fn r_storeid_into_pathbuf() -> AnyObject {
            itself.get_data(&*STOREID_WRAPPER)
                .clone()
                .into_pathbuf()
                // TODO: No unwraps
                .map(|pb| pb.to_str().map(String::from).unwrap())
                .as_ref()
                .map(|s| AnyObject::from(RString::new(s).value()))
                // TODO: Exception!
                .unwrap_or(AnyObject::from(NilClass::new().value()))
        }

        fn r_storeid_exists() -> Boolean {
            Boolean::new(itself.get_data(&*STOREID_WRAPPER).exists())
        }

        fn r_storeid_to_str() -> AnyObject {
            itself.get_data(&*STOREID_WRAPPER)
                .to_str()
                .as_ref()
                .map(|s| AnyObject::from(RString::new(s).value()))
                // TODO: Exception!
                .unwrap_or(AnyObject::from(NilClass::new().value()))
        }

        fn r_storeid_local() -> RString {
            let local = itself.get_data(&*STOREID_WRAPPER).local();
            let local = local.to_str().unwrap(); // TODO: No unwraps
            RString::new(local)
        }

    );

    pub fn setup() -> Class {
        let mut class = Class::new("RStoreId", None);
        class.define(|itself| {
            itself.def_self("new"          , r_storeid_new);
            itself.def_self("new_baseless" , r_storeid_new_baseless);

            itself.def("without_base"      , r_storeid_without_base);
            itself.def("with_base"         , r_storeid_with_base);
            itself.def("into_pathbuf"      , r_storeid_into_pathbuf);
            itself.def("exists"            , r_storeid_exists);
            itself.def("to_str"            , r_storeid_to_str);
            itself.def("local"             , r_storeid_local);
        });
        class
    }

}

#[allow(unused_variables)]
pub mod store {
    pub mod entry {
        use std::collections::BTreeMap;
        use std::error::Error;
        use std::ops::Deref;
        use std::ops::DerefMut;

        use ruru::{Class, Object, AnyObject, Boolean, RString, VM};

        use libimagstore::store::FileLockEntry as FLE;
        use libimagstore::store::EntryHeader;
        use libimagstore::store::EntryContent;
        use libimagstore::store::Entry;

        use ruby_utils::IntoToml;
        use toml_utils::IntoRuby;
        use store::Wrap;

        pub struct FLECustomWrapper(Box<FLE<'static>>);

        impl Deref for FLECustomWrapper {
            type Target = Box<FLE<'static>>;

            fn deref(&self) -> &Self::Target {
                &self.0
            }
        }

        impl DerefMut for FLECustomWrapper {
            fn deref_mut(&mut self) -> &mut Self::Target {
                &mut self.0
            }
        }

        wrappable_struct!(FLECustomWrapper, FileLockEntryWrapper, FLE_WRAPPER);
        class!(RFileLockEntry);

        methods!(
            RFileLockEntry,
            itself,

            fn r_get_location() -> AnyObject {
                itself.get_data(&*FLE_WRAPPER).get_location().clone().wrap()
            }
        );

        wrappable_struct!(EntryHeader, EntryHeaderWrapper, ENTRY_HEADER_WRAPPER);
        class!(REntryHeader);
        methods!(
            REntryHeader,
            itself,

            fn r_entry_header_new() -> AnyObject {
                Class::from_existing("REntryHeader")
                    .wrap_data(EntryHeader::new(), &*ENTRY_HEADER_WRAPPER)
            }

            fn r_entry_header_insert(spec: RString, obj: AnyObject) -> Boolean {
                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false);
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.
                let obj = obj.unwrap(); // possibly not safe... TODO

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).insert(&spec, obj.into_toml()) {
                    Ok(b) => Boolean::new(b),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false);
                    }
                }
            }

            fn r_entry_header_set(spec: RString, obj: AnyObject) -> AnyObject {
                use ruru::NilClass;

                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false).to_any_object();
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.
                let obj = obj.unwrap(); // possibly not safe... TODO

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).set(&spec, obj.into_toml()) {
                    Ok(Some(v)) => v.into_ruby(),
                    Ok(None)    => NilClass::new().to_any_object(),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false).to_any_object();
                    }
                }
            }

            fn r_entry_header_get(spec: RString) -> AnyObject {
                use ruru::NilClass;

                if let Err(ref error) = spec { // raise exception if "spec" is not a String
                    VM::raise(error.to_exception(), error.description());
                    return Boolean::new(false).to_any_object();
                }

                let spec = spec.unwrap().to_string(); // safe because of check above.

                match itself.get_data(&*ENTRY_HEADER_WRAPPER).read(&spec) {
                    Ok(Some(v)) => v.into_ruby(),
                    Ok(None)    => NilClass::new().to_any_object(),
                    Err(e) => {
                        VM::raise(Class::from_existing("RuntimeError"), e.description());
                        return Boolean::new(false).to_any_object();
                    }
                }
            }

        );

        wrappable_struct!(EntryContent, EntryContentWrapper, ENTRY_CONTENT_WRAPPER);
        class!(REntryContent);

        wrappable_struct!(Entry, EntryWrapper, ENTRY_WRAPPER);
        class!(REntry);

        pub fn setup_filelockentry() -> Class {
            let mut class = Class::new("RFileLockEntry", None);
            class
        }

        pub fn setup_entryheader() -> Class {
            let mut class = Class::new("REntryHeader", None);
            class.define(|itself| {
                itself.def("insert", r_entry_header_insert);
                itself.def("set"   , r_entry_header_set);
                itself.def("read"  , r_entry_header_get);
            });
            class
        }

        pub fn setup_entrycontent() -> Class {
            let string = Class::from_existing("String");
            let mut class = Class::new("REntryContent", Some(&string));
            class
        }
    }

}

<|endoftext|>


<commit_before>//! A module providing a data structure for messages to and from IRC servers.
use std::borrow::ToOwned;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str::FromStr;

use error;
use error::{Error, ErrorKind};
use proto::{Command, ChannelExt};

/// A data structure representing an IRC message according to the protocol specification. It
/// consists of a collection of IRCv3 tags, a prefix (describing the source of the message), and
/// the protocol command. If the command is unknown, it is treated as a special raw command that
/// consists of a collection of arguments and the special suffix argument. Otherwise, the command
/// is parsed into a more useful form as described in [Command](../command/enum.Command.html).
#[derive(Clone, PartialEq, Debug)]
pub struct Message {
    /// Message tags as defined by [IRCv3.2](http://ircv3.net/specs/core/message-tags-3.2.html).
    /// These tags are used to add extended information to the given message, and are commonly used
    /// in IRCv3 extensions to the IRC protocol.
    pub tags: Option<Vec<Tag>>,
    /// The message prefix (or source) as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812).
    pub prefix: Option<String>,
    /// The IRC command, parsed according to the known specifications. The command itself and its
    /// arguments (including the special suffix argument) are captured in this component.
    pub command: Command,
}

impl Message {
    /// Creates a new message from the given components.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// let message = Message::new(
    ///     Some("nickname!username@hostname"), "JOIN", vec!["#channel"], None
    /// ).unwrap();
    /// # }
    /// ```
    pub fn new(
        prefix: Option<&str>,
        command: &str,
        args: Vec<&str>,
        suffix: Option<&str>,
    ) -> error::Result<Message> {
        Message::with_tags(None, prefix, command, args, suffix)
    }

    /// Creates a new IRCv3.2 message from the given components, including message tags. These tags
    /// are used to add extended information to the given message, and are commonly used in IRCv3
    /// extensions to the IRC protocol.
    pub fn with_tags(
        tags: Option<Vec<Tag>>,
        prefix: Option<&str>,
        command: &str,
        args: Vec<&str>,
        suffix: Option<&str>,
    ) -> error::Result<Message> {
        Ok(Message {
            tags: tags,
            prefix: prefix.map(|s| s.to_owned()),
            command: Command::new(command, args, suffix)?,
        })
    }

    /// Gets the nickname of the message source, if it exists.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// let message = Message::new(
    ///     Some("nickname!username@hostname"), "JOIN", vec!["#channel"], None
    /// ).unwrap();
    /// assert_eq!(message.source_nickname(), Some("nickname"));
    /// # }
    /// ```
    pub fn source_nickname(&self) -> Option<&str> {
        // <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
        // <servername> ::= <host>
        self.prefix.as_ref().and_then(|s| match (
            s.find('!'),
            s.find('@'),
            s.find('.'),
        ) {
            (Some(i), _, _) => Some(&s[..i]), // <nick> '!' <user> [ '@' <host> ]
            (None, Some(i), _) => Some(&s[..i]), // <nick> '@' <host>
            (None, None, None) => Some(s), // <nick>
            _ => None, // <servername>
        })
    }

    /// Gets the likely intended place to respond to this message.
    /// If the type of the message is a `PRIVMSG` or `NOTICE` and the message is sent to a channel,
    /// the result will be that channel. In all other cases, this will call `source_nickname`.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// let msg1 = Message::new(
    ///     Some("ada"), "PRIVMSG", vec!["#channel"], Some("Hi, everyone!")
    /// ).unwrap();
    /// assert_eq!(msg1.response_target(), Some("#channel"));
    /// let msg2 = Message::new(
    ///     Some("ada"), "PRIVMSG", vec!["betsy"], Some("betsy: hi")
    /// ).unwrap();
    /// assert_eq!(msg2.response_target(), Some("ada"));
    /// # }
    /// ```
    pub fn response_target(&self) -> Option<&str> {
        match self.command {
            Command::PRIVMSG(ref target, _) if target.is_channel_name() => Some(target),
            Command::NOTICE(ref target, _) if target.is_channel_name() => Some(target),
            _ => self.source_nickname()
        }
    }

    /// Converts a Message into a String according to the IRC protocol.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// let msg = Message::new(
    ///     Some("ada"), "PRIVMSG", vec!["#channel"], Some("Hi, everyone!")
    /// ).unwrap();
    /// assert_eq!(msg.to_string(), ":ada PRIVMSG #channel :Hi, everyone!\r\n");
    /// # }
    /// ```
    pub fn to_string(&self) -> String {
        let mut ret = String::new();
        if let Some(ref tags) = self.tags {
            ret.push('@');
            for tag in tags {
                ret.push_str(&tag.0);
                if let Some(ref value) = tag.1 {
                    ret.push('=');
                    ret.push_str(value);
                }
                ret.push(';');
            }
            ret.pop();
            ret.push(' ');
        }
        if let Some(ref prefix) = self.prefix {
            ret.push(':');
            ret.push_str(prefix);
            ret.push(' ');
        }
        let cmd: String = From::from(&self.command);
        ret.push_str(&cmd);
        ret.push_str("\r\n");
        ret
    }
}

impl From<Command> for Message {
    fn from(cmd: Command) -> Message {
        Message {
            tags: None,
            prefix: None,
            command: cmd,
        }
    }
}

impl FromStr for Message {
    type Err = Error;

    fn from_str(s: &str) -> Result<Message, Self::Err> {
        let mut state = s;
        if s.is_empty() {
            return Err(ErrorKind::ParseEmpty.into());
        }
        let tags = if state.starts_with('@') {
            let tags = state.find(' ').map(|i| &state[1..i]);
            state = state.find(' ').map_or("", |i| &state[i + 1..]);
            tags.map(|ts| {
                ts.split(';')
                    .filter(|s| !s.is_empty())
                    .map(|s: &str| {
                        let mut iter = s.splitn(2, '=');
                        let (fst, snd) = (iter.next(), iter.next());
                        Tag(fst.unwrap_or("").to_owned(), snd.map(|s| s.to_owned()))
                    })
                    .collect::<Vec<_>>()
            })
        } else {
            None
        };
        let prefix = if state.starts_with(':') {
            let prefix = state.find(' ').map(|i| &state[1..i]);
            state = state.find(' ').map_or("", |i| &state[i + 1..]);
            prefix
        } else {
            None
        };
        let line_ending_len = if state.ends_with("\r\n") {
            "\r\n"
        } else if state.ends_with('\r') {
            "\r"
        } else if state.ends_with('\n') {
            "\n"
        } else {
            ""
        }.len();
        let suffix = if state.contains(" :") {
            let suffix = state.find(" :").map(|i| &state[i + 2..state.len() - line_ending_len]);
            state = state.find(" :").map_or("", |i| &state[..i + 1]);
            suffix
        } else {
            state = &state[..state.len() - line_ending_len];
            None
        };
        let command = match state.find(' ').map(|i| &state[..i]) {
            Some(cmd) => {
                state = state.find(' ').map_or("", |i| &state[i + 1..]);
                cmd
            }
            _ => return Err(ErrorKind::InvalidCommand.into()),
        };
        let args: Vec<_> = state.splitn(14, ' ').filter(|s| !s.is_empty()).collect();
        Message::with_tags(tags, prefix, command, args, suffix)
            .map_err(|_| ErrorKind::InvalidCommand.into())
    }
}

impl<'a> From<&'a str> for Message {
    fn from(s: &'a str) -> Message {
        s.parse().unwrap()
    }
}

impl Display for Message {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", self.to_string())
    }
}

/// A message tag as defined by [IRCv3.2](http://ircv3.net/specs/core/message-tags-3.2.html).
/// It consists of a tag key, and an optional value for the tag. Each message can contain a number
/// of tags (in the string format, they are separated by semicolons). Tags are used to add extended
/// information to a message under IRCv3.
#[derive(Clone, PartialEq, Debug)]
pub struct Tag(pub String, pub Option<String>);

#[cfg(test)]
mod test {
    use super::{Message, Tag};
    use proto::Command::{PRIVMSG, Raw};

    #[test]
    fn new() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            Message::new(None, "PRIVMSG", vec!["test"], Some("Testing!")).unwrap(),
            message
        )
    }

    #[test]
    fn source_nickname() {
        assert_eq!(
            Message::new(None, "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            None
        );

        assert_eq!(
            Message::new(Some("irc.test.net"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            None
        );

        assert_eq!(
            Message::new(Some("test!test@test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test@test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test!test@irc.test.com"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test!test@127.0.0.1"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test@test.com"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );
    }

    #[test]
    fn to_string() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(&message.to_string()[..], "PRIVMSG test :Testing!\r\n");
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        assert_eq!(
            &message.to_string()[..],
            ":test!test@test PRIVMSG test :Still testing!\r\n"
        );
    }

    #[test]
    fn from_string() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            "PRIVMSG test :Testing!\r\n".parse::<Message>().unwrap(),
            message
        );
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        assert_eq!(
            ":test!test@test PRIVMSG test :Still testing!\r\n"
                .parse::<Message>()
                .unwrap(),
            message
        );
        let message = Message {
            tags: Some(vec![
                Tag(format!("aaa"), Some(format!("bbb"))),
                Tag(format!("ccc"), None),
                Tag(format!("example.com/ddd"), Some(format!("eee"))),
            ]),
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Testing with tags!")),
        };
        assert_eq!(
            "@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
                    tags!\r\n"
                .parse::<Message>()
                .unwrap(),
            message
        )
    }

    #[test]
    fn from_string_atypical_endings() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            "PRIVMSG test :Testing!\r".parse::<Message>().unwrap(),
            message
        );
        assert_eq!(
            "PRIVMSG test :Testing!\n".parse::<Message>().unwrap(),
            message
        );
        assert_eq!(
            "PRIVMSG test :Testing!".parse::<Message>().unwrap(),
            message
        );
    }

    #[test]
    fn from_and_to_string() {
        let message = "@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
                       tags!\r\n";
        assert_eq!(message.parse::<Message>().unwrap().to_string(), message);
    }

    #[test]
    fn to_message() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        let msg: Message = "PRIVMSG test :Testing!\r\n".into();
        assert_eq!(msg, message);
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        let msg: Message = ":test!test@test PRIVMSG test :Still testing!\r\n".into();
        assert_eq!(msg, message);
    }

    #[test]
    fn to_message_with_colon_in_arg() {
        // Apparently, UnrealIRCd (and perhaps some others) send some messages that include
        // colons within individual parameters. So, let's make sure it parses correctly.
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: Raw(
                format!("COMMAND"),
                vec![format!("ARG:test")],
                Some(format!("Testing!")),
            ),
        };
        let msg: Message = ":test!test@test COMMAND ARG:test :Testing!\r\n".into();
        assert_eq!(msg, message);
    }

    #[test]
    #[should_panic]
    fn to_message_invalid_format() {
        let _: Message = ":invalid :message".into();
    }
}
<commit_msg>Fixed parsing bug allowing messages without arguments to parse correctly.<commit_after>//! A module providing a data structure for messages to and from IRC servers.
use std::borrow::ToOwned;
use std::fmt::{Display, Formatter, Result as FmtResult};
use std::str::FromStr;

use error;
use error::{Error, ErrorKind};
use proto::{Command, ChannelExt};

/// A data structure representing an IRC message according to the protocol specification. It
/// consists of a collection of IRCv3 tags, a prefix (describing the source of the message), and
/// the protocol command. If the command is unknown, it is treated as a special raw command that
/// consists of a collection of arguments and the special suffix argument. Otherwise, the command
/// is parsed into a more useful form as described in [Command](../command/enum.Command.html).
#[derive(Clone, PartialEq, Debug)]
pub struct Message {
    /// Message tags as defined by [IRCv3.2](http://ircv3.net/specs/core/message-tags-3.2.html).
    /// These tags are used to add extended information to the given message, and are commonly used
    /// in IRCv3 extensions to the IRC protocol.
    pub tags: Option<Vec<Tag>>,
    /// The message prefix (or source) as defined by [RFC 2812](http://tools.ietf.org/html/rfc2812).
    pub prefix: Option<String>,
    /// The IRC command, parsed according to the known specifications. The command itself and its
    /// arguments (including the special suffix argument) are captured in this component.
    pub command: Command,
}

impl Message {
    /// Creates a new message from the given components.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// let message = Message::new(
    ///     Some("nickname!username@hostname"), "JOIN", vec!["#channel"], None
    /// ).unwrap();
    /// # }
    /// ```
    pub fn new(
        prefix: Option<&str>,
        command: &str,
        args: Vec<&str>,
        suffix: Option<&str>,
    ) -> error::Result<Message> {
        Message::with_tags(None, prefix, command, args, suffix)
    }

    /// Creates a new IRCv3.2 message from the given components, including message tags. These tags
    /// are used to add extended information to the given message, and are commonly used in IRCv3
    /// extensions to the IRC protocol.
    pub fn with_tags(
        tags: Option<Vec<Tag>>,
        prefix: Option<&str>,
        command: &str,
        args: Vec<&str>,
        suffix: Option<&str>,
    ) -> error::Result<Message> {
        Ok(Message {
            tags: tags,
            prefix: prefix.map(|s| s.to_owned()),
            command: Command::new(command, args, suffix)?,
        })
    }

    /// Gets the nickname of the message source, if it exists.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// let message = Message::new(
    ///     Some("nickname!username@hostname"), "JOIN", vec!["#channel"], None
    /// ).unwrap();
    /// assert_eq!(message.source_nickname(), Some("nickname"));
    /// # }
    /// ```
    pub fn source_nickname(&self) -> Option<&str> {
        // <prefix> ::= <servername> | <nick> [ '!' <user> ] [ '@' <host> ]
        // <servername> ::= <host>
        self.prefix.as_ref().and_then(|s| match (
            s.find('!'),
            s.find('@'),
            s.find('.'),
        ) {
            (Some(i), _, _) => Some(&s[..i]), // <nick> '!' <user> [ '@' <host> ]
            (None, Some(i), _) => Some(&s[..i]), // <nick> '@' <host>
            (None, None, None) => Some(s), // <nick>
            _ => None, // <servername>
        })
    }

    /// Gets the likely intended place to respond to this message.
    /// If the type of the message is a `PRIVMSG` or `NOTICE` and the message is sent to a channel,
    /// the result will be that channel. In all other cases, this will call `source_nickname`.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// let msg1 = Message::new(
    ///     Some("ada"), "PRIVMSG", vec!["#channel"], Some("Hi, everyone!")
    /// ).unwrap();
    /// assert_eq!(msg1.response_target(), Some("#channel"));
    /// let msg2 = Message::new(
    ///     Some("ada"), "PRIVMSG", vec!["betsy"], Some("betsy: hi")
    /// ).unwrap();
    /// assert_eq!(msg2.response_target(), Some("ada"));
    /// # }
    /// ```
    pub fn response_target(&self) -> Option<&str> {
        match self.command {
            Command::PRIVMSG(ref target, _) if target.is_channel_name() => Some(target),
            Command::NOTICE(ref target, _) if target.is_channel_name() => Some(target),
            _ => self.source_nickname()
        }
    }

    /// Converts a Message into a String according to the IRC protocol.
    ///
    /// # Example
    /// ```
    /// # extern crate irc;
    /// # use irc::client::prelude::*;
    /// # fn main() {
    /// let msg = Message::new(
    ///     Some("ada"), "PRIVMSG", vec!["#channel"], Some("Hi, everyone!")
    /// ).unwrap();
    /// assert_eq!(msg.to_string(), ":ada PRIVMSG #channel :Hi, everyone!\r\n");
    /// # }
    /// ```
    pub fn to_string(&self) -> String {
        let mut ret = String::new();
        if let Some(ref tags) = self.tags {
            ret.push('@');
            for tag in tags {
                ret.push_str(&tag.0);
                if let Some(ref value) = tag.1 {
                    ret.push('=');
                    ret.push_str(value);
                }
                ret.push(';');
            }
            ret.pop();
            ret.push(' ');
        }
        if let Some(ref prefix) = self.prefix {
            ret.push(':');
            ret.push_str(prefix);
            ret.push(' ');
        }
        let cmd: String = From::from(&self.command);
        ret.push_str(&cmd);
        ret.push_str("\r\n");
        ret
    }
}

impl From<Command> for Message {
    fn from(cmd: Command) -> Message {
        Message {
            tags: None,
            prefix: None,
            command: cmd,
        }
    }
}

impl FromStr for Message {
    type Err = Error;

    fn from_str(s: &str) -> Result<Message, Self::Err> {
        let mut state = s;
        if s.is_empty() {
            return Err(ErrorKind::ParseEmpty.into());
        }
        let tags = if state.starts_with('@') {
            let tags = state.find(' ').map(|i| &state[1..i]);
            state = state.find(' ').map_or("", |i| &state[i + 1..]);
            tags.map(|ts| {
                ts.split(';')
                    .filter(|s| !s.is_empty())
                    .map(|s: &str| {
                        let mut iter = s.splitn(2, '=');
                        let (fst, snd) = (iter.next(), iter.next());
                        Tag(fst.unwrap_or("").to_owned(), snd.map(|s| s.to_owned()))
                    })
                    .collect::<Vec<_>>()
            })
        } else {
            None
        };
        let prefix = if state.starts_with(':') {
            let prefix = state.find(' ').map(|i| &state[1..i]);
            state = state.find(' ').map_or("", |i| &state[i + 1..]);
            prefix
        } else {
            None
        };
        let line_ending_len = if state.ends_with("\r\n") {
            "\r\n"
        } else if state.ends_with('\r') {
            "\r"
        } else if state.ends_with('\n') {
            "\n"
        } else {
            ""
        }.len();
        let suffix = if state.contains(" :") {
            let suffix = state.find(" :").map(|i| &state[i + 2..state.len() - line_ending_len]);
            state = state.find(" :").map_or("", |i| &state[..i + 1]);
            suffix
        } else {
            state = &state[..state.len() - line_ending_len];
            None
        };
        let command = match state.find(' ').map(|i| &state[..i]) {
            Some(cmd) => {
                state = state.find(' ').map_or("", |i| &state[i + 1..]);
                cmd
            }
            // If there's no arguments but the "command" starts with colon, it's not a command.
            None if state.starts_with(":") => return Err(ErrorKind::InvalidCommand.into()),
            // If there's no arguments following the command, the rest of the state is the command.
            None => {
                let cmd = state;
                state = "";
                cmd
            },

        };
        let args: Vec<_> = state.splitn(14, ' ').filter(|s| !s.is_empty()).collect();
        Message::with_tags(tags, prefix, command, args, suffix)
            .map_err(|_| ErrorKind::InvalidCommand.into())
    }
}

impl<'a> From<&'a str> for Message {
    fn from(s: &'a str) -> Message {
        s.parse().unwrap()
    }
}

impl Display for Message {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}", self.to_string())
    }
}

/// A message tag as defined by [IRCv3.2](http://ircv3.net/specs/core/message-tags-3.2.html).
/// It consists of a tag key, and an optional value for the tag. Each message can contain a number
/// of tags (in the string format, they are separated by semicolons). Tags are used to add extended
/// information to a message under IRCv3.
#[derive(Clone, PartialEq, Debug)]
pub struct Tag(pub String, pub Option<String>);

#[cfg(test)]
mod test {
    use super::{Message, Tag};
    use proto::Command::{PRIVMSG, QUIT, Raw};

    #[test]
    fn new() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            Message::new(None, "PRIVMSG", vec!["test"], Some("Testing!")).unwrap(),
            message
        )
    }

    #[test]
    fn source_nickname() {
        assert_eq!(
            Message::new(None, "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            None
        );

        assert_eq!(
            Message::new(Some("irc.test.net"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            None
        );

        assert_eq!(
            Message::new(Some("test!test@test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test@test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test!test@irc.test.com"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test!test@127.0.0.1"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test@test.com"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );

        assert_eq!(
            Message::new(Some("test"), "PING", vec![], Some("data"))
                .unwrap()
                .source_nickname(),
            Some("test")
        );
    }

    #[test]
    fn to_string() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(&message.to_string()[..], "PRIVMSG test :Testing!\r\n");
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        assert_eq!(
            &message.to_string()[..],
            ":test!test@test PRIVMSG test :Still testing!\r\n"
        );
    }

    #[test]
    fn from_string() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            "PRIVMSG test :Testing!\r\n".parse::<Message>().unwrap(),
            message
        );
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        assert_eq!(
            ":test!test@test PRIVMSG test :Still testing!\r\n"
                .parse::<Message>()
                .unwrap(),
            message
        );
        let message = Message {
            tags: Some(vec![
                Tag(format!("aaa"), Some(format!("bbb"))),
                Tag(format!("ccc"), None),
                Tag(format!("example.com/ddd"), Some(format!("eee"))),
            ]),
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Testing with tags!")),
        };
        assert_eq!(
            "@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
                    tags!\r\n"
                .parse::<Message>()
                .unwrap(),
            message
        )
    }

    #[test]
    fn from_string_atypical_endings() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        assert_eq!(
            "PRIVMSG test :Testing!\r".parse::<Message>().unwrap(),
            message
        );
        assert_eq!(
            "PRIVMSG test :Testing!\n".parse::<Message>().unwrap(),
            message
        );
        assert_eq!(
            "PRIVMSG test :Testing!".parse::<Message>().unwrap(),
            message
        );
    }

    #[test]
    fn from_and_to_string() {
        let message = "@aaa=bbb;ccc;example.com/ddd=eee :test!test@test PRIVMSG test :Testing with \
                       tags!\r\n";
        assert_eq!(message.parse::<Message>().unwrap().to_string(), message);
    }

    #[test]
    fn to_message() {
        let message = Message {
            tags: None,
            prefix: None,
            command: PRIVMSG(format!("test"), format!("Testing!")),
        };
        let msg: Message = "PRIVMSG test :Testing!\r\n".into();
        assert_eq!(msg, message);
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: PRIVMSG(format!("test"), format!("Still testing!")),
        };
        let msg: Message = ":test!test@test PRIVMSG test :Still testing!\r\n".into();
        assert_eq!(msg, message);
    }

    #[test]
    fn to_message_with_colon_in_arg() {
        // Apparently, UnrealIRCd (and perhaps some others) send some messages that include
        // colons within individual parameters. So, let's make sure it parses correctly.
        let message = Message {
            tags: None,
            prefix: Some(format!("test!test@test")),
            command: Raw(
                format!("COMMAND"),
                vec![format!("ARG:test")],
                Some(format!("Testing!")),
            ),
        };
        let msg: Message = ":test!test@test COMMAND ARG:test :Testing!\r\n".into();
        assert_eq!(msg, message);
    }

    #[test]
    fn to_message_no_prefix_no_args() {
        let message = Message {
            tags: None,
            prefix: None,
            command: QUIT(None),
        };
        let msg: Message = "QUIT\r\n".into();
        assert_eq!(msg, message);
    }

    #[test]
    #[should_panic]
    fn to_message_invalid_format() {
        let _: Message = ":invalid :message".into();
    }
}
<|endoftext|>


<commit_before>use std::ops::{Index, IndexMut, Range};

pub trait Data {
    type Slice: ?Sized + Index<usize,Output=u8>;

    fn get(&self, index: usize) -> Option<&u8>;
    fn get_range(&self, index: Range<usize>) -> Option<&Self::Slice>;
}

pub trait MutData: Data {
    type SliceMut: ?Sized + IndexMut<usize,Output=u8>;

    fn get_mut(&mut self, index: usize) -> Option<&mut u8>;
    fn get_mut_range(&mut self, index: Range<usize>) -> Option<&mut Self::SliceMut>;
}

impl Data for [u8] {
    type Slice = [u8];
    #[inline] fn get(&self, index: usize) -> Option<&u8> { (self as &[u8]).get(index) }

    #[inline]
    fn get_range(&self, index: Range<usize>) -> Option<&Self::Slice> {
        if index.end > self.len() { return None; }
        Some(&self[index])
    }
}

impl MutData for [u8] {
    type SliceMut = [u8];

    #[inline] fn get_mut(&mut self, index: usize) -> Option<&mut u8> { (self as &mut[u8]).get_mut(index) }

    #[inline]
    fn get_mut_range(&mut self, index: Range<usize>) -> Option<&mut Self::SliceMut> {
        if index.end > self.len() { return None; }
        Some(&mut self[index])
    }
}

type U8Slice = [u8];

impl Data for Vec<u8> {
    type Slice = [u8];

    #[inline] fn get(&self, index: usize) -> Option<&u8> { U8Slice::get(self, index) }

    #[inline]
    fn get_range(&self, index: Range<usize>) -> Option<&Self::Slice> {
        if index.end > self.len() { return None; }
        Some(&self[index])
    }
}

/// Bit field manipulation. Does not yet work across byte boundaries.
pub struct BitField {
    /// Byte offset from start of data.
    pub index: usize,
    /// Bits to extract from the target byte.
    pub mask: u8,
}

impl BitField {
    #[inline]
    pub fn get<T: Data + ?Sized>(&self, data: &T) -> Option<u8> {
        if let Some(val) = data.get(self.index) {
            return Some((val & self.mask) >> self.mask.trailing_zeros());
        }
        None
    }

    #[inline]
    pub fn nonzero<T: Data + ?Sized>(&self, data: &T) -> Option<bool> {
        if let Some(val) = data.get(self.index) {
            return Some(0 != (val & self.mask));
        }
        None
    }

    #[inline]
    pub fn set<T: MutData + ?Sized>(&self, data: &mut T, value: u8) {
        if let Some(elem) = data.get_mut(self.index) {
            *elem = (*elem & !self.mask) | ((value as u8 & self.mask) << self.mask.trailing_zeros());
        }
    }
}

pub struct BEU16Field {
    pub index: usize
}

impl BEU16Field {
    #[inline]
    pub fn get<T: Data + ?Sized>(&self, data: &T) -> Option<u16> {
        if let Some(split) = data.get_range(self.index..self.index+2) {
            return Some((split[0] as u16) << 8 + (split[1] as u16));
        }
        None
    }

    #[inline]
    pub fn set<T: MutData + ?Sized>(&self, data: &mut T, value: u16) {
        if let Some(split) = data.get_mut_range(self.index..self.index+2) {
            split[0] = ((value & 0xff00) >> 8) as u8;
            split[1] = (value & 0x00ff) as u8;
        }
    }
}

pub struct BEU32Field {
    pub index: usize
}

impl BEU32Field {
    #[inline]
    pub fn get<T: Data + ?Sized>(&self, data: &T) -> Option<u32> {
        if let Some(split) = data.get_range(self.index..self.index+4) {
            return Some(((split[0] as u32) << 24) +
                        ((split[1] as u32) << 16) +
                        ((split[2] as u32) << 8) +
                        ((split[3] as u32) << 0))
        }
        None
    }

    #[inline]
    pub fn set<T: MutData + ?Sized>(&self, data: &mut T, value: u32) {
        // TODO unsafe impl, once I can tell what the native endianness is.
        if let Some(split) = data.get_mut_range(self.index..self.index+4) {
            split[0] = ((value & 0xff000000) >> 24) as u8;
            split[1] = ((value & 0x00ff0000) >> 16) as u8;
            split[2] = ((value & 0x0000ff00) >> 8) as u8;
            split[3] = ((value & 0x000000ff) >> 0) as u8;
        }
    }
}

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

    #[test]
    fn bits0() {
        let data = [0xab, 0xcd];
        let view:&[u8] = &data[..];
        let field = BitField{index:0, mask:0xf0};
        assert_eq!(Some(0xa), field.get(view));
    }
    #[test]
    fn bits1() {
        let data = [0xab, 0xcd];
        let view:&[u8] = &data[..];
        let field = BitField{index:1, mask:0xf0};
        assert_eq!(Some(0xc), field.get(view));
    }
    #[test]
    fn bits1() {
        let data = [0xab, 0xcd];
        let view:&[u8] = &data[..];
        let field = BitField{index:1, mask:0xf0};
        assert_eq!(Some(0xc), field.get(view));
    }
}
<commit_msg>Bit read tests.<commit_after>use std::ops::{Index, IndexMut, Range};

pub trait Data {
    type Slice: ?Sized + Index<usize,Output=u8>;

    fn get(&self, index: usize) -> Option<&u8>;
    fn get_range(&self, index: Range<usize>) -> Option<&Self::Slice>;
}

pub trait MutData: Data {
    type SliceMut: ?Sized + IndexMut<usize,Output=u8>;

    fn get_mut(&mut self, index: usize) -> Option<&mut u8>;
    fn get_mut_range(&mut self, index: Range<usize>) -> Option<&mut Self::SliceMut>;
}

impl Data for [u8] {
    type Slice = [u8];
    #[inline]
    fn get(&self, index: usize) -> Option<&u8> {
        (self as &[u8]).get(index)
    }

    #[inline]
    fn get_range(&self, index: Range<usize>) -> Option<&Self::Slice> {
        if index.end > self.len() {
            return None;
        }
        Some(&self[index])
    }
}

impl MutData for [u8] {
    type SliceMut = [u8];

    #[inline]
    fn get_mut(&mut self, index: usize) -> Option<&mut u8> {
        (self as &mut [u8]).get_mut(index)
    }

    #[inline]
    fn get_mut_range(&mut self, index: Range<usize>) -> Option<&mut Self::SliceMut> {
        if index.end > self.len() {
            return None;
        }
        Some(&mut self[index])
    }
}

type U8Slice = [u8];

impl Data for Vec<u8> {
    type Slice = [u8];

    #[inline]
    fn get(&self, index: usize) -> Option<&u8> {
        U8Slice::get(self, index)
    }

    #[inline]
    fn get_range(&self, index: Range<usize>) -> Option<&Self::Slice> {
        if index.end > self.len() {
            return None;
        }
        Some(&self[index])
    }
}

/// Bit field manipulation. Does not yet work across byte boundaries.
pub struct BitField {
    /// Byte offset from start of data.
    pub index: usize,
    /// Bits to extract from the target byte.
    pub mask: u8,
}

impl BitField {
    #[inline]
    pub fn get<T: Data + ?Sized>(&self, data: &T) -> Option<u8> {
        if let Some(val) = data.get(self.index) {
            return Some((val & self.mask) >> self.mask.trailing_zeros());
        }
        None
    }

    #[inline]
    pub fn nonzero<T: Data + ?Sized>(&self, data: &T) -> Option<bool> {
        if let Some(val) = data.get(self.index) {
            return Some(0 != (val & self.mask));
        }
        None
    }

    #[inline]
    pub fn set<T: MutData + ?Sized>(&self, data: &mut T, value: u8) {
        if let Some(elem) = data.get_mut(self.index) {
            *elem = (*elem & !self.mask) |
                    ((value as u8 & self.mask) << self.mask.trailing_zeros());
        }
    }
}

pub struct BEU16Field {
    pub index: usize,
}

impl BEU16Field {
    #[inline]
    pub fn get<T: Data + ?Sized>(&self, data: &T) -> Option<u16> {
        if let Some(split) = data.get_range(self.index..self.index + 2) {
            return Some(((split[0] as u16) << 8) + (split[1] as u16));
        }
        None
    }

    #[inline]
    pub fn set<T: MutData + ?Sized>(&self, data: &mut T, value: u16) {
        if let Some(split) = data.get_mut_range(self.index..self.index + 2) {
            split[0] = ((value & 0xff00) >> 8) as u8;
            split[1] = (value & 0x00ff) as u8;
        }
    }
}

pub struct BEU32Field {
    pub index: usize,
}

impl BEU32Field {
    #[inline]
    pub fn get<T: Data + ?Sized>(&self, data: &T) -> Option<u32> {
        if let Some(split) = data.get_range(self.index..self.index + 4) {
            return Some(((split[0] as u32) << 24) + ((split[1] as u32) << 16) +
                        ((split[2] as u32) << 8) + ((split[3] as u32) << 0));
        }
        None
    }

    #[inline]
    pub fn set<T: MutData + ?Sized>(&self, data: &mut T, value: u32) {
        // TODO unsafe impl, once I can tell what the native endianness is.
        if let Some(split) = data.get_mut_range(self.index..self.index + 4) {
            split[0] = ((value & 0xff000000) >> 24) as u8;
            split[1] = ((value & 0x00ff0000) >> 16) as u8;
            split[2] = ((value & 0x0000ff00) >> 8) as u8;
            split[3] = ((value & 0x000000ff) >> 0) as u8;
        }
    }
}

#[cfg(test)]
mod tests {
    use super::BitField;
    use super::BEU16Field;
    use super::BEU32Field;

    #[test]
    fn u8_extract0() {
        let data = [0xab, 0xcd];
        let view: &[u8] = &data[..];
        let field = BitField {
            index: 0,
            mask: 0xff,
        };
        assert_eq!(Some(0xab), field.get(view));
    }

    #[test]
    fn u8_extract1() {
        let data = [0xab, 0xcd];
        let view: &[u8] = &data[..];
        let field = BitField {
            index: 1,
            mask: 0xff,
        };
        assert_eq!(Some(0xcd), field.get(view));
    }

    #[test]
    fn u4_extract() {
        let data = [0xab, 0xcd];
        let view: &[u8] = &data[..];
        let field = BitField {
            index: 1,
            mask: 0xf0,
        };
        assert_eq!(Some(0xc), field.get(view));
    }

    #[test]
    fn u1_extract() {
        let data = [0xab, 0xcd];
        let view: &[u8] = &data[..];
        let field = BitField {
            index: 1,
            mask: 0x80,
        };
        assert_eq!(Some(0x1), field.get(view));
    }

    #[test]
    fn bitfield_invalid_adddress() {
        let data = [0xab];
        let view: &[u8] = &data[..];
        let field = BitField {
            index: 1,
            mask: 0xf0,
        };
        assert_eq!(None, field.get(view));
    }

    #[test]
    fn u16_extract() {
        let data = [0xab, 0xcd];
        let view: &[u8] = &data[..];
        let field = BEU16Field {
            index: 0,
        };
        assert_eq!(Some(0xabcd), field.get(view));
    }

    #[test]
    fn u16_unaligned() {
        let data = [0xab, 0xcd, 0xef];
        let view: &[u8] = &data[..];
        let field = BEU16Field {
            index: 1,
        };
        assert_eq!(Some(0xcdef), field.get(view));
    }

    #[test]
    fn u16_invalid_address() {
        let data = [0xab, 0xcd];
        let view: &[u8] = &data[..];
        let field = BEU16Field {
            index: 1,
        };
        assert_eq!(None, field.get(view));
    }

    #[test]
    fn u32_extract() {
        let data = [0xab, 0xcd, 0xef, 0x01];
        let view: &[u8] = &data[..];
        let field = BEU32Field {
            index: 0,
        };
        assert_eq!(Some(0xabcdef01), field.get(view));
    }

    #[test]
    fn u32_unaligned() {
        let data = [0x00, 0xab, 0xcd, 0xef, 0x01];
        let view: &[u8] = &data[..];
        let field = BEU32Field {
            index: 1,
        };
        assert_eq!(Some(0xabcdef01), field.get(view));
    }

    #[test]
    fn u32_invalid_address() {
        let data = [0xab, 0xcd];
        let view: &[u8] = &data[..];
        let field = BEU16Field {
            index: 1,
        };
        assert_eq!(None, field.get(view));
    }
}
<|endoftext|>


<commit_before>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use anyhow::{format_err, Result};
use reqwest::Url;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct CommitInfo {
    pub sha: String,
    pub commit: GitCommitInfo,
}

#[derive(Debug, Deserialize)]
pub struct GitCommitInfo {
    pub author: Author,
    pub message: String,
}

#[derive(Debug, Deserialize)]
pub struct Author {
    pub name: String,
    pub email: String,
}

pub struct GitHub {
    client: reqwest::blocking::Client,
}

impl GitHub {
    pub fn new() -> GitHub {
        let client = reqwest::blocking::Client::new();
        GitHub { client }
    }

    /// repo in format owner/repo_name
    /// sha can be long or short hash, or branch name
    /// Paging is not implemented yet
    pub fn get_commits(&self, repo: &str, sha: &str) -> Result<Vec<CommitInfo>> {
        let url = format!("https://api.github.com/repos/{}/commits?sha={}", repo, sha);
        let url: Url = url.parse().expect("Failed to parse github url");
        let request = self.client.get(url);
        let response = request
            .send()
            .map_err(|e| format_err!("Failed to query github: {:?}", e))?;
        let response: Vec<CommitInfo> = response
            .json()
            .map_err(|e| format_err!("Failed to parse github response: {:?}", e))?;
        Ok(response)
    }
}
<commit_msg>[cluster-test] Fix changelog generation<commit_after>// Copyright (c) The Libra Core Contributors
// SPDX-License-Identifier: Apache-2.0

#![forbid(unsafe_code)]

use anyhow::{format_err, Result};
use reqwest::header::USER_AGENT;
use reqwest::Url;
use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct CommitInfo {
    pub sha: String,
    pub commit: GitCommitInfo,
}

#[derive(Debug, Deserialize)]
pub struct GitCommitInfo {
    pub author: Author,
    pub message: String,
}

#[derive(Debug, Deserialize)]
pub struct Author {
    pub name: String,
    pub email: String,
}

pub struct GitHub {
    client: reqwest::blocking::Client,
}

impl GitHub {
    pub fn new() -> GitHub {
        let client = reqwest::blocking::Client::new();
        GitHub { client }
    }

    /// repo in format owner/repo_name
    /// sha can be long or short hash, or branch name
    /// Paging is not implemented yet
    pub fn get_commits(&self, repo: &str, sha: &str) -> Result<Vec<CommitInfo>> {
        let url = format!("https://api.github.com/repos/{}/commits?sha={}", repo, sha);
        let url: Url = url.parse().expect("Failed to parse github url");
        let request = self.client.get(url);
        let response = request
            .header(USER_AGENT, "libra-cluster-test")
            .send()
            .map_err(|e| format_err!("Failed to query github: {:?}", e))?;
        let response: Vec<CommitInfo> = response
            .json()
            .map_err(|e| format_err!("Failed to parse github response: {:?}", e))?;
        Ok(response)
    }
}
<|endoftext|>