<commit_before>extern crate serial;
#[macro_use] extern crate serde_derive;
extern crate serde;

use serde::ser::{Serializer, Serialize};
use serde::de::{Deserializer, Deserialize};
use serial::Error as SerialError;
use std::fmt;
use std::error::Error as StdError;

mod enttec;

pub use enttec::{EnttecDmxPort, ENTTEC_NAMESPACE, EnttecPortProvider};

/// Trait for the general notion of a DMX port.
/// This enables creation of an "offline" port to slot into place if an API requires an output.
pub trait DmxPort: fmt::Debug {
    /// Write a DMX frame out to the port.  If the frame is smaller than the minimum universe size,
    /// it will be padded with zeros.  If the frame is larger than the maximum universe size, the
    /// values beyond the max size will be ignored.
    fn write(&mut self, frame: &[u8]) -> Result<(), Error>;

    /// Return the name of this port.  Should only be used for display purposes.
    fn port_name(&self) -> &str;

    /// Return a SerializablePort to be used to try to reopen a port
    /// after deserialization of a saved show or after application restart.
    fn serializable(&self) -> SerializablePort;
}

/// A source of DmxPorts based on unique string identifiers.
pub trait DmxPortProvider {
    /// Return a unique namespace for this port provider.
    fn namespace(&self) -> &str;
    /// Return a description of the available ports provided by this provider.
    fn available_ports(&self) -> Vec<String>;
    /// Attempt to open this port, and return it behind the trait object or an error.
    fn open<N: Into<String>>(&self, port: N) -> Result<Box<DmxPort>, Error>;
}

pub struct OfflineDmxPort;

impl fmt::Debug for OfflineDmxPort {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.serializable().fmt(f)
    }
}

const OFFLINE_NAMESPACE: &'static str = "offline";
const OFFLINE_ID: &'static str = "offline";

impl DmxPort for OfflineDmxPort {
    fn write(&mut self, _: &[u8]) -> Result<(), Error> {
        Ok(())
    }
    fn port_name(&self) -> &str {
        OFFLINE_ID
    }
    fn serializable(&self) -> SerializablePort {
        SerializablePort::new(OFFLINE_NAMESPACE, OFFLINE_ID)
    }
}

pub struct OfflinePortProvider;

impl DmxPortProvider for OfflinePortProvider {
    /// Return a unique namespace for this port provider.
    fn namespace(&self) -> &str {
        OFFLINE_NAMESPACE
    }
    /// Return a description of the available ports provided by this provider.
    fn available_ports(&self) -> Vec<String> {
        vec!(OFFLINE_ID.to_string())
    }
    /// Attempt to open this port, and return it behind the trait object or an error.
    fn open<N: Into<String>>(&self, _: N) -> Result<Box<DmxPort>, Error> {
        Ok(Box::new(OfflineDmxPort))
    }
}

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
/// A serializable data structure for persisting a record of a port to disk, also providing
/// for attempted reopening of a port.
pub struct SerializablePort<'a> {
    namespace: &'a str,
    id: &'a str,
}

impl<'a> SerializablePort<'a> {
    fn new(namespace: &'a str, id: &'a str) -> Self {
        SerializablePort { namespace: namespace, id: id }
    }

    /// Try to open the port described by this serialized form.
    fn open(&self) -> Result<Box<DmxPort>, Error> {
        match self.namespace {
            OFFLINE_NAMESPACE => Ok(Box::new(OfflineDmxPort{})),
            ENTTEC_NAMESPACE => Ok(Box::new(EnttecDmxPort::new(self.id)?)),
            _ => Err(Error::InvalidNamespace(self.namespace.to_string())),
        }
    }

    /// Based on the namespace and id, try to reopen this DMX port.
    /// If we don't know the namespace or the port isn't available, return an offline port.
    fn reopen(self) -> Box<DmxPort> {
        self.open().unwrap_or(Box::new(OfflineDmxPort{}))
    }
}

// Helper functions to use when serializing and deserializing DmxPort trait objects contained in
// other structs.  This can be done using the serde with attribute.
pub fn serialize<S>(port: &Box<DmxPort>, serializer: S) -> Result<S::Ok, S::Error>
    where S: Serializer
{
    port.serializable().serialize(serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Box<DmxPort>, D::Error>
    where D: Deserializer<'de>
{
    SerializablePort::deserialize(deserializer).map(SerializablePort::reopen)
}

#[derive(Debug)]
pub enum Error {
    Serial(SerialError),
    IO(std::io::Error),
    InvalidNamespace(String),
}

/// We're ok with a loose equality comparison here.  Just delegate to description for now.
impl PartialEq for Error {
    fn eq(&self, other: &Self) -> bool {
        use Error::*;
        match (self, other) {
            (&Serial(ref e0), &Serial(ref e1)) => e0.description() == e1.description(),
            (&IO(ref e0), &IO(ref e1)) => e0.description() == e1.description(),
            (&InvalidNamespace(ref n0), &InvalidNamespace(ref n1)) => n0 == n1,
            _ => false,
        }
    }
}

impl Eq for Error {}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Error::*;
        match *self {
            Serial(ref e) => e.fmt(f),
            IO(ref e) => e.fmt(f),
            InvalidNamespace(ref n) => write!(f, "Invalid DMX port namesapce: '{}'.", n),
        }
    }
}

impl From<SerialError> for Error {
    fn from(e: SerialError) -> Self {
        Error::Serial(e)
    }
}

impl From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Self {
        Error::IO(e)
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        use Error::*;
        match *self {
            Serial(ref e) => e.description(),
            IO(ref e) => e.description(),
            InvalidNamespace(_) => "Invalid DMX port namespace.",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use Error::*;
        match *self {
            Serial(ref e) => Some(e),
            IO(ref e) => Some(e),
            InvalidNamespace(_) => None,
        }
    }
}<commit_msg>ENH: add open_port function<commit_after>extern crate serial;
#[macro_use] extern crate serde_derive;
extern crate serde;

use serde::ser::{Serializer, Serialize};
use serde::de::{Deserializer, Deserialize};
use serial::Error as SerialError;
use std::fmt;
use std::error::Error as StdError;

mod enttec;

pub use enttec::{EnttecDmxPort, ENTTEC_NAMESPACE, EnttecPortProvider};

/// Trait for the general notion of a DMX port.
/// This enables creation of an "offline" port to slot into place if an API requires an output.
pub trait DmxPort: fmt::Debug {
    /// Write a DMX frame out to the port.  If the frame is smaller than the minimum universe size,
    /// it will be padded with zeros.  If the frame is larger than the maximum universe size, the
    /// values beyond the max size will be ignored.
    fn write(&mut self, frame: &[u8]) -> Result<(), Error>;

    /// Return the name of this port.  Should only be used for display purposes.
    fn port_name(&self) -> &str;

    /// Return a SerializablePort to be used to try to reopen a port
    /// after deserialization of a saved show or after application restart.
    fn serializable(&self) -> SerializablePort;
}

/// A source of DmxPorts based on unique string identifiers.
pub trait DmxPortProvider {
    /// Return a unique namespace for this port provider.
    fn namespace(&self) -> &str;
    /// Return a description of the available ports provided by this provider.
    fn available_ports(&self) -> Vec<String>;
    /// Attempt to open this port, and return it behind the trait object or an error.
    fn open<N: Into<String>>(&self, port: N) -> Result<Box<DmxPort>, Error>;
}

pub struct OfflineDmxPort;

impl fmt::Debug for OfflineDmxPort {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.serializable().fmt(f)
    }
}

const OFFLINE_NAMESPACE: &'static str = "offline";
const OFFLINE_ID: &'static str = "offline";

impl DmxPort for OfflineDmxPort {
    fn write(&mut self, _: &[u8]) -> Result<(), Error> {
        Ok(())
    }
    fn port_name(&self) -> &str {
        OFFLINE_ID
    }
    fn serializable(&self) -> SerializablePort {
        SerializablePort::new(OFFLINE_NAMESPACE, OFFLINE_ID)
    }
}

pub struct OfflinePortProvider;

impl DmxPortProvider for OfflinePortProvider {
    /// Return a unique namespace for this port provider.
    fn namespace(&self) -> &str {
        OFFLINE_NAMESPACE
    }
    /// Return a description of the available ports provided by this provider.
    fn available_ports(&self) -> Vec<String> {
        vec!(OFFLINE_ID.to_string())
    }
    /// Attempt to open this port, and return it behind the trait object or an error.
    fn open<N: Into<String>>(&self, _: N) -> Result<Box<DmxPort>, Error> {
        Ok(Box::new(OfflineDmxPort))
    }
}

/// Gather up all of the providers behind their namespace.
/// This is your one-stop-shop for port creation.
pub fn open_port<N: Into<String>>(namespace: &str, port_name: N) -> Result<Box<DmxPort>, Error> {
    match namespace {
        OFFLINE_NAMESPACE => OfflinePortProvider.open(port_name),
        ENTTEC_NAMESPACE => EnttecPortProvider.open(port_name),
        _ => return Err(Error::InvalidNamespace(namespace.to_string())),
    }
}

#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
/// A serializable data structure for persisting a record of a port to disk, also providing
/// for attempted reopening of a port.
pub struct SerializablePort<'a> {
    namespace: &'a str,
    id: &'a str,
}

impl<'a> SerializablePort<'a> {
    fn new(namespace: &'a str, id: &'a str) -> Self {
        SerializablePort { namespace: namespace, id: id }
    }

    /// Try to open the port described by this serialized form.
    fn open(&self) -> Result<Box<DmxPort>, Error> {
        match self.namespace {
            OFFLINE_NAMESPACE => Ok(Box::new(OfflineDmxPort{})),
            ENTTEC_NAMESPACE => Ok(Box::new(EnttecDmxPort::new(self.id)?)),
            _ => Err(Error::InvalidNamespace(self.namespace.to_string())),
        }
    }

    /// Based on the namespace and id, try to reopen this DMX port.
    /// If we don't know the namespace or the port isn't available, return an offline port.
    fn reopen(self) -> Box<DmxPort> {
        self.open().unwrap_or(Box::new(OfflineDmxPort{}))
    }
}

// Helper functions to use when serializing and deserializing DmxPort trait objects contained in
// other structs.  This can be done using the serde with attribute.
pub fn serialize<S>(port: &Box<DmxPort>, serializer: S) -> Result<S::Ok, S::Error>
    where S: Serializer
{
    port.serializable().serialize(serializer)
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Box<DmxPort>, D::Error>
    where D: Deserializer<'de>
{
    SerializablePort::deserialize(deserializer).map(SerializablePort::reopen)
}

#[derive(Debug)]
pub enum Error {
    Serial(SerialError),
    IO(std::io::Error),
    InvalidNamespace(String),
}

/// We're ok with a loose equality comparison here.  Just delegate to description for now.
impl PartialEq for Error {
    fn eq(&self, other: &Self) -> bool {
        use Error::*;
        match (self, other) {
            (&Serial(ref e0), &Serial(ref e1)) => e0.description() == e1.description(),
            (&IO(ref e0), &IO(ref e1)) => e0.description() == e1.description(),
            (&InvalidNamespace(ref n0), &InvalidNamespace(ref n1)) => n0 == n1,
            _ => false,
        }
    }
}

impl Eq for Error {}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use Error::*;
        match *self {
            Serial(ref e) => e.fmt(f),
            IO(ref e) => e.fmt(f),
            InvalidNamespace(ref n) => write!(f, "Invalid DMX port namesapce: '{}'.", n),
        }
    }
}

impl From<SerialError> for Error {
    fn from(e: SerialError) -> Self {
        Error::Serial(e)
    }
}

impl From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Self {
        Error::IO(e)
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        use Error::*;
        match *self {
            Serial(ref e) => e.description(),
            IO(ref e) => e.description(),
            InvalidNamespace(_) => "Invalid DMX port namespace.",
        }
    }

    fn cause(&self) -> Option<&StdError> {
        use Error::*;
        match *self {
            Serial(ref e) => Some(e),
            IO(ref e) => Some(e),
            InvalidNamespace(_) => None,
        }
    }
}<|endoftext|>


<commit_before>//! **mprovision** is a tool that helps `iOS` developers to manage mobileprovision
//! files. Main purpose of this crate is to contain functions and types
//! for **mprovision**.

use futures::stream::Stream;
use futures::Future;
use futures_cpupool::CpuPool;

use std::fs::{self, DirEntry, File};
use std::io::Read;
use std::path::{Path, PathBuf};

pub use crate::error::Error;
pub use crate::profile::Profile;

mod error;
mod plist_extractor;
mod profile;

/// A Result type for this crate.
pub type Result<T> = std::result::Result<T, Error>;

/// Returns an iterator over the `*.mobileprovision` entries within a given
/// directory.
///
/// # Errors
/// This function will return an error in the following cases:
///
/// - the user lacks the requisite permissions
/// - there is no entry in the filesystem at the provided path
/// - the provided path is not a directory
pub fn entries(dir: &Path) -> Result<Box<Iterator<Item = DirEntry>>> {
    let entries = fs::read_dir(dir)?;
    let filtered = entries
        .filter(|entry| {
            entry.as_ref().ok().and_then(|v| {
                v.path()
                    .extension()
                    .map(|ext| ext.to_str() == Some("mobileprovision"))
            }) == Some(true)
        })
        .filter_map(|entry| entry.ok());
    Ok(Box::new(filtered))
}

/// Returns the path to the directory that contains installed mobile
/// provisioning profiles.
///
/// Should return `~/Library/MobileDevice/Provisioning Profiles` directory.
///
/// # Errors
/// This function will return an error if 'HOME' environment variable is not set
/// or equal to the empty string.
pub fn directory() -> Result<PathBuf> {
    dirs::home_dir()
        .map(|path| path.join("Library/MobileDevice/Provisioning Profiles"))
        .ok_or_else(|| {
            Error::Own(
                "'HOME' environment variable is not set or equal to the empty string.".to_owned(),
            )
        })
}

/// Returns `dir` if it is not `None` otherwise returns `directory()`.
pub fn with_directory(dir: Option<PathBuf>) -> Result<PathBuf> {
    if let Some(d) = dir {
        Ok(d)
    } else {
        directory()
    }
}

/// Removes a provisioning profile.
pub fn remove(file_path: &Path) -> Result<()> {
    validate_path(file_path)
        .and_then(|file_path| std::fs::remove_file(file_path).map_err(|err| err.into()))
}

/// Returns internals of a provisioning profile.
pub fn show(file_path: &Path) -> Result<String> {
    validate_path(file_path).and_then(|file_path| {
        let mut buf = Vec::new();
        File::open(file_path)
            .and_then(|mut file| file.read_to_end(&mut buf))
            .map_err(|err| err.into())
            .and_then(|_| {
                plist_extractor::find(&buf)
                    .ok_or_else(|| Error::Own(format!("Couldn't parse '{}'", file_path.display())))
            })
            .and_then(|data| String::from_utf8(data.to_owned()).map_err(|err| err.into()))
    })
}

/// Validates that `file_path` has a `mobileprovision` extension.
fn validate_path(file_path: &Path) -> Result<&Path> {
    match file_path.extension() {
        Some(extension) if extension == "mobileprovision" => Ok(file_path),
        _ => Err(Error::Own(format!(
            "'{}' doesn't have 'mobileprovision' extension",
            file_path.display()
        ))),
    }
}

/// Filters entries of a directory using `f`.
pub fn filter<F>(entries: Vec<DirEntry>, f: F) -> Vec<Profile>
where
    F: Fn(&Profile) -> bool + Sync,
{
    let cpu_pool = CpuPool::new(num_cpus::get());

    let stream = futures::stream::iter_ok(entries.into_iter())
        .map(|entry| cpu_pool.spawn_fn(move || Profile::from_file(&entry.path())))
        .buffered(num_cpus::get() * 2)
        .filter(f)
        .collect();

    stream.wait().unwrap_or_default()
}

/// Searches a provisioning profile by its uuid.
pub fn find_by_uuid(dir: &Path, uuid: &str) -> Result<Profile> {
    let entries: Vec<DirEntry> = entries(dir)?.collect();
    if let Some(profile) = filter(entries, |profile| profile.info.uuid == uuid).pop() {
        Ok(profile)
    } else {
        Err(Error::Own(format!("Profile '{}' is not found.", uuid)))
    }
}

/// Searches provisioning profiles by their uuid(s) or bundle id(s).
pub fn find_by_ids(dir: &Path, ids: &[String]) -> Result<Vec<Profile>> {
    let entries: Vec<DirEntry> = entries(dir)?.collect();
    let profiles = filter(entries, |profile| {
        ids.iter()
            .any(|id| id == &profile.info.uuid || profile.info.bundle_id() == Some(&id))
    });
    Ok(profiles)
}

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

    #[test]
    fn filter_mobileprovision_files() {
        use std::fs::File;
        use tempdir::TempDir;

        let temp_dir = TempDir::new("test").unwrap();
        let result = entries(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(0));

        File::create(temp_dir.path().join("1.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("2.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("3.txt")).unwrap();
        let result = entries(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(2));
    }
}
<commit_msg>Use impl Iterator instead of boxing for the entries function<commit_after>//! **mprovision** is a tool that helps `iOS` developers to manage mobileprovision
//! files. Main purpose of this crate is to contain functions and types
//! for **mprovision**.

use futures::stream::Stream;
use futures::Future;
use futures_cpupool::CpuPool;

use std::fs::{self, DirEntry, File};
use std::io::Read;
use std::path::{Path, PathBuf};

pub use crate::error::Error;
pub use crate::profile::Profile;

mod error;
mod plist_extractor;
mod profile;

/// A Result type for this crate.
pub type Result<T> = std::result::Result<T, Error>;

/// Returns an iterator over the `*.mobileprovision` entries within a given
/// directory.
///
/// # Errors
/// This function will return an error in the following cases:
///
/// - the user lacks the requisite permissions
/// - there is no entry in the filesystem at the provided path
/// - the provided path is not a directory
pub fn entries(dir: &Path) -> Result<impl Iterator<Item = DirEntry>> {
    let entries = fs::read_dir(dir)?;
    let filtered = entries
        .filter(|entry| {
            entry.as_ref().ok().and_then(|v| {
                v.path()
                    .extension()
                    .map(|ext| ext.to_str() == Some("mobileprovision"))
            }) == Some(true)
        })
        .filter_map(|entry| entry.ok());
    Ok(filtered)
}

/// Returns the path to the directory that contains installed mobile
/// provisioning profiles.
///
/// Should return `~/Library/MobileDevice/Provisioning Profiles` directory.
///
/// # Errors
/// This function will return an error if 'HOME' environment variable is not set
/// or equal to the empty string.
pub fn directory() -> Result<PathBuf> {
    dirs::home_dir()
        .map(|path| path.join("Library/MobileDevice/Provisioning Profiles"))
        .ok_or_else(|| {
            Error::Own(
                "'HOME' environment variable is not set or equal to the empty string.".to_owned(),
            )
        })
}

/// Returns `dir` if it is not `None` otherwise returns `directory()`.
pub fn with_directory(dir: Option<PathBuf>) -> Result<PathBuf> {
    if let Some(d) = dir {
        Ok(d)
    } else {
        directory()
    }
}

/// Removes a provisioning profile.
pub fn remove(file_path: &Path) -> Result<()> {
    validate_path(file_path)
        .and_then(|file_path| std::fs::remove_file(file_path).map_err(|err| err.into()))
}

/// Returns internals of a provisioning profile.
pub fn show(file_path: &Path) -> Result<String> {
    validate_path(file_path).and_then(|file_path| {
        let mut buf = Vec::new();
        File::open(file_path)
            .and_then(|mut file| file.read_to_end(&mut buf))
            .map_err(|err| err.into())
            .and_then(|_| {
                plist_extractor::find(&buf)
                    .ok_or_else(|| Error::Own(format!("Couldn't parse '{}'", file_path.display())))
            })
            .and_then(|data| String::from_utf8(data.to_owned()).map_err(|err| err.into()))
    })
}

/// Validates that `file_path` has a `mobileprovision` extension.
fn validate_path(file_path: &Path) -> Result<&Path> {
    match file_path.extension() {
        Some(extension) if extension == "mobileprovision" => Ok(file_path),
        _ => Err(Error::Own(format!(
            "'{}' doesn't have 'mobileprovision' extension",
            file_path.display()
        ))),
    }
}

/// Filters entries of a directory using `f`.
///
/// The filtering is perfomed concurrently.
pub fn filter<F>(entries: Vec<DirEntry>, f: F) -> Vec<Profile>
where
    F: Fn(&Profile) -> bool + Sync,
{
    let cpu_pool = CpuPool::new(num_cpus::get());

    let stream = futures::stream::iter_ok(entries.into_iter())
        .map(|entry| cpu_pool.spawn_fn(move || Profile::from_file(&entry.path())))
        .buffered(num_cpus::get() * 2)
        .filter(f)
        .collect();

    stream.wait().unwrap_or_default()
}

/// Searches a provisioning profile by its uuid.
///
/// The search is performed concurrently.
pub fn find_by_uuid(dir: &Path, uuid: &str) -> Result<Profile> {
    let entries: Vec<DirEntry> = entries(dir)?.collect();
    if let Some(profile) = filter(entries, |profile| profile.info.uuid == uuid).pop() {
        Ok(profile)
    } else {
        Err(Error::Own(format!("Profile '{}' is not found.", uuid)))
    }
}

/// Searches provisioning profiles by their uuid(s) or bundle id(s).
///
/// The search is performed concurrently.
pub fn find_by_ids(dir: &Path, ids: &[String]) -> Result<Vec<Profile>> {
    let entries: Vec<DirEntry> = entries(dir)?.collect();
    let profiles = filter(entries, |profile| {
        ids.iter()
            .any(|id| id == &profile.info.uuid || profile.info.bundle_id() == Some(&id))
    });
    Ok(profiles)
}

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

    #[test]
    fn filter_mobileprovision_files() {
        use std::fs::File;
        use tempdir::TempDir;

        let temp_dir = TempDir::new("test").unwrap();
        let result = entries(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(0));

        File::create(temp_dir.path().join("1.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("2.mobileprovision")).unwrap();
        File::create(temp_dir.path().join("3.txt")).unwrap();
        let result = entries(temp_dir.path()).map(|iter| iter.count());
        expect!(result).to(be_ok().value(2));
    }
}
<|endoftext|>


<commit_before>pub mod ui {
    extern crate term;
    use net;

    pub fn update(state: net::State) {
        match state {
            net::State::Online  => online(),
            net::State::Offline => offline()
        }
    }

    pub fn online() {
        let mut t = term::stdout().unwrap();
        t.fg(term::color::GREEN).unwrap();
        writeln!(t, "Online").unwrap();
        t.reset();
    }
    pub fn offline() {
        let mut t = term::stdout().unwrap();
        t.fg(term::color::RED).unwrap();
        writeln!(t, "Offline").unwrap();
        t.reset();
    }
    pub fn clear_screen() {
        println!("\x1b[2J\x1b[1;1H");
    }
}

pub mod net {
    extern crate curl;
    use self::curl::http;
    pub enum State { Online, Offline }
    pub fn is_online() -> State {
        let resp = http::handle()
            .get("http://localhost:8000")
            .exec();

        match resp {
            Ok(r) => State::Online,
            Err(e) => State::Offline
        }
    }

}
<commit_msg>Supply an URL via environment variables (fixes #1)<commit_after>pub mod ui {
    extern crate term;
    use net;

    pub fn update(state: net::State) {
        match state {
            net::State::Online  => online(),
            net::State::Offline => offline()
        }
    }

    pub fn online() {
        let mut t = term::stdout().unwrap();
        t.fg(term::color::GREEN).unwrap();
        writeln!(t, "Online").unwrap();
        t.reset();
    }
    pub fn offline() {
        let mut t = term::stdout().unwrap();
        t.fg(term::color::RED).unwrap();
        writeln!(t, "Offline").unwrap();
        t.reset();
    }
    pub fn clear_screen() {
        println!("\x1b[2J\x1b[1;1H");
    }
}

pub mod net {
    extern crate curl;
    use self::curl::http;
    use std::env;
    pub enum State { Online, Offline }
    pub fn is_online() -> State {

        // Going full on rust here. Thanks @skade! :D
        let env_var = env::var_os("ONLINE_CHECK_HOST");
        let check_target = env_var.as_ref()
            .map(|val| val.to_str().unwrap())
            .unwrap_or("https://duckduckgo.com/");

        let resp = http::handle()
            .get(check_target)
            .exec();

        match resp {
            Ok(r) => State::Online,
            Err(e) => State::Offline
        }
    }

}
<|endoftext|>


<commit_before>
extern crate ndarray;
extern crate num_traits;

pub mod prelude;
pub mod error;
pub mod vector;
pub mod matrix;
pub mod square;
pub mod hermite;

pub mod qr;
pub mod svd;
pub mod eigh;
pub mod norm;
pub mod solve;
<commit_msg>Add crate document<commit_after>//! This crate serves matrix manipulation for [rust-ndarray](https://github.com/bluss/rust-ndarray)
//! They are implemented as traits,
//! [Matrix](matrix/trait.Matrix.html), [SquareMatrix](square/trait.SquareMatrix.html), and
//! [HermiteMatrix](hermite/trait.HermiteMatrix.html)
//!
//! Matrix
//! -------
//! - [singular-value decomposition](matrix/trait.Matrix.html#tymethod.svd)
//! - [QR decomposition](matrix/trait.Matrix.html#tymethod.qr)
//! - [operator norm for L1 norm](matrix/trait.Matrix.html#tymethod.norm_1)
//! - [operator norm for L-inf norm](matrix/trait.Matrix.html#tymethod.norm_i)
//! - [Frobeiuns norm](matrix/trait.Matrix.html#tymethod.norm_f)
//!
//! SquareMatrix
//! -------------
//! - [inverse of matrix](square/trait.SquareMatrix.html#tymethod.inv)
//! - [trace of matrix](square/trait.SquareMatrix.html#tymethod.trace)
//! - [WIP] eigenvalue
//! - [WIP] LU factorization
//!
//! HermiteMatrix
//! --------------
//! - [eigenvalue analysis](hermite/trait.HermiteMatrix.html#tymethod.eigh)
//! - [symmetric square root](hermite/trait.HermiteMatrix.html#tymethod.ssqrt)
//! - [WIP] Cholesky factorization

extern crate ndarray;
extern crate num_traits;

pub mod prelude;
pub mod error;
pub mod vector;
pub mod matrix;
pub mod square;
pub mod hermite;

pub mod qr;
pub mod svd;
pub mod eigh;
pub mod norm;
pub mod solve;
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * TODO: Show the current shortcut in the status bar.
 * TODO: Set a fixed height to the status bar.
 * TODO: Try to return an Application directly instead of an Rc<Application>.
 * TODO: support shortcuts with number like "50G".
 */

//! Minimal UI library based on GTK+.

#![warn(missing_docs)]

extern crate gdk;
extern crate gdk_sys;
extern crate glib;
extern crate gobject_sys;
extern crate gtk;
extern crate gtk_sys;
extern crate libc;
extern crate mg_settings;

mod key_converter;
mod gobject;
mod style_context;
#[macro_use]
mod widget;
mod status_bar;

use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::rc::Rc;

use gdk::{EventKey, RGBA, CONTROL_MASK};
use gdk::enums::key::{Escape, colon};
use gdk_sys::GdkRGBA;
use gtk::{ContainerExt, Grid, Inhibit, IsA, Settings, Widget, WidgetExt, Window, WindowExt, WindowType, STATE_FLAG_NORMAL};
use gtk::prelude::WidgetExtManual;
use mg_settings::{Config, EnumFromStr, Parser};
use mg_settings::Command::{Custom, Include, Map, Set, Unmap};
use mg_settings::error::{Error, Result};
use mg_settings::error::ErrorType::{MissingArgument, NoCommand, Parse, UnknownCommand};
use mg_settings::key::Key;

use key_converter::gdk_key_to_key;
use gobject::ObjectExtManual;
use self::ShortcutCommand::{Complete, Incomplete};
use status_bar::{StatusBar, StatusBarItem};
use style_context::StyleContextExtManual;

const RED: &'static GdkRGBA = &GdkRGBA { red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0 };
const TRANSPARENT: &'static GdkRGBA = &GdkRGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0 };
const WHITE: &'static GdkRGBA = &GdkRGBA { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 };

/// A command from a map command.
#[derive(Debug)]
enum ShortcutCommand {
    /// A complete command that is to be executed.
    Complete(String),
    /// An incomplete command where the user needs to complete it and press Enter.
    Incomplete(String),
}

/// Create a new MG application window.
/// This window contains a status bar where the user can type a command and a central widget.
pub struct Application<T> {
    command_callback: RefCell<Option<Box<Fn(T)>>>,
    current_mode: String,
    current_shortcut: RefCell<Vec<Key>>,
    foreground_color: RefCell<RGBA>,
    mappings: RefCell<HashMap<String, HashMap<Vec<Key>, String>>>,
    message: StatusBarItem,
    settings_parser: RefCell<Parser<T>>,
    status_bar: StatusBar,
    vbox: Grid,
    variables: RefCell<HashMap<String, Box<Fn() -> String>>>,
    window: Window,
}

impl<T: EnumFromStr + 'static> Application<T> {
    /// Create a new application.
    #[allow(new_without_default)]
    pub fn new() -> Rc<Self> {
        Application::new_with_config(Config::default())
    }

    /// Create a new application with configuration.
    pub fn new_with_config(config: Config) -> Rc<Self> {
        let window = Window::new(WindowType::Toplevel);
        window.connect_delete_event(|_, _| {
            gtk::main_quit();
            Inhibit(false)
        });

        let grid = Grid::new();
        window.add(&grid);

        let status_bar = StatusBar::new();
        grid.attach(&status_bar, 0, 1, 1, 1);
        window.show_all();
        status_bar.hide();

        let foreground_color = Application::<T>::get_foreground_color(&window);

        let message = StatusBarItem::new().left();

        let app = Rc::new(Application {
            command_callback: RefCell::new(None),
            current_mode: "n".to_string(),
            current_shortcut: RefCell::new(vec![]),
            foreground_color: RefCell::new(foreground_color),
            mappings: RefCell::new(HashMap::new()),
            message: message,
            settings_parser: RefCell::new(Parser::new_with_config(config)),
            status_bar: status_bar,
            vbox: grid,
            variables: RefCell::new(HashMap::new()),
            window: window,
        });

        app.status_bar.add_item(&app.message);

        {
            let instance = app.clone();
            app.status_bar.connect_activate(move |command| instance.handle_command(command));
        }

        {
            let instance = app.clone();
            app.window.connect_key_press_event(move |_, key| instance.key_press(key));
        }

        app
    }

    /// Convert an action String to a command String.
    fn action_to_command(&self, action: &str) -> ShortcutCommand {
        if let Some(':') = action.chars().next() {
            if let Some(index) = action.find("<Enter>") {
                Complete(action[1..index].to_string())
            }
            else {
                Incomplete(action[1..].to_string())
            }
        }
        else {
            Complete(action.to_string())
        }
    }

    /// Create a new status bar item.
    pub fn add_statusbar_item(&self) -> StatusBarItem {
        let item = StatusBarItem::new();
        self.status_bar.add_item(&item);
        item
    }

    /// Add the key to the current shortcut.
    fn add_to_shortcut(&self, key: Key) {
        let mut shortcut = self.current_shortcut.borrow_mut();
        shortcut.push(key);
    }

    /// Add a variable that can be used in mappings.
    /// The placeholder will be replaced by the value return by the function.
    pub fn add_variable<F: Fn() -> String + 'static>(&self, variable_name: &str, function: F) {
        let mut variables = self.variables.borrow_mut();
        variables.insert(variable_name.to_string(), Box::new(function));
    }

    /// Add a callback to the command event.
    pub fn connect_command<F: Fn(T) + 'static>(&self, callback: F) {
        *self.command_callback.borrow_mut() = Some(Box::new(callback));
    }

    /// Show an error to the user.
    pub fn error(&self, error: &str) {
        self.message.set_text(error);
        self.status_bar.override_background_color(STATE_FLAG_NORMAL, RED);
        self.status_bar.override_color(STATE_FLAG_NORMAL, WHITE);
    }

    /// Get the color of the text.
    fn get_foreground_color(window: &Window) -> RGBA {
        let style_context = window.get_style_context().unwrap();
        style_context.get_color(STATE_FLAG_NORMAL)
    }

    /// Handle the command activate event.
    fn handle_command(&self, command: Option<String>) {
        if let Some(command) = command {
            if let Some(ref callback) = *self.command_callback.borrow() {
                let result = self.settings_parser.borrow_mut().parse_line(&command);
                match result {
                    Ok(command) => {
                        match command {
                            Custom(command) => callback(command),
                            _ => unimplemented!(),
                        }
                    },
                    Err(error) => {
                        if let Some(error) = error.downcast_ref::<Error>() {
                            let message =
                                match error.typ {
                                    MissingArgument => "Argument required".to_string(),
                                    NoCommand => return,
                                    Parse => format!("Parse error: unexpected {}, expecting: {}", error.unexpected, error.expected),
                                    UnknownCommand => format!("Not a command: {}", error.unexpected),
                                };
                            self.error(&message);
                        }
                    },
                }
            }
            self.status_bar.hide_entry();
        }
    }

    /// Handle a possible input of a shortcut.
    fn handle_shortcut(&self, key: &EventKey) -> Inhibit {
        if !self.status_bar.entry_shown() {
            let control_pressed = key.get_state() & CONTROL_MASK == CONTROL_MASK;
            if let Some(key) = gdk_key_to_key(key.get_keyval(), control_pressed) {
                self.add_to_shortcut(key);
                let action = {
                    let shortcut = self.current_shortcut.borrow();
                    let mappings = self.mappings.borrow();
                    mappings.get(&self.current_mode)
                        .and_then(|mappings| mappings.get(&*shortcut).cloned())
                };
                if let Some(action) = action {
                    self.reset();
                    match self.action_to_command(&action) {
                        Complete(command) => self.handle_command(Some(command)),
                        Incomplete(command) => {
                            self.input_command(&command);
                            return Inhibit(true);
                        },
                    }
                }
                else if self.no_possible_shortcut() {
                    self.reset();
                }
            }
        }
        Inhibit(false)
    }

    /// Input the specified command.
    fn input_command(&self, command: &str) {
        self.status_bar.show_entry();
        let variables = self.variables.borrow();
        let mut command = command.to_string();
        for (variable, function) in variables.iter() {
            command = command.replace(&format!("<{}>", variable), &function());
        }
        self.status_bar.set_command(&format!("{} ", command));
    }

    /// Handle the key press event.
    #[allow(non_upper_case_globals)]
    fn key_press(&self, key: &EventKey) -> Inhibit {
        match key.get_keyval() {
            colon => {
                if !self.status_bar.entry_shown() {
                    self.reset();
                    self.status_bar.show_entry();
                    Inhibit(true)
                }
                else {
                    Inhibit(false)
                }
            },
            Escape => {
                self.reset();
                Inhibit(true)
            },
            _ => self.handle_shortcut(key),
        }
    }

    /// Check if there are no possible shortcuts.
    fn no_possible_shortcut(&self) -> bool {
        let current_shortcut = self.current_shortcut.borrow();
        let mappings = self.mappings.borrow();
        if let Some(mappings) = mappings.get(&self.current_mode) {
            for key in mappings.keys() {
                if key.starts_with(&*current_shortcut) {
                    return false;
                }
            }
        }
        true
    }

    /// Parse a configuration file.
    pub fn parse_config<P: AsRef<Path>>(&self, filename: P) -> Result<()> {
        let file = try!(File::open(filename));
        let buf_reader = BufReader::new(file);
        let commands = try!(self.settings_parser.borrow_mut().parse(buf_reader));
        for command in commands {
            match command {
                Custom(_) => (), // TODO: call the callback?
                Include(_) => (), // TODO: parse the included file.
                Map { action, keys, mode } => {
                    let mut mappings = self.mappings.borrow_mut();
                    let mappings = mappings.entry(mode).or_insert_with(HashMap::new);
                    mappings.insert(keys, action);
                },
                Set(_, _) => (), // TODO: set settings.
                Unmap { .. } => (), // TODO
            }
        }
        Ok(())
    }

    /// Handle the escape event.
    fn reset(&self) {
        self.status_bar.override_background_color(STATE_FLAG_NORMAL, TRANSPARENT);
        self.status_bar.override_color(STATE_FLAG_NORMAL, &self.foreground_color.borrow());
        self.status_bar.hide();
        self.message.hide();
        let mut shortcut = self.current_shortcut.borrow_mut();
        shortcut.clear();
    }

    /// Set the main widget.
    pub fn set_view<W: IsA<Widget> + WidgetExt>(&self, view: &W) {
        view.set_hexpand(true);
        view.set_vexpand(true);
        view.show_all();
        self.vbox.attach(view, 0, 0, 1, 1);
    }

    /// Set the window title.
    pub fn set_window_title(&self, title: &str) {
        self.window.set_title(title);
    }

    /// Use the dark variant of the theme if available.
    pub fn use_dark_theme(&self) {
        let settings = Settings::get_default().unwrap();
        settings.set_data("gtk-application-prefer-dark-theme", 1);
        *self.foreground_color.borrow_mut() = Application::<T>::get_foreground_color(&self.window);
    }

    /// Get the application window.
    pub fn window(&self) -> &Window {
        &self.window
    }
}
<commit_msg>Fixed to only append a space in the command entry when there are no arguments.<commit_after>/*
 * Copyright (c) 2016 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

/*
 * TODO: Show the current shortcut in the status bar.
 * TODO: Set a fixed height to the status bar.
 * TODO: Try to return an Application directly instead of an Rc<Application>.
 * TODO: support shortcuts with number like "50G".
 */

//! Minimal UI library based on GTK+.

#![warn(missing_docs)]

extern crate gdk;
extern crate gdk_sys;
extern crate glib;
extern crate gobject_sys;
extern crate gtk;
extern crate gtk_sys;
extern crate libc;
extern crate mg_settings;

mod key_converter;
mod gobject;
mod style_context;
#[macro_use]
mod widget;
mod status_bar;

use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fs::File;
use std::io::BufReader;
use std::path::Path;
use std::rc::Rc;

use gdk::{EventKey, RGBA, CONTROL_MASK};
use gdk::enums::key::{Escape, colon};
use gdk_sys::GdkRGBA;
use gtk::{ContainerExt, Grid, Inhibit, IsA, Settings, Widget, WidgetExt, Window, WindowExt, WindowType, STATE_FLAG_NORMAL};
use gtk::prelude::WidgetExtManual;
use mg_settings::{Config, EnumFromStr, Parser};
use mg_settings::Command::{Custom, Include, Map, Set, Unmap};
use mg_settings::error::{Error, Result};
use mg_settings::error::ErrorType::{MissingArgument, NoCommand, Parse, UnknownCommand};
use mg_settings::key::Key;

use key_converter::gdk_key_to_key;
use gobject::ObjectExtManual;
use self::ShortcutCommand::{Complete, Incomplete};
use status_bar::{StatusBar, StatusBarItem};
use style_context::StyleContextExtManual;

const RED: &'static GdkRGBA = &GdkRGBA { red: 1.0, green: 0.0, blue: 0.0, alpha: 1.0 };
const TRANSPARENT: &'static GdkRGBA = &GdkRGBA { red: 0.0, green: 0.0, blue: 0.0, alpha: 0.0 };
const WHITE: &'static GdkRGBA = &GdkRGBA { red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0 };

/// A command from a map command.
#[derive(Debug)]
enum ShortcutCommand {
    /// A complete command that is to be executed.
    Complete(String),
    /// An incomplete command where the user needs to complete it and press Enter.
    Incomplete(String),
}

/// Create a new MG application window.
/// This window contains a status bar where the user can type a command and a central widget.
pub struct Application<T> {
    command_callback: RefCell<Option<Box<Fn(T)>>>,
    current_mode: String,
    current_shortcut: RefCell<Vec<Key>>,
    foreground_color: RefCell<RGBA>,
    mappings: RefCell<HashMap<String, HashMap<Vec<Key>, String>>>,
    message: StatusBarItem,
    settings_parser: RefCell<Parser<T>>,
    status_bar: StatusBar,
    vbox: Grid,
    variables: RefCell<HashMap<String, Box<Fn() -> String>>>,
    window: Window,
}

impl<T: EnumFromStr + 'static> Application<T> {
    /// Create a new application.
    #[allow(new_without_default)]
    pub fn new() -> Rc<Self> {
        Application::new_with_config(Config::default())
    }

    /// Create a new application with configuration.
    pub fn new_with_config(config: Config) -> Rc<Self> {
        let window = Window::new(WindowType::Toplevel);
        window.connect_delete_event(|_, _| {
            gtk::main_quit();
            Inhibit(false)
        });

        let grid = Grid::new();
        window.add(&grid);

        let status_bar = StatusBar::new();
        grid.attach(&status_bar, 0, 1, 1, 1);
        window.show_all();
        status_bar.hide();

        let foreground_color = Application::<T>::get_foreground_color(&window);

        let message = StatusBarItem::new().left();

        let app = Rc::new(Application {
            command_callback: RefCell::new(None),
            current_mode: "n".to_string(),
            current_shortcut: RefCell::new(vec![]),
            foreground_color: RefCell::new(foreground_color),
            mappings: RefCell::new(HashMap::new()),
            message: message,
            settings_parser: RefCell::new(Parser::new_with_config(config)),
            status_bar: status_bar,
            vbox: grid,
            variables: RefCell::new(HashMap::new()),
            window: window,
        });

        app.status_bar.add_item(&app.message);

        {
            let instance = app.clone();
            app.status_bar.connect_activate(move |command| instance.handle_command(command));
        }

        {
            let instance = app.clone();
            app.window.connect_key_press_event(move |_, key| instance.key_press(key));
        }

        app
    }

    /// Convert an action String to a command String.
    fn action_to_command(&self, action: &str) -> ShortcutCommand {
        if let Some(':') = action.chars().next() {
            if let Some(index) = action.find("<Enter>") {
                Complete(action[1..index].to_string())
            }
            else {
                Incomplete(action[1..].to_string())
            }
        }
        else {
            Complete(action.to_string())
        }
    }

    /// Create a new status bar item.
    pub fn add_statusbar_item(&self) -> StatusBarItem {
        let item = StatusBarItem::new();
        self.status_bar.add_item(&item);
        item
    }

    /// Add the key to the current shortcut.
    fn add_to_shortcut(&self, key: Key) {
        let mut shortcut = self.current_shortcut.borrow_mut();
        shortcut.push(key);
    }

    /// Add a variable that can be used in mappings.
    /// The placeholder will be replaced by the value return by the function.
    pub fn add_variable<F: Fn() -> String + 'static>(&self, variable_name: &str, function: F) {
        let mut variables = self.variables.borrow_mut();
        variables.insert(variable_name.to_string(), Box::new(function));
    }

    /// Add a callback to the command event.
    pub fn connect_command<F: Fn(T) + 'static>(&self, callback: F) {
        *self.command_callback.borrow_mut() = Some(Box::new(callback));
    }

    /// Show an error to the user.
    pub fn error(&self, error: &str) {
        self.message.set_text(error);
        self.status_bar.override_background_color(STATE_FLAG_NORMAL, RED);
        self.status_bar.override_color(STATE_FLAG_NORMAL, WHITE);
    }

    /// Get the color of the text.
    fn get_foreground_color(window: &Window) -> RGBA {
        let style_context = window.get_style_context().unwrap();
        style_context.get_color(STATE_FLAG_NORMAL)
    }

    /// Handle the command activate event.
    fn handle_command(&self, command: Option<String>) {
        if let Some(command) = command {
            if let Some(ref callback) = *self.command_callback.borrow() {
                let result = self.settings_parser.borrow_mut().parse_line(&command);
                match result {
                    Ok(command) => {
                        match command {
                            Custom(command) => callback(command),
                            _ => unimplemented!(),
                        }
                    },
                    Err(error) => {
                        if let Some(error) = error.downcast_ref::<Error>() {
                            let message =
                                match error.typ {
                                    MissingArgument => "Argument required".to_string(),
                                    NoCommand => return,
                                    Parse => format!("Parse error: unexpected {}, expecting: {}", error.unexpected, error.expected),
                                    UnknownCommand => format!("Not a command: {}", error.unexpected),
                                };
                            self.error(&message);
                        }
                    },
                }
            }
            self.status_bar.hide_entry();
        }
    }

    /// Handle a possible input of a shortcut.
    fn handle_shortcut(&self, key: &EventKey) -> Inhibit {
        if !self.status_bar.entry_shown() {
            let control_pressed = key.get_state() & CONTROL_MASK == CONTROL_MASK;
            if let Some(key) = gdk_key_to_key(key.get_keyval(), control_pressed) {
                self.add_to_shortcut(key);
                let action = {
                    let shortcut = self.current_shortcut.borrow();
                    let mappings = self.mappings.borrow();
                    mappings.get(&self.current_mode)
                        .and_then(|mappings| mappings.get(&*shortcut).cloned())
                };
                if let Some(action) = action {
                    self.reset();
                    match self.action_to_command(&action) {
                        Complete(command) => self.handle_command(Some(command)),
                        Incomplete(command) => {
                            self.input_command(&command);
                            return Inhibit(true);
                        },
                    }
                }
                else if self.no_possible_shortcut() {
                    self.reset();
                }
            }
        }
        Inhibit(false)
    }

    /// Input the specified command.
    fn input_command(&self, command: &str) {
        self.status_bar.show_entry();
        let variables = self.variables.borrow();
        let mut command = command.to_string();
        for (variable, function) in variables.iter() {
            command = command.replace(&format!("<{}>", variable), &function());
        }
        let text: Cow<str> =
            if command.contains(' ') {
                command.into()
            }
            else {
                format!("{} ", command).into()
            };
        self.status_bar.set_command(&text);
    }

    /// Handle the key press event.
    #[allow(non_upper_case_globals)]
    fn key_press(&self, key: &EventKey) -> Inhibit {
        match key.get_keyval() {
            colon => {
                if !self.status_bar.entry_shown() {
                    self.reset();
                    self.status_bar.show_entry();
                    Inhibit(true)
                }
                else {
                    Inhibit(false)
                }
            },
            Escape => {
                self.reset();
                Inhibit(true)
            },
            _ => self.handle_shortcut(key),
        }
    }

    /// Check if there are no possible shortcuts.
    fn no_possible_shortcut(&self) -> bool {
        let current_shortcut = self.current_shortcut.borrow();
        let mappings = self.mappings.borrow();
        if let Some(mappings) = mappings.get(&self.current_mode) {
            for key in mappings.keys() {
                if key.starts_with(&*current_shortcut) {
                    return false;
                }
            }
        }
        true
    }

    /// Parse a configuration file.
    pub fn parse_config<P: AsRef<Path>>(&self, filename: P) -> Result<()> {
        let file = try!(File::open(filename));
        let buf_reader = BufReader::new(file);
        let commands = try!(self.settings_parser.borrow_mut().parse(buf_reader));
        for command in commands {
            match command {
                Custom(_) => (), // TODO: call the callback?
                Include(_) => (), // TODO: parse the included file.
                Map { action, keys, mode } => {
                    let mut mappings = self.mappings.borrow_mut();
                    let mappings = mappings.entry(mode).or_insert_with(HashMap::new);
                    mappings.insert(keys, action);
                },
                Set(_, _) => (), // TODO: set settings.
                Unmap { .. } => (), // TODO
            }
        }
        Ok(())
    }

    /// Handle the escape event.
    fn reset(&self) {
        self.status_bar.override_background_color(STATE_FLAG_NORMAL, TRANSPARENT);
        self.status_bar.override_color(STATE_FLAG_NORMAL, &self.foreground_color.borrow());
        self.status_bar.hide();
        self.message.hide();
        let mut shortcut = self.current_shortcut.borrow_mut();
        shortcut.clear();
    }

    /// Set the main widget.
    pub fn set_view<W: IsA<Widget> + WidgetExt>(&self, view: &W) {
        view.set_hexpand(true);
        view.set_vexpand(true);
        view.show_all();
        self.vbox.attach(view, 0, 0, 1, 1);
    }

    /// Set the window title.
    pub fn set_window_title(&self, title: &str) {
        self.window.set_title(title);
    }

    /// Use the dark variant of the theme if available.
    pub fn use_dark_theme(&self) {
        let settings = Settings::get_default().unwrap();
        settings.set_data("gtk-application-prefer-dark-theme", 1);
        *self.foreground_color.borrow_mut() = Application::<T>::get_foreground_color(&self.window);
    }

    /// Get the application window.
    pub fn window(&self) -> &Window {
        &self.window
    }
}
<|endoftext|>


<commit_before>//! A library the provides a safe Rust wrapper for
//! [Stacy's Pathetically Simple Parsing System](https://github.com/sprowell/spsps).

extern crate libc;
#[macro_use]
extern crate log;
extern crate env_logger as logger;
use log::LogLevel;
use std::io::prelude::*;
use std::str;
use std::char;
use std::ptr;
use std::path::Path;
use std::fs;
use std::fs::File;
use std::ffi::CStr;
use std::ffi::CString;

/// For now we're treating spsps_loc_ as an opaque pointer.
enum spsps_loc_ {}
/// An opaque type for spsps_errno. Currently unused.
enum spsps_errno {}

/*
#[repr(C)]
struct Loc{
    name: *const libc::c_char,
    line: u32,
    column: u32,
}
*/

/// This is for the opaque C pointer to spsps_parser_
enum spsps_parser_ {}
/// This is the safe abstraction over a spsps parser.
#[derive(Debug)]
pub struct Parser{
    /// The raw pointer to a C spsps parser.
    _ptr: *mut spsps_parser_,
    /// A raw pointer a C file stream. May be null for stdin.
    _fd: *mut libc::FILE
}

#[derive(Debug)]
/// A safe abstruction over a raw spsps_loc_ pointer.
pub struct Loc{
    /// The *mut spsps_loc_pointer.
    ptr: *mut spsps_loc_
}

#[link(name = "spsps")]
extern{
    //This returns an owned Parser
    fn spsps_new(name: *const libc::c_char, stream: *mut libc::FILE) -> *mut spsps_parser_;
    fn spsps_free(parser: *mut spsps_parser_);
    //This returns an owned C string.
    fn spsps_loc_to_string(loc: *mut spsps_loc_) -> *mut libc::c_char;
    //This returns a borrowed string
    fn spsps_printchar(xch: libc::c_char) -> *mut libc::c_char;
    fn spsps_consume(parser: *mut spsps_parser_) -> libc::c_char;
    fn spsps_consume_n(parser: *mut spsps_parser_, n: libc::size_t);
    fn spsps_consume_whitespace(parser: *mut spsps_parser_);
    //Not sure what return type to use for bool
    fn spsps_eof(parser: *mut spsps_parser_) -> libc::c_int;
    //This returns an owned Loc
    fn spsps_loc(parser: *mut spsps_parser_) -> *mut spsps_loc_;
    fn spsps_peek(parser: *mut spsps_parser_) -> libc::c_char;
    fn spsps_peek_n(parser: *mut spsps_parser_, n: libc::size_t) -> *mut libc::c_char;
    fn spsps_peek_str(parser: *mut spsps_parser_, next: *const libc::c_char) -> libc::c_int;
    fn spsps_peek_and_consume(parser: *mut spsps_parser_, next: *const libc::c_char) -> libc::c_int;
}

impl Parser{
    /// Create a new `Parser` with zero configuration. Name will use the default from libspsps,
    /// and will read from stdin.
    pub fn new() -> Parser {
        unsafe{
            Parser{
                _ptr: spsps_new(ptr::null(), ptr::null_mut()),
                _fd: ptr::null_mut()
            }
         }
    }

    /// Create a Parser that will read from the file given in `path`. If `None` is given for `name`
    /// the default spsps parser name will be used.
    pub fn from_file(name: Option<&str>, path: &Path) -> Parser {
        unsafe{
            let c_path = CString::new(path.to_str().unwrap()).unwrap().as_ptr();
            debug!("from_file path: {:?}", CString::new(path.to_str().unwrap()).unwrap());
            let fd = libc::funcs::c95::stdio::fopen(c_path,  CString::new("r").unwrap().as_ptr());
            if fd.is_null() {
                panic!("Unable to open file to parse.");
            }
            match name{
                Some(n) => Parser{
                        _ptr: spsps_new(CString::new(n).unwrap().as_ptr(), fd),
                        _fd: fd
                    },
                None => Parser{
                        _ptr: spsps_new(ptr::null(), fd),
                        _fd: fd
                    }
            }
         }
    }

    /// Consume and return the next character in the stream.
    pub fn consume(&self) -> String {
        unsafe {
            char::from_u32(spsps_consume(self._ptr) as u32).unwrap().to_string()
        }
    }

    /// Consume and discard the next `n` characters from the stream.
    pub fn consume_n(&self, n: usize){
        unsafe { spsps_consume_n(self._ptr, n as libc::size_t); }
    }

    /// Consume and discard all whitespace.  When this method returns the next
    /// character is the first non-whitespace character, or the end of file has
    /// been reached.
    pub fn consume_whitespace(&self){
        unsafe { spsps_consume_whitespace(self._ptr); }
    }

    ///Determine if the end of file has been consumed.
    pub fn eof(&self) -> bool{
        unsafe { spsps_eof(self._ptr) as usize != 0 }
    }

    /// Get the current location in the stream.  This is the location of the next
    /// character to be read, unless the end of stream has been reached.
    pub fn get_loc(&self) -> Loc {
        unsafe{
            Loc { ptr: spsps_loc(self._ptr) }
        }
    }

    ///Peek and return the next character in the stream.  The character is not consumed.
    pub fn peek(&self) -> String{
        let c_char = unsafe { spsps_peek(self._ptr) };
        char::from_u32(c_char as u32).unwrap().to_string()
    }

    /// Peek ahead at the next few characters in the stream, and return them.  The
    /// The number of characters must be below the lookahead limit.  End of file causes the
    /// returned string to be populated by EOF characters.
    pub fn peek_n(&self, n: usize) -> String{
        let cstr = unsafe{ spsps_peek_n(self._ptr, n as libc::size_t) };
        let safe_cstr = unsafe { CStr::from_ptr(cstr) };
        let retval = str::from_utf8(safe_cstr.to_bytes()).unwrap_or("").to_owned();
        unsafe { libc::free(safe_cstr.as_ptr() as *mut libc::c_void); }
        return retval;
    }

    /// Peek ahead and determine if the next characters in the stream are the given
    /// characters, in sequence.  That is, the given string must be the next thing in the stream.
    pub fn peek_str(&self, needle: &str) -> bool {
        unsafe { spsps_peek_str(self._ptr, CString::new(needle).unwrap().as_ptr()) != 0 }
    }

    ///Peek ahead at the next few characters and if they are a given string, then
    /// consume them.  Otherwise leave the stream unchanged.
    pub fn peek_str_and_consume(&self, needle: &str) -> bool {
        unsafe { spsps_peek_and_consume(self._ptr, CString::new(needle).unwrap().as_ptr()) != 0 }
    }
}

impl Drop for Parser{
    fn drop(&mut self) {
        unsafe{
            if !self._ptr.is_null() { spsps_free(self._ptr); }
            if !self._fd.is_null() { libc::fclose(self._fd); }
        }
    }
}

impl ToString for Loc{
    ///Convert this location to a short string.
    fn to_string(&self) -> String{
        let tmp = unsafe { CStr::from_ptr(spsps_loc_to_string(self.ptr)) };
        let retval = str::from_utf8(tmp.to_bytes()).unwrap_or("").to_owned();
        unsafe {libc::free(tmp.as_ptr() as *mut libc::c_void);}
        retval
    }
}

impl Drop for Loc{
    fn drop(&mut self) {
        unsafe{ libc::free(self.ptr as *mut libc::c_void); }
    }
}

fn test_parser() -> Parser {
    let mut f = File::create("./foo.txt").unwrap();
    f.write_all(b"This is a test.\n");
    f.sync_data();
    let test_path = Path::new("./foo.txt");
    Parser::from_file(Some("test"), test_path)
}

#[test]
fn test_from_file(){
    logger::init().unwrap();
    let p = test_parser();
    //fs::remove_file("foo.txt").unwrap();
    println!("{:?}", p);
}

#[test]
fn test_peek_n(){
    let p = test_parser();
    let first_chars = p.peek_n(4);
    println!("{}", first_chars);
    assert_eq!(first_chars, "This");
}

#[test]
fn test_peek(){
    let p = test_parser();
    let first_char = p.peek();
    println!("{:?}", first_char);
    assert_eq!(first_char, "T");
}

#[test]
fn test_consume(){
    let p = test_parser();
    let first_char = p.consume();
    println!("{}", first_char);
    assert_eq!(first_char, "T");
}

#[test]
fn test_loc_to_string(){
    let p = test_parser();
    let location = p.get_loc();
    let loc_string = location.to_string();
    println!("{}",loc_string);
    assert_eq!(loc_string, "test:1:1");
}

#[test]
fn test_peek_consume(){
    let parser = test_parser();
    let p = parser.peek();
    let c = parser.consume();
    assert_eq!(p, c);
}

#[test]
fn test_zzz_cleanup()
{
    fs::remove_file("foo.txt").unwrap();
}<commit_msg>Cleaned up warnings.<commit_after>//! A library the provides a safe Rust wrapper for
//! [Stacy's Pathetically Simple Parsing System](https://github.com/sprowell/spsps).

extern crate libc;
#[macro_use]
extern crate log;
extern crate env_logger as logger;
use log::LogLevel;
use std::io::prelude::*;
use std::str;
use std::char;
use std::ptr;
use std::path::Path;
use std::fs::File;
use std::ffi::CStr;
use std::ffi::CString;

/// For now we're treating spsps_loc_ as an opaque pointer.
#[allow(non_camel_case_types)]
enum spsps_loc_ {}
/// An opaque type for spsps_errno. Currently unused.
#[allow(non_camel_case_types)]
enum spsps_errno {}

/*
#[repr(C)]
struct Loc{
    name: *const libc::c_char,
    line: u32,
    column: u32,
}
*/

/// This is for the opaque C pointer to spsps_parser_
#[allow(non_camel_case_types)]
enum spsps_parser_ {}
/// This is the safe abstraction over a spsps parser.
#[derive(Debug)]
#[allow(raw_pointer_derive)]
pub struct Parser{
    /// The raw pointer to a C spsps parser.
    _ptr: *mut spsps_parser_,
    /// A raw pointer a C file stream. May be null for stdin.
    _fd: *mut libc::FILE
}

#[derive(Debug)]
#[allow(raw_pointer_derive)]
/// A safe abstruction over a raw spsps_loc_ pointer.
pub struct Loc{
    /// The *mut spsps_loc_pointer.
    ptr: *mut spsps_loc_
}

#[link(name = "spsps")]
extern{
    //This returns an owned Parser
    fn spsps_new(name: *const libc::c_char, stream: *mut libc::FILE) -> *mut spsps_parser_;
    fn spsps_free(parser: *mut spsps_parser_);
    //This returns an owned C string.
    fn spsps_loc_to_string(loc: *mut spsps_loc_) -> *mut libc::c_char;
    //This returns a borrowed string
    fn spsps_printchar(xch: libc::c_char) -> *mut libc::c_char;
    fn spsps_consume(parser: *mut spsps_parser_) -> libc::c_char;
    fn spsps_consume_n(parser: *mut spsps_parser_, n: libc::size_t);
    fn spsps_consume_whitespace(parser: *mut spsps_parser_);
    //Not sure what return type to use for bool
    fn spsps_eof(parser: *mut spsps_parser_) -> libc::c_int;
    //This returns an owned Loc
    fn spsps_loc(parser: *mut spsps_parser_) -> *mut spsps_loc_;
    fn spsps_peek(parser: *mut spsps_parser_) -> libc::c_char;
    fn spsps_peek_n(parser: *mut spsps_parser_, n: libc::size_t) -> *mut libc::c_char;
    fn spsps_peek_str(parser: *mut spsps_parser_, next: *const libc::c_char) -> libc::c_int;
    fn spsps_peek_and_consume(parser: *mut spsps_parser_, next: *const libc::c_char) -> libc::c_int;
}

impl Parser{
    /// Create a new `Parser` with zero configuration. Name will use the default from libspsps,
    /// and will read from stdin.
    pub fn new() -> Parser {
        unsafe{
            Parser{
                _ptr: spsps_new(ptr::null(), ptr::null_mut()),
                _fd: ptr::null_mut()
            }
         }
    }

    /// Create a Parser that will read from the file given in `path`. If `None` is given for `name`
    /// the default spsps parser name will be used.
    pub fn from_file(name: Option<&str>, path: &Path) -> Parser {
        unsafe{
            let c_path = CString::new(path.to_str().unwrap()).unwrap().as_ptr();
            debug!("from_file path: {:?}", CString::new(path.to_str().unwrap()).unwrap());
            let fd = libc::funcs::c95::stdio::fopen(c_path,  CString::new("r").unwrap().as_ptr());
            if fd.is_null() {
                panic!("Unable to open file to parse.");
            }
            match name{
                Some(n) => Parser{
                        _ptr: spsps_new(CString::new(n).unwrap().as_ptr(), fd),
                        _fd: fd
                    },
                None => Parser{
                        _ptr: spsps_new(ptr::null(), fd),
                        _fd: fd
                    }
            }
         }
    }

    /// Consume and return the next character in the stream.
    pub fn consume(&self) -> String {
        unsafe {
            char::from_u32(spsps_consume(self._ptr) as u32).unwrap().to_string()
        }
    }

    /// Consume and discard the next `n` characters from the stream.
    pub fn consume_n(&self, n: usize){
        unsafe { spsps_consume_n(self._ptr, n as libc::size_t); }
    }

    /// Consume and discard all whitespace.  When this method returns the next
    /// character is the first non-whitespace character, or the end of file has
    /// been reached.
    pub fn consume_whitespace(&self){
        unsafe { spsps_consume_whitespace(self._ptr); }
    }

    ///Determine if the end of file has been consumed.
    pub fn eof(&self) -> bool{
        unsafe { spsps_eof(self._ptr) as usize != 0 }
    }

    /// Get the current location in the stream.  This is the location of the next
    /// character to be read, unless the end of stream has been reached.
    pub fn get_loc(&self) -> Loc {
        unsafe{
            Loc { ptr: spsps_loc(self._ptr) }
        }
    }

    ///Peek and return the next character in the stream.  The character is not consumed.
    pub fn peek(&self) -> String{
        let c_char = unsafe { spsps_peek(self._ptr) };
        char::from_u32(c_char as u32).unwrap().to_string()
    }

    /// Peek ahead at the next few characters in the stream, and return them.  The
    /// The number of characters must be below the lookahead limit.  End of file causes the
    /// returned string to be populated by EOF characters.
    pub fn peek_n(&self, n: usize) -> String{
        let cstr = unsafe{ spsps_peek_n(self._ptr, n as libc::size_t) };
        let safe_cstr = unsafe { CStr::from_ptr(cstr) };
        let retval = str::from_utf8(safe_cstr.to_bytes()).unwrap_or("").to_owned();
        unsafe { libc::free(safe_cstr.as_ptr() as *mut libc::c_void); }
        return retval;
    }

    /// Peek ahead and determine if the next characters in the stream are the given
    /// characters, in sequence.  That is, the given string must be the next thing in the stream.
    pub fn peek_str(&self, needle: &str) -> bool {
        unsafe { spsps_peek_str(self._ptr, CString::new(needle).unwrap().as_ptr()) != 0 }
    }

    ///Peek ahead at the next few characters and if they are a given string, then
    /// consume them.  Otherwise leave the stream unchanged.
    pub fn peek_str_and_consume(&self, needle: &str) -> bool {
        unsafe { spsps_peek_and_consume(self._ptr, CString::new(needle).unwrap().as_ptr()) != 0 }
    }
}

impl Drop for Parser{
    fn drop(&mut self) {
        unsafe{
            if !self._ptr.is_null() { spsps_free(self._ptr); }
            if !self._fd.is_null() { libc::fclose(self._fd); }
        }
    }
}

impl ToString for Loc{
    ///Convert this location to a short string.
    fn to_string(&self) -> String{
        let tmp = unsafe { CStr::from_ptr(spsps_loc_to_string(self.ptr)) };
        let retval = str::from_utf8(tmp.to_bytes()).unwrap_or("").to_owned();
        unsafe {libc::free(tmp.as_ptr() as *mut libc::c_void);}
        retval
    }
}

impl Drop for Loc{
    fn drop(&mut self) {
        unsafe{ libc::free(self.ptr as *mut libc::c_void); }
    }
}

#[allow(unused_must_use)]
fn test_parser() -> Parser {
    let mut f = File::create("./foo.txt").unwrap();
    f.write_all(b"This is a test.\n");
    f.sync_data();
    let test_path = Path::new("./foo.txt");
    Parser::from_file(Some("test"), test_path)
}

#[test]
fn test_from_file(){
    logger::init().unwrap();
    let p = test_parser();
    //fs::remove_file("foo.txt").unwrap();
    println!("{:?}", p);
}

#[test]
fn test_peek_n(){
    let p = test_parser();
    let first_chars = p.peek_n(4);
    println!("{}", first_chars);
    assert_eq!(first_chars, "This");
}

#[test]
fn test_peek(){
    let p = test_parser();
    let first_char = p.peek();
    println!("{:?}", first_char);
    assert_eq!(first_char, "T");
}

#[test]
fn test_consume(){
    let p = test_parser();
    let first_char = p.consume();
    println!("{}", first_char);
    assert_eq!(first_char, "T");
}

#[test]
fn test_loc_to_string(){
    let p = test_parser();
    let location = p.get_loc();
    let loc_string = location.to_string();
    println!("{}",loc_string);
    assert_eq!(loc_string, "test:1:1");
}

#[test]
fn test_peek_consume(){
    let parser = test_parser();
    let p = parser.peek();
    let c = parser.consume();
    assert_eq!(p, c);
}

#[test]
fn test_zzz_cleanup()
{
    use std::fs;
    fs::remove_file("foo.txt").unwrap();
}<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;

use std::collections::{HashMap, HashSet};
use std::io::Read;
use std::fs::File;

use regex::Regex;

lazy_static! {
    static ref WORDS: HashMap<String, usize> = {
        let mut f = File::open("data/big.txt").unwrap();
        let mut s = String::new();
        f.read_to_string(&mut s).unwrap();

        // Count occurrences of words found in `big.txt`
        let mut words = HashMap::new();

        for word in Regex::new(r"\W+").unwrap().split(&s) {
            let count = words.entry(word.to_lowercase()).or_insert(0);
            *count += 1;
        }

        return words;
    };
}

/// Most probable spelling correction for `word`.
pub fn correction(word: &str) -> String {
    if word == "" {
        return String::from("");
    }

    let mut best_match = String::from(word);
    let mut best_score = 0;

    for candidate in candidates(word) {
        if let Some(score) = WORDS.get(&candidate) {
            if *score > best_score {
                best_score = *score;
                best_match = candidate;
            }
        }
    }

    return best_match;
}

/// Generate possible spelling corrections for `word`.
fn candidates(word: &str) -> HashSet<String> {
    let mut word_set = HashSet::new();
    word_set.insert(String::from(word));

    if WORDS.contains_key(word) {
        return word_set;
    } else if let Some(single_edits) = known(edits(word)) {
        return single_edits;
    } else if let Some(double_edits) = known(double_edits(word)) {
        return double_edits;
    }

    return word_set;
}

/// The subset of `words` that appear in the dictionary of WORDS
fn known(words: HashSet<String>) -> Option<HashSet<String>> {
    let mut known_words = HashSet::new();

    for word in words {
        if WORDS.contains_key(&word) {
            known_words.insert(word);
        }
    }

    match known_words.is_empty() {
        false => return Some(known_words),
        true => return None,
    }
}

/// All edits that are one edit away from `word`.
fn edits(word: &str) -> HashSet<String> {
    // String containing lowercase letters in alphabetical order
    let letters = String::from_utf8((97..123).collect()).unwrap();

    // Construct vector of split variants of `word`
    // cat -> [("", "cat"), ("c", "at"), ("ca", "t")]
    let splits: Vec<_> = (0..word.len()).map(|i| (&word[..i], &word[i..])).collect();

    // Iterate through different edit permutations
    let mut all_edits = HashSet::new();

    for (left, right) in splits {
        // Deletions
        let mut deletion = String::from(left);
        deletion = deletion + &right[1..];
        all_edits.insert(deletion);

        // Transpositions
        if right.len() > 1 {
            let mut transposition = String::from(left);
            let middle: &String = &right[..2].chars().rev().collect();
            transposition = transposition + middle + &right[2..];
            all_edits.insert(transposition);
        }

        for letter in letters.chars() {
            // Replacements
            let mut replacement = String::from(left);
            replacement.push(letter);
            replacement = replacement + &right[1..];
            all_edits.insert(replacement);

            // Insertions
            let mut insertion = String::from(left);
            insertion.push(letter);
            insertion = insertion + right;
            all_edits.insert(insertion);
        }
    }

    // End-of-word insertions (appends?)
    for letter in letters.chars() {
        let mut insertion = String::from(word);
        insertion.push(letter);
        all_edits.insert(insertion);
    }

    return all_edits;
}

/// All edits that are two edits away from `word`
fn double_edits(word: &str) -> HashSet<String> {
    let mut all_edits = HashSet::new();

    for single_edit in edits(word) {
        for double_edit in edits(&single_edit) {
            all_edits.insert(double_edit);
        }
    }

    return all_edits;
}

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

    #[test]
    fn edit_count() {
        // Words of length n > 1 will have:
        // - n deletions
        // - n-1 transpositions
        // - 26n replacements
        // - 26(n+1) insertions
        // = 54n + 25 total edits

        // Use a punctuation-based test word to ensure unique edits
        let test_word = "!@#$%^&*()";

        for i in 1..(test_word.len() + 1) {
            assert_eq!(edits(&test_word[..i]).len(), 54*i + 25);
        }
    }

    #[test]
    fn possible_candidates() {
        let candidates = candidates("ct");
        assert!(candidates.contains("cat"));
        assert!(candidates.contains("cot"));
        assert!(candidates.contains("cut"));
        assert!(!candidates.contains("czt"));
    }
}
<commit_msg>Store words using vectors instead of hash sets<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;

use std::collections::HashMap;
use std::io::Read;
use std::fs::File;

use regex::Regex;

lazy_static! {
    static ref WORDS: HashMap<String, usize> = {
        let mut f = File::open("data/big.txt").unwrap();
        let mut s = String::new();
        f.read_to_string(&mut s).unwrap();

        // Count occurrences of words found in `big.txt`
        let mut words = HashMap::new();

        for word in Regex::new(r"\W+").unwrap().split(&s) {
            let count = words.entry(word.to_lowercase()).or_insert(0);
            *count += 1;
        }

        return words;
    };
}

/// Most probable spelling correction for `word`.
pub fn correction(word: &str) -> String {
    if word == "" {
        return String::from("");
    }

    let mut best_match = String::from(word);
    let mut best_score = 0;

    for candidate in candidates(word) {
        if let Some(score) = WORDS.get(&candidate) {
            if *score > best_score {
                best_score = *score;
                best_match = candidate;
            }
        }
    }

    return best_match;
}

/// Generate possible spelling corrections for `word`.
fn candidates(word: &str) -> Vec<String> {
    let mut word_set = Vec::new();
    word_set.push(String::from(word));

    if WORDS.contains_key(word) {
        return word_set;
    } else if let Some(single_edits) = known(edits(word)) {
        return single_edits;
    } else if let Some(double_edits) = known(double_edits(word)) {
        return double_edits;
    }

    return word_set;
}

/// The subset of `words` that appear in the dictionary of WORDS
fn known(words: Vec<String>) -> Option<Vec<String>> {
    let mut known_words = Vec::new();

    for word in words {
        if WORDS.contains_key(&word) {
            known_words.push(word);
        }
    }

    match known_words.is_empty() {
        false => return Some(known_words),
        true => return None,
    }
}

/// All edits that are one edit away from `word`.
fn edits(word: &str) -> Vec<String> {
    // String containing lowercase letters in alphabetical order
    let letters = String::from_utf8((97..123).collect()).unwrap();

    // Construct vector of split variants of `word`
    // cat -> [("", "cat"), ("c", "at"), ("ca", "t")]
    let splits: Vec<_> = (0..word.len()).map(|i| (&word[..i], &word[i..])).collect();

    // Iterate through different edit permutations (at most 54n + 25)
    let mut all_edits = Vec::with_capacity(word.len() * 54 + 25);

    for (left, right) in splits {
        // Deletions
        let deletion = String::from(left);
        all_edits.push(deletion + &right[1..]);

        // Transpositions
        if right.len() > 1 {
            let transposition = String::from(left);
            let middle: &String = &right[..2].chars().rev().collect();
            all_edits.push(transposition + middle + &right[2..]);
        }

        for letter in letters.chars() {
            // Replacements
            let mut replacement = String::from(left);
            replacement.push(letter);
            all_edits.push(replacement + &right[1..]);

            // Insertions
            let mut insertion = String::from(left);
            insertion.push(letter);
            all_edits.push(insertion + right);
        }
    }

    // End-of-word insertions (appends?)
    for letter in letters.chars() {
        let mut insertion = String::from(word);
        insertion.push(letter);
        all_edits.push(insertion);
    }

    return all_edits;
}

/// All edits that are two edits away from `word`
fn double_edits(word: &str) -> Vec<String> {
    let mut all_edits = Vec::new();

    for single_edit in edits(word) {
        for double_edit in edits(&single_edit) {
            all_edits.push(double_edit);
        }
    }

    return all_edits;
}

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

    #[test]
    fn edit_count() {
        // Words of length n > 1 will have:
        // - n deletions
        // - n-1 transpositions
        // - 26n replacements
        // - 26(n+1) insertions
        // = 54n + 25 total edits

        // Use a punctuation-based test word to ensure unique edits
        let test_word = "!@#$%^&*()";

        for i in 1..(test_word.len() + 1) {
            assert_eq!(edits(&test_word[..i]).len(), 54*i + 25);
        }
    }

    #[test]
    fn possible_candidates() {
        let candidates = candidates("ther");

        for word in vec!["there", "the", "their", "her"] {
            assert!(candidates.contains(&String::from(word)));
        }
    }
}
<|endoftext|>


<commit_before>#![feature(if_let)]

use std::os;
use std::io::Command;
use std::io::process::InheritFd;
use std::default::Default;

/// Extra configuration to pass to gcc.
pub struct Config {
    /// Directories where gcc will look for header files.
    pub include_directories: Vec<Path>,
    /// Additional definitions (`-DKEY` or `-DKEY=VALUE`).
    pub definitions: Vec<(String, Option<String>)>,
    /// Additional object files to link into the final archive
    pub objects: Vec<Path>,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            include_directories: Vec::new(),
            definitions: Vec::new(),
            objects: Vec::new(),
        }
    }
}

/// Compile a library from the given set of input C files.
///
/// This will simply compile all files into object files and then assemble them
/// into the output. This will read the standard environment variables to detect
/// cross compilations and such.
///
/// # Example
///
/// ```no_run
/// use std::default::Default;
/// gcc::compile_library("libfoo.a", &Default::default(), &[
///     "foo.c",
///     "bar.c",
/// ]);
/// ```
pub fn compile_library(output: &str, config: &Config, files: &[&str]) {
    assert!(output.starts_with("lib"));
    assert!(output.ends_with(".a"));

    let target = os::getenv("TARGET").unwrap();
    let opt_level = os::getenv("OPT_LEVEL").unwrap();

    let mut cmd = Command::new(gcc());
    cmd.arg(format!("-O{}", opt_level));
    cmd.arg("-c");
    cmd.arg("-ffunction-sections").arg("-fdata-sections");
    cmd.args(cflags().as_slice());

    if target.as_slice().contains("-ios") {
        cmd.args(ios_flags(target.as_slice()).as_slice());
    } else {
        if target.as_slice().contains("i686") {
            cmd.arg("-m32");
        } else if target.as_slice().contains("x86_64") {
            cmd.arg("-m64");
        }

        if !target.as_slice().contains("i686") {
            cmd.arg("-fPIC");
        }
    }

    for directory in config.include_directories.iter() {
        cmd.arg("-I").arg(directory);
    }

    for &(ref key, ref value) in config.definitions.iter() {
        if let &Some(ref value) = value {
            cmd.arg(format!("-D{}={}", key, value));
        } else {
            cmd.arg(format!("-D{}", key));
        }
    }

    let src = Path::new(os::getenv("CARGO_MANIFEST_DIR").unwrap());
    let dst = Path::new(os::getenv("OUT_DIR").unwrap());
    let mut objects = Vec::new();
    for file in files.iter() {
        let obj = dst.join(*file).with_extension("o");
        std::io::fs::mkdir_recursive(&obj.dir_path(), std::io::USER_RWX).unwrap();
        run(cmd.clone().arg(src.join(*file)).arg("-o").arg(&obj));
        objects.push(obj);
    }

    run(Command::new(ar()).arg("crus")
                          .arg(dst.join(output))
                          .args(objects.as_slice())
                          .args(config.objects.as_slice()));
    println!("cargo:rustc-flags=-L {} -l {}:static",
             dst.display(), output.slice(3, output.len() - 2));
}

fn run(cmd: &mut Command) {
    println!("running: {}", cmd);
    let status = match cmd.stdout(InheritFd(1)).stderr(InheritFd(2)).status() {
        Ok(status) => status,
        Err(e) => panic!("failed to spawn process: {}", e),
    };
    if !status.success() {
        panic!("nonzero exit status: {}", status);
    }
}

fn get_var(var_base: &str) -> Result<String, String> {
    let target = os::getenv("TARGET")
        .expect("Environment variable 'TARGET' is unset");
    let host = match os::getenv("HOST") {
            None => { return Err("Environment variable 'HOST' is unset".to_string()); }
            Some(x) => x
        };
    let kind = if host == target { "HOST" } else { "TARGET" };
    let target_u = target.split('-')
        .collect::<Vec<&str>>()
        .connect("_");
    let res = os::getenv(format!("{}_{}", var_base, target).as_slice())
        .or_else(|| os::getenv(format!("{}_{}", var_base, target_u).as_slice()))
        .or_else(|| os::getenv(format!("{}_{}", kind, var_base).as_slice()))
        .or_else(|| os::getenv(var_base)).expect("Could not get environment variable");
    Ok(res)
}

fn gcc() -> String {
    get_var("CC").unwrap_or(if cfg!(windows) {
        "gcc".to_string()
    } else {
        "cc".to_string()
    })
}

fn ar() -> String {
    get_var("AR").unwrap_or("ar".to_string())
}

fn cflags() -> Vec<String> {
    get_var("CFLAGS").unwrap_or(String::new())
       .as_slice().words().map(|s| s.to_string())
       .collect()
}

fn ios_flags(target: &str) -> Vec<String> {
    let mut is_device_arch = false;
    let mut res = Vec::new();

    if target.starts_with("arm-") {
        res.push("-arch");
        res.push("armv7");
        is_device_arch = true;
    } else if target.starts_with("arm64-") {
        res.push("-arch");
        res.push("arm64");
        is_device_arch = true;
    } else if target.starts_with("i386-") {
        res.push("-m32");
    } else if target.starts_with("x86_64-") {
        res.push("-m64");
    }

    let sdk = if is_device_arch {"iphoneos"} else {"iphonesimulator"};

    println!("Detecting iOS SDK path for {}", sdk);
    let sdk_path = Command::new("xcrun")
        .arg("--show-sdk-path")
        .arg("--sdk")
        .arg(sdk)
        .stderr(InheritFd(2))
        .output()
        .unwrap()
        .output;

    let sdk_path = String::from_utf8(sdk_path).unwrap();

    res.push("-isysroot");
    res.push(sdk_path.as_slice().trim());

    res.iter().map(|s| s.to_string()).collect::<Vec<_>>()
}
<commit_msg>Don't fail when all envs are unset. Also log envs checked.<commit_after>#![feature(if_let)]

use std::io::Command;
use std::io::process::InheritFd;
use std::default::Default;

/// Extra configuration to pass to gcc.
pub struct Config {
    /// Directories where gcc will look for header files.
    pub include_directories: Vec<Path>,
    /// Additional definitions (`-DKEY` or `-DKEY=VALUE`).
    pub definitions: Vec<(String, Option<String>)>,
    /// Additional object files to link into the final archive
    pub objects: Vec<Path>,
}

impl Default for Config {
    fn default() -> Config {
        Config {
            include_directories: Vec::new(),
            definitions: Vec::new(),
            objects: Vec::new(),
        }
    }
}

fn getenv(v: &str) -> Option<String> {
    use std::os::getenv;
    let r = getenv(v);
    println!("{} = {}", v, r);
    r
}

/// Compile a library from the given set of input C files.
///
/// This will simply compile all files into object files and then assemble them
/// into the output. This will read the standard environment variables to detect
/// cross compilations and such.
///
/// # Example
///
/// ```no_run
/// use std::default::Default;
/// gcc::compile_library("libfoo.a", &Default::default(), &[
///     "foo.c",
///     "bar.c",
/// ]);
/// ```
pub fn compile_library(output: &str, config: &Config, files: &[&str]) {
    assert!(output.starts_with("lib"));
    assert!(output.ends_with(".a"));

    let target = getenv("TARGET").unwrap();
    let opt_level = getenv("OPT_LEVEL").unwrap();

    let mut cmd = Command::new(gcc());
    cmd.arg(format!("-O{}", opt_level));
    cmd.arg("-c");
    cmd.arg("-ffunction-sections").arg("-fdata-sections");
    cmd.args(cflags().as_slice());

    if target.as_slice().contains("-ios") {
        cmd.args(ios_flags(target.as_slice()).as_slice());
    } else {
        if target.as_slice().contains("i686") {
            cmd.arg("-m32");
        } else if target.as_slice().contains("x86_64") {
            cmd.arg("-m64");
        }

        if !target.as_slice().contains("i686") {
            cmd.arg("-fPIC");
        }
    }

    for directory in config.include_directories.iter() {
        cmd.arg("-I").arg(directory);
    }

    for &(ref key, ref value) in config.definitions.iter() {
        if let &Some(ref value) = value {
            cmd.arg(format!("-D{}={}", key, value));
        } else {
            cmd.arg(format!("-D{}", key));
        }
    }

    let src = Path::new(getenv("CARGO_MANIFEST_DIR").unwrap());
    let dst = Path::new(getenv("OUT_DIR").unwrap());
    let mut objects = Vec::new();
    for file in files.iter() {
        let obj = dst.join(*file).with_extension("o");
        std::io::fs::mkdir_recursive(&obj.dir_path(), std::io::USER_RWX).unwrap();
        run(cmd.clone().arg(src.join(*file)).arg("-o").arg(&obj));
        objects.push(obj);
    }

    run(Command::new(ar()).arg("crus")
                          .arg(dst.join(output))
                          .args(objects.as_slice())
                          .args(config.objects.as_slice()));
    println!("cargo:rustc-flags=-L {} -l {}:static",
             dst.display(), output.slice(3, output.len() - 2));
}

fn run(cmd: &mut Command) {
    println!("running: {}", cmd);
    let status = match cmd.stdout(InheritFd(1)).stderr(InheritFd(2)).status() {
        Ok(status) => status,
        Err(e) => panic!("failed to spawn process: {}", e),
    };
    if !status.success() {
        panic!("nonzero exit status: {}", status);
    }
}

fn get_var(var_base: &str) -> Result<String, String> {
    let target = getenv("TARGET")
        .expect("Environment variable 'TARGET' is unset");
    let host = match getenv("HOST") {
            None => { return Err("Environment variable 'HOST' is unset".to_string()); }
            Some(x) => x
        };
    let kind = if host == target { "HOST" } else { "TARGET" };
    let target_u = target.split('-')
        .collect::<Vec<&str>>()
        .connect("_");
    let res = getenv(format!("{}_{}", var_base, target).as_slice())
        .or_else(|| getenv(format!("{}_{}", var_base, target_u).as_slice()))
        .or_else(|| getenv(format!("{}_{}", kind, var_base).as_slice()))
        .or_else(|| getenv(var_base));

    match res {
        Some(res) => Ok(res),
        None => Err("Could not get environment variable".to_string()),
    }
}

fn gcc() -> String {
    get_var("CC").unwrap_or(if cfg!(windows) {
        "gcc".to_string()
    } else {
        "cc".to_string()
    })
}

fn ar() -> String {
    get_var("AR").unwrap_or("ar".to_string())
}

fn cflags() -> Vec<String> {
    get_var("CFLAGS").unwrap_or(String::new())
       .as_slice().words().map(|s| s.to_string())
       .collect()
}

fn ios_flags(target: &str) -> Vec<String> {
    let mut is_device_arch = false;
    let mut res = Vec::new();

    if target.starts_with("arm-") {
        res.push("-arch");
        res.push("armv7");
        is_device_arch = true;
    } else if target.starts_with("arm64-") {
        res.push("-arch");
        res.push("arm64");
        is_device_arch = true;
    } else if target.starts_with("i386-") {
        res.push("-m32");
    } else if target.starts_with("x86_64-") {
        res.push("-m64");
    }

    let sdk = if is_device_arch {"iphoneos"} else {"iphonesimulator"};

    println!("Detecting iOS SDK path for {}", sdk);
    let sdk_path = Command::new("xcrun")
        .arg("--show-sdk-path")
        .arg("--sdk")
        .arg(sdk)
        .stderr(InheritFd(2))
        .output()
        .unwrap()
        .output;

    let sdk_path = String::from_utf8(sdk_path).unwrap();

    res.push("-isysroot");
    res.push(sdk_path.as_slice().trim());

    res.iter().map(|s| s.to_string()).collect::<Vec<_>>()
}
<|endoftext|>