<commit_before>//! Routing for paths delimited by a forward slash, with the ability to capture
//! specified path segments.
//!
//! # Example
//!
//! ```rust
//! use path_router::Tree;
//! let mut routes = Tree::new();
//! routes.add("GET/user/:username/profile", "profile.html");
//! assert_eq!(
//!     routes.find("GET/user/my_name/profile"),
//!     Some((&"profile.html", vec![("username", "my_name")])));
//! ```

/// The routing information is stored as a trie.
///
/// # Description
///
/// Each node is labelled with its path segment. The value is a tuple. The first
/// element is generic, and is usually a handler function; the second element is
/// the captured path segments.
///
pub struct Tree<'a, T> {
    label: &'a str,
    value: Option<(T, Vec<&'a str>)>,
    branches: Vec<Tree<'a, T>>
}

impl<'a, T> Tree<'a, T> {
    /// Constructs a new routing tree.
    pub fn new<'b>() -> Tree<'b, T> {
        Tree {
            label: "",
            value: None,
            branches: Vec::new()
        }
    }
    /// Adds a new path and its associated value to the tree. Prefix a segment
    /// with a colon (:) to enable capturing on the segment.
    ///
    /// # Panics
    ///
    /// Panics if a duplicate route is added.
    ///
    pub fn add(&mut self, key: &'a str, value: T) {
        info!("Adding route {}", key);
        let segments = key.split('/').filter(|s| !s.is_empty());
        let capture_labels = Vec::new();    // Will be filled while iterating
        self.add_(segments, value, capture_labels);
    }
    fn add_<I: Iterator<Item=&'a str>>(
        &mut self, mut segments: I, value: T,
        mut capture_labels: Vec<&'a str>) {
        match segments.next() {
            None => {
                if self.value.is_some() {
                    error!("Duplicate route!");
                    panic!("Duplicate route!");
                }
                self.value = Some((value, capture_labels))
            },
            Some(segment) => {
                if let Some(existing_branch) =
                    self.branches.iter_mut().find(|t| t.label == segment) {
                        existing_branch.add_(segments, value, capture_labels);
                        return;
                    }
                if segment.starts_with(':') {
                    capture_labels.push(&segment[1..]);
                    if let Some(existing_branch) =
                        self.branches.iter_mut().find(|t| t.label.is_empty()) {
                            existing_branch.add_(
                                segments, value, capture_labels);
                            return;
                        }
                    let mut branch = Tree {
                        label: "",
                        value: None,
                        branches: Vec::new()
                    };
                    branch.add_(segments, value, capture_labels);
                    self.branches.push(branch);
                } else {
                    let mut branch = Tree {
                        label: segment,
                        value: None,
                        branches: Vec::new()
                    };
                    branch.add_(segments, value, capture_labels);
                    self.branches.push(branch);
                }
            }
        }
    }
    /// Retrieve the value associated with the path, together with the captured
    /// path segments.
    pub fn find<'b>(
        &self,
        key: &'b str
        ) -> Option<(&T, Vec<(&'a str, &'b str)>)> {
        let segments: Vec<&str> = key.split('/')
            .filter(|s| !s.is_empty())
            .collect();
        let mut captured = Vec::new();  // Will be filled while iterating
        self.find_(segments.as_slice(), &mut captured)
            .map(|&(ref v, ref labels)| {
                (v, labels.iter().cloned().zip(captured).collect())
            })
    }
    fn find_<'b>(
        &self,
        segments: &[&'b str],
        captured: &mut Vec<&'b str>
        ) -> Option<&(T, Vec<&'a str>)> {
        match segments.split_first() {
            None => self.value.as_ref(),
            Some((&segment, remaining)) => self.branches.iter().filter_map(|t| {
                if t.label == segment {
                    t.find_(remaining, captured)
                } else if t.label == "" {
                    captured.push(segment);
                    let result = t.find_(remaining, captured);
                    if result.is_none() {
                        captured.pop();
                    }
                    result
                } else {
                    None
                }
            }).next()
        }
    }
}

#[cfg(test)]
mod tests {
    use Tree;
    #[test]
    fn can_add_and_find() {
        let mut routes = Tree::new();
        routes.add("/", 0);
        routes.add("/var", 1);
        routes.add("/var/www", 11);
        routes.add("/var/log", 12);
        assert_eq!(routes.find("/vax"), None);
        assert_eq!(routes.find("/var/something"), None);
        assert_eq!(
            routes.find("////"),
            Some((&0, vec![])));
        assert_eq!(
            routes.find("//var//"),
            Some((&1, vec![])));
        assert_eq!(
            routes.find("/var/www/"),
            Some((&11, vec![])));
        assert_eq!(
            routes.find("/var/log/"),
            Some((&12, vec![])));
    }
    #[test]
    fn can_add_and_capture_and_find() {
        let mut routes = Tree::new();
        routes.add("/user/:username", 11);
        routes.add("/user/:username/:intent/show", 111);
        routes.add("/user/:username/profile", 112);
        assert_eq!(routes.find("/user/myname/delete"), None);
        assert_eq!(routes.find("/user/myname/cook/throw"), None);
        assert_eq!(
            routes.find("/user/myname"),
            Some((&11, vec![("username", "myname")])));
        assert_eq!(
            routes.find("/user/myname/profile"),
            Some((&112, vec![("username", "myname")])));
        assert_eq!(
            routes.find("/user/myname/cook/show"),
            Some((&111, vec![
                  ("username", "myname"),
                  ("intent", "cook")
            ])));
    }
    #[test]
    fn can_add_and_capture_and_find_handlers() {
        let mut routes = Tree::new();
        let handler = |captured: Vec<(&str, &str)>| {
            assert_eq!(captured.len(), 2);
            assert_eq!(captured[0].0, "folder");
            assert_eq!(captured[0].1, "myfolder");
            assert_eq!(captured[1].0, "file");
            assert_eq!(captured[1].1, "myfile");
        };
        routes.add("home/:folder/:file", handler);
        match routes.find("/home/myfolder/myfile") {
            None => assert!(false),
            Some((fx, captured)) => fx(captured)
        }
    }
}
<commit_msg>Remove log dependency<commit_after>//! Routing for paths delimited by a forward slash, with the ability to capture
//! specified path segments.
//!
//! # Example
//!
//! ```rust
//! use path_router::Tree;
//! let mut routes = Tree::new();
//! routes.add("GET/user/:username/profile", "profile.html");
//! assert_eq!(
//!     routes.find("GET/user/my_name/profile"),
//!     Some((&"profile.html", vec![("username", "my_name")])));
//! ```

/// The routing information is stored as a trie.
///
/// # Description
///
/// Each node is labelled with its path segment. The value is a tuple. The first
/// element is generic, and is usually a handler function; the second element is
/// the captured path segments.
///
pub struct Tree<'a, T> {
    label: &'a str,
    value: Option<(T, Vec<&'a str>)>,
    branches: Vec<Tree<'a, T>>
}

impl<'a, T> Tree<'a, T> {
    /// Constructs a new routing tree.
    pub fn new<'b>() -> Tree<'b, T> {
        Tree {
            label: "",
            value: None,
            branches: Vec::new()
        }
    }
    /// Adds a new path and its associated value to the tree. Prefix a segment
    /// with a colon (:) to enable capturing on the segment.
    ///
    /// # Panics
    ///
    /// Panics if a duplicate route is added.
    ///
    pub fn add(&mut self, key: &'a str, value: T) {
        let segments = key.split('/').filter(|s| !s.is_empty());
        let capture_labels = Vec::new();    // Will be filled while iterating
        self.add_(segments, value, capture_labels);
    }
    fn add_<I: Iterator<Item=&'a str>>(
        &mut self, mut segments: I, value: T,
        mut capture_labels: Vec<&'a str>) {
        match segments.next() {
            None => {
                if self.value.is_some() {
                    panic!("Duplicate route!");
                }
                self.value = Some((value, capture_labels))
            },
            Some(segment) => {
                if let Some(existing_branch) =
                    self.branches.iter_mut().find(|t| t.label == segment) {
                        existing_branch.add_(segments, value, capture_labels);
                        return;
                    }
                if segment.starts_with(':') {
                    capture_labels.push(&segment[1..]);
                    if let Some(existing_branch) =
                        self.branches.iter_mut().find(|t| t.label.is_empty()) {
                            existing_branch.add_(
                                segments, value, capture_labels);
                            return;
                        }
                    let mut branch = Tree {
                        label: "",
                        value: None,
                        branches: Vec::new()
                    };
                    branch.add_(segments, value, capture_labels);
                    self.branches.push(branch);
                } else {
                    let mut branch = Tree {
                        label: segment,
                        value: None,
                        branches: Vec::new()
                    };
                    branch.add_(segments, value, capture_labels);
                    self.branches.push(branch);
                }
            }
        }
    }
    /// Retrieve the value associated with the path, together with the captured
    /// path segments.
    pub fn find<'b>(
        &self,
        key: &'b str
        ) -> Option<(&T, Vec<(&'a str, &'b str)>)> {
        let segments: Vec<&str> = key.split('/')
            .filter(|s| !s.is_empty())
            .collect();
        let mut captured = Vec::new();  // Will be filled while iterating
        self.find_(segments.as_slice(), &mut captured)
            .map(|&(ref v, ref labels)| {
                (v, labels.iter().cloned().zip(captured).collect())
            })
    }
    fn find_<'b>(
        &self,
        segments: &[&'b str],
        captured: &mut Vec<&'b str>
        ) -> Option<&(T, Vec<&'a str>)> {
        match segments.split_first() {
            None => self.value.as_ref(),
            Some((&segment, remaining)) => self.branches.iter().filter_map(|t| {
                if t.label == segment {
                    t.find_(remaining, captured)
                } else if t.label == "" {
                    captured.push(segment);
                    let result = t.find_(remaining, captured);
                    if result.is_none() {
                        captured.pop();
                    }
                    result
                } else {
                    None
                }
            }).next()
        }
    }
}

#[cfg(test)]
mod tests {
    use Tree;
    #[test]
    fn can_add_and_find() {
        let mut routes = Tree::new();
        routes.add("/", 0);
        routes.add("/var", 1);
        routes.add("/var/www", 11);
        routes.add("/var/log", 12);
        assert_eq!(routes.find("/vax"), None);
        assert_eq!(routes.find("/var/something"), None);
        assert_eq!(
            routes.find("////"),
            Some((&0, vec![])));
        assert_eq!(
            routes.find("//var//"),
            Some((&1, vec![])));
        assert_eq!(
            routes.find("/var/www/"),
            Some((&11, vec![])));
        assert_eq!(
            routes.find("/var/log/"),
            Some((&12, vec![])));
    }
    #[test]
    fn can_add_and_capture_and_find() {
        let mut routes = Tree::new();
        routes.add("/user/:username", 11);
        routes.add("/user/:username/:intent/show", 111);
        routes.add("/user/:username/profile", 112);
        assert_eq!(routes.find("/user/myname/delete"), None);
        assert_eq!(routes.find("/user/myname/cook/throw"), None);
        assert_eq!(
            routes.find("/user/myname"),
            Some((&11, vec![("username", "myname")])));
        assert_eq!(
            routes.find("/user/myname/profile"),
            Some((&112, vec![("username", "myname")])));
        assert_eq!(
            routes.find("/user/myname/cook/show"),
            Some((&111, vec![
                  ("username", "myname"),
                  ("intent", "cook")
            ])));
    }
    #[test]
    fn can_add_and_capture_and_find_handlers() {
        let mut routes = Tree::new();
        let handler = |captured: Vec<(&str, &str)>| {
            assert_eq!(captured.len(), 2);
            assert_eq!(captured[0].0, "folder");
            assert_eq!(captured[0].1, "myfolder");
            assert_eq!(captured[1].0, "file");
            assert_eq!(captured[1].1, "myfile");
        };
        routes.add("home/:folder/:file", handler);
        match routes.find("/home/myfolder/myfile") {
            None => assert!(false),
            Some((fx, captured)) => fx(captured)
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate exonum;
#[macro_use]
extern crate log;

extern crate serde;

use std::fmt;

use serde::{Serialize, Serializer};

use exonum::blockchain::{Service, Transaction, Schema, NodeState};
use exonum::crypto::{PublicKey, Hash, HexValue};
use exonum::messages::{RawMessage, Message, FromRaw, RawTransaction, Error as MessageError};
use exonum::storage::{Map, View, MapTable, MerklePatriciaTable,
                      Result as StorageResult};

pub const CONFIG_SERVICE: u16 = 1;
pub const CONFIG_PROPOSE_MESSAGE_ID: u16 = 0;
pub const CONFIG_VOTE_MESSAGE_ID: u16 = 1;

message! {
    TxConfigPropose {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_PROPOSE_MESSAGE_ID;
        const SIZE = 56;

        from:           &PublicKey  [00 => 32]
        height:         u64         [32 => 40]
        config:        &[u8]        [40 => 48] // serialized config bytes
        actual_from_height: u64     [48 => 56] // с какой высоты становится актуальным
    }
}

message! {
    TxConfigVote {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_VOTE_MESSAGE_ID;
        const SIZE = 81;

        from:           &PublicKey  [00 => 32]
        height:         u64         [32 => 40]
        hash_propose:   &Hash       [40 => 72] // hash of transacion we're voting for
        seed:           u64         [72 => 80] // incremental (1, 2, 3, 4, 5, 6, 7) проверять +1
        revoke:         bool        [80 => 81] // голос_за=false / отозвать=true
    }
}

#[derive(Clone, PartialEq)]
pub enum ConfigTx {
    ConfigPropose(TxConfigPropose),
    ConfigVote(TxConfigVote),
}

impl Serialize for TxConfigPropose {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        let mut state;
        state = ser.serialize_struct("config_propose", 4)?;
        ser.serialize_struct_elt(&mut state, "from", self.from().to_hex())?;
        ser.serialize_struct_elt(&mut state, "height", self.height())?;
        ser.serialize_struct_elt(&mut state, "config", self.config())?;
        ser.serialize_struct_elt(&mut state, "actual_from_height", self.actual_from_height())?;
        ser.serialize_struct_end(state)
    }
}

impl Serialize for TxConfigVote {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        let mut state;
        state = ser.serialize_struct("vote", 5)?;
        ser.serialize_struct_elt(&mut state, "from", self.from().to_hex())?;
        ser.serialize_struct_elt(&mut state, "height", self.height())?;
        ser.serialize_struct_elt(&mut state, "hash_propose", self.hash_propose().to_hex())?;
        ser.serialize_struct_elt(&mut state, "seed", self.seed())?;
        ser.serialize_struct_elt(&mut state, "revoke", self.revoke())?;
        ser.serialize_struct_end(state)
    }
}

impl Serialize for ConfigTx {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        match *self {
            ConfigTx::ConfigPropose(ref propose) => propose.serialize(ser),
            ConfigTx::ConfigVote(ref vote) => vote.serialize(ser),
        }
    }
}

#[derive(Default)]
pub struct ConfigurationService {}

pub struct ConfigurationSchema<'a> {
    view: &'a View,
}

impl ConfigTx {
    pub fn from(&self) -> &PublicKey {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.from(),
            ConfigTx::ConfigVote(ref msg) => msg.from(),
        }
    }

    pub fn height(&self) -> u64 {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.height(),
            ConfigTx::ConfigVote(ref msg) => msg.height(),
        }
    }
}

impl fmt::Debug for ConfigTx {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => write!(fmt, "{:?}", msg),
            ConfigTx::ConfigVote(ref msg) => write!(fmt, "{:?}", msg),
        }
    }
}

impl FromRaw for ConfigTx {
    fn from_raw(raw: RawMessage) -> Result<ConfigTx, MessageError> {
        match raw.message_type() {
            CONFIG_PROPOSE_MESSAGE_ID => {
                Ok(ConfigTx::ConfigPropose(TxConfigPropose::from_raw(raw)?))
            }
            CONFIG_VOTE_MESSAGE_ID => Ok(ConfigTx::ConfigVote(TxConfigVote::from_raw(raw)?)),
            _ => Err(MessageError::IncorrectMessageType { message_type: raw.message_type() }),
        }
    }
}

impl Message for ConfigTx {
    fn raw(&self) -> &RawMessage {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.raw(),
            ConfigTx::ConfigVote(ref msg) => msg.raw(),
        }
    }

    fn verify_signature(&self, public_key: &PublicKey) -> bool {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.verify_signature(public_key),
            ConfigTx::ConfigVote(ref msg) => msg.verify_signature(public_key),
        }
    }

    fn hash(&self) -> Hash {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.hash(),
            ConfigTx::ConfigVote(ref msg) => msg.hash(),
        }
    }
}

impl<'a> ConfigurationSchema<'a> {
    pub fn new(view: &'a View) -> ConfigurationSchema {
        ConfigurationSchema { view: view }
    }

    pub fn config_proposes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, Hash, TxConfigPropose> {
        // config_propose paricia merkletree <hash_tx> транзакция пропоз
        MerklePatriciaTable::new(MapTable::new(vec![04], self.view))
    }

    pub fn config_votes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, PublicKey, TxConfigVote> {
        // config_votes patricia merkletree <pub_key> последний голос
        MerklePatriciaTable::new(MapTable::new(vec![05], self.view))
    }

    pub fn state_hash(&self) -> StorageResult<Vec<Hash>> {
        Ok(vec![self.config_proposes().root_hash()?, self.config_votes().root_hash()?])
    }

    pub fn get_config_propose(&self,
                              hash: &Hash)
                              -> Result<Option<TxConfigPropose>, exonum::storage::Error> {
        self.config_proposes().get(hash)
    }

    pub fn get_vote(&self,
                    pub_key: &PublicKey)
                    -> Result<Option<TxConfigVote>, exonum::storage::Error> {
        self.config_votes().get(pub_key)
    }
}

impl TxConfigPropose {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let config = blockchain_schema.get_actual_configuration()?;
        if !config.validators.contains(self.from()) {
            error!("ConfigPropose from unknown validator: {:?}", self.from());
            return Ok(());
        }

        let hash = self.hash();
        if config_schema.config_proposes().get(&hash)?.is_some() {
            error!("Received config_propose has already been handled, msg={:?}",
                   self);
            return Ok(());
        }

        trace!("Handle ConfigPropose");
        config_schema.config_proposes().put(&hash, self.clone())?;
        Ok(())
    }
}

impl TxConfigVote {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let config = blockchain_schema.get_actual_configuration()?;

        if !config.validators.contains(self.from()) {
            error!("ConfigVote from unknown validator: {:?}", self.from());
            return Ok(());
        }

        if config_schema.config_proposes().get(self.hash_propose())?.is_none() {
            error!("Received config_vote for unknown transaciton, msg={:?}",
                   self);
            return Ok(());
        }

        if let Some(vote) = config_schema.config_votes().get(self.from())? {
            if vote.seed() != self.seed() - 1 {
                error!("Received config_vote with wrong seed, msg={:?}", self);
                return Ok(());
            }
        }

        let msg = self.clone();
        config_schema.config_votes().put(msg.from(), self.clone())?;
        info!("posted tx: {:?}", self.clone());

        let mut votes_count = 0;
        for pub_key in config.validators.clone() {
            if let Some(vote) = config_schema.config_votes().get(&pub_key)? {
                if !vote.revoke() {
                    votes_count += 1;
                }
            }
        }

        info!("majority count: {}", 2 * config.validators.len() /3);
        if votes_count > 2 * config.validators.len() / 3 {
            if let Some(config_propose) =
                config_schema.config_proposes()
                    .get(self.hash_propose())? {
                blockchain_schema.commit_actual_configuration(config_propose.actual_from_height(),
                                                 config_propose.config())?;
            }
        }
        Ok(())
    }
}

impl Transaction for ConfigTx {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, view: &View) -> StorageResult<()> {
        match *self {
            ConfigTx::ConfigPropose(ref tx) => tx.execute(view),
            ConfigTx::ConfigVote(ref tx) => tx.execute(view),
        }
    }
}

impl ConfigurationService {
    pub fn new() -> ConfigurationService {
        ConfigurationService {}
    }
}

impl Service for ConfigurationService {
    fn service_id(&self) -> u16 {
        CONFIG_SERVICE
    }

    fn state_hash(&self, view: &View) -> StorageResult<Vec<Hash>> {
        let schema = ConfigurationSchema::new(view);
        schema.state_hash()
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError> {
        ConfigTx::from_raw(raw).map(|tx| Box::new(tx) as Box<Transaction>)
    }

    fn handle_commit(&self, state: &mut NodeState) -> StorageResult<()> {
        let old_cfg = state.actual_config().clone();

        let new_cfg = {
            let schema = Schema::new(state.view());
            schema.get_actual_configuration()?
        };

        if new_cfg != old_cfg {
            info!("Updated node config={:#?}", new_cfg);
            state.update_config(new_cfg);
        }
        Ok(())
    }
}
<commit_msg>Changed format of tables, which store config in exonum::blockchain::schema.<commit_after>#[macro_use]
extern crate exonum;
#[macro_use]
extern crate log;

extern crate serde;

use std::fmt;

use serde::{Serialize, Serializer};

use exonum::blockchain::{Service, Transaction, Schema, NodeState};
use exonum::crypto::{PublicKey, Hash, HexValue};
use exonum::messages::{RawMessage, Message, FromRaw, RawTransaction, Error as MessageError};
use exonum::storage::{Map, View, MapTable, MerklePatriciaTable,
                      Result as StorageResult};

pub const CONFIG_SERVICE: u16 = 1;
pub const CONFIG_PROPOSE_MESSAGE_ID: u16 = 0;
pub const CONFIG_VOTE_MESSAGE_ID: u16 = 1;

message! {
    TxConfigPropose {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_PROPOSE_MESSAGE_ID;
        const SIZE = 72;

        from:           &PublicKey  [00 => 32]
        prev_cfg_hash:  &Hash       [32 => 64]
        cfg:            &[u8]       [64 => 72] // serialized config bytes
    }
}

message! {
    TxConfigVote {
        const TYPE = CONFIG_SERVICE;
        const ID = CONFIG_VOTE_MESSAGE_ID;
        const SIZE = 64;

        from:           &PublicKey  [00 => 32]
        cfg_hash:       &Hash       [32 => 64] // hash of config we're voting for
    }
}

#[derive(Clone, PartialEq)]
pub enum ConfigTx {
    ConfigPropose(TxConfigPropose),
    ConfigVote(TxConfigVote),
}

impl Serialize for TxConfigPropose {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        let mut state;
        state = ser.serialize_struct("config_propose", 4)?;
        ser.serialize_struct_elt(&mut state, "from", self.from().to_hex())?;
        ser.serialize_struct_elt(&mut state, "height", self.height())?;
        ser.serialize_struct_elt(&mut state, "config", self.config())?;
        ser.serialize_struct_elt(&mut state, "actual_from_height", self.actual_from_height())?;
        ser.serialize_struct_end(state)
    }
}

impl Serialize for TxConfigVote {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        let mut state;
        state = ser.serialize_struct("vote", 5)?;
        ser.serialize_struct_elt(&mut state, "from", self.from().to_hex())?;
        ser.serialize_struct_elt(&mut state, "height", self.height())?;
        ser.serialize_struct_elt(&mut state, "hash_propose", self.hash_propose().to_hex())?;
        ser.serialize_struct_elt(&mut state, "seed", self.seed())?;
        ser.serialize_struct_elt(&mut state, "revoke", self.revoke())?;
        ser.serialize_struct_end(state)
    }
}

impl Serialize for ConfigTx {
    fn serialize<S>(&self, ser: &mut S) -> Result<(), S::Error>
        where S: Serializer
    {
        match *self {
            ConfigTx::ConfigPropose(ref propose) => propose.serialize(ser),
            ConfigTx::ConfigVote(ref vote) => vote.serialize(ser),
        }
    }
}

#[derive(Default)]
pub struct ConfigurationService {}

pub struct ConfigurationSchema<'a> {
    view: &'a View,
}

impl ConfigTx {
    pub fn from(&self) -> &PublicKey {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.from(),
            ConfigTx::ConfigVote(ref msg) => msg.from(),
        }
    }

    pub fn height(&self) -> u64 {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.height(),
            ConfigTx::ConfigVote(ref msg) => msg.height(),
        }
    }
}

impl fmt::Debug for ConfigTx {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => write!(fmt, "{:?}", msg),
            ConfigTx::ConfigVote(ref msg) => write!(fmt, "{:?}", msg),
        }
    }
}

impl FromRaw for ConfigTx {
    fn from_raw(raw: RawMessage) -> Result<ConfigTx, MessageError> {
        match raw.message_type() {
            CONFIG_PROPOSE_MESSAGE_ID => {
                Ok(ConfigTx::ConfigPropose(TxConfigPropose::from_raw(raw)?))
            }
            CONFIG_VOTE_MESSAGE_ID => Ok(ConfigTx::ConfigVote(TxConfigVote::from_raw(raw)?)),
            _ => Err(MessageError::IncorrectMessageType { message_type: raw.message_type() }),
        }
    }
}

impl Message for ConfigTx {
    fn raw(&self) -> &RawMessage {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.raw(),
            ConfigTx::ConfigVote(ref msg) => msg.raw(),
        }
    }

    fn verify_signature(&self, public_key: &PublicKey) -> bool {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.verify_signature(public_key),
            ConfigTx::ConfigVote(ref msg) => msg.verify_signature(public_key),
        }
    }

    fn hash(&self) -> Hash {
        match *self {
            ConfigTx::ConfigPropose(ref msg) => msg.hash(),
            ConfigTx::ConfigVote(ref msg) => msg.hash(),
        }
    }
}

impl<'a> ConfigurationSchema<'a> {
    pub fn new(view: &'a View) -> ConfigurationSchema {
        ConfigurationSchema { view: view }
    }

    pub fn config_proposes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, Hash, TxConfigPropose> {
        // config_propose paricia merkletree <hash_tx> транзакция пропоз
        MerklePatriciaTable::new(MapTable::new(vec![04], self.view))
    }

    pub fn config_votes
        (&self)
         -> MerklePatriciaTable<MapTable<View, [u8], Vec<u8>>, PublicKey, TxConfigVote> {
        // config_votes patricia merkletree <pub_key> последний голос
        MerklePatriciaTable::new(MapTable::new(vec![05], self.view))
    }

    pub fn state_hash(&self) -> StorageResult<Vec<Hash>> {
        Ok(vec![self.config_proposes().root_hash()?, self.config_votes().root_hash()?])
    }

    pub fn get_config_propose(&self,
                              hash: &Hash)
                              -> Result<Option<TxConfigPropose>, exonum::storage::Error> {
        self.config_proposes().get(hash)
    }

    pub fn get_vote(&self,
                    pub_key: &PublicKey)
                    -> Result<Option<TxConfigVote>, exonum::storage::Error> {
        self.config_votes().get(pub_key)
    }
}

impl TxConfigPropose {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let config = blockchain_schema.get_actual_configuration()?;
        if !config.validators.contains(self.from()) {
            error!("ConfigPropose from unknown validator: {:?}", self.from());
            return Ok(());
        }

        let hash = self.hash();
        if config_schema.config_proposes().get(&hash)?.is_some() {
            error!("Received config_propose has already been handled, msg={:?}",
                   self);
            return Ok(());
        }

        trace!("Handle ConfigPropose");
        config_schema.config_proposes().put(&hash, self.clone())?;
        Ok(())
    }
}

impl TxConfigVote {
    fn execute(&self, view: &View) -> StorageResult<()> {
        let blockchain_schema = Schema::new(view);
        let config_schema = ConfigurationSchema::new(view);

        let config = blockchain_schema.get_actual_configuration()?;

        if !config.validators.contains(self.from()) {
            error!("ConfigVote from unknown validator: {:?}", self.from());
            return Ok(());
        }

        if config_schema.config_proposes().get(self.hash_propose())?.is_none() {
            error!("Received config_vote for unknown transaciton, msg={:?}",
                   self);
            return Ok(());
        }

        if let Some(vote) = config_schema.config_votes().get(self.from())? {
            if vote.seed() != self.seed() - 1 {
                error!("Received config_vote with wrong seed, msg={:?}", self);
                return Ok(());
            }
        }

        let msg = self.clone();
        config_schema.config_votes().put(msg.from(), self.clone())?;
        info!("posted tx: {:?}", self.clone());

        let mut votes_count = 0;
        for pub_key in config.validators.clone() {
            if let Some(vote) = config_schema.config_votes().get(&pub_key)? {
                if !vote.revoke() {
                    votes_count += 1;
                }
            }
        }

        info!("majority count: {}", 2 * config.validators.len() /3);
        if votes_count > 2 * config.validators.len() / 3 {
            if let Some(config_propose) =
                config_schema.config_proposes()
                    .get(self.hash_propose())? {
                blockchain_schema.commit_actual_configuration(config_propose.actual_from_height(),
                                                 config_propose.config())?;
            }
        }
        Ok(())
    }
}

impl Transaction for ConfigTx {
    fn verify(&self) -> bool {
        self.verify_signature(self.from())
    }

    fn execute(&self, view: &View) -> StorageResult<()> {
        match *self {
            ConfigTx::ConfigPropose(ref tx) => tx.execute(view),
            ConfigTx::ConfigVote(ref tx) => tx.execute(view),
        }
    }
}

impl ConfigurationService {
    pub fn new() -> ConfigurationService {
        ConfigurationService {}
    }
}

impl Service for ConfigurationService {
    fn service_id(&self) -> u16 {
        CONFIG_SERVICE
    }

    fn state_hash(&self, view: &View) -> StorageResult<Vec<Hash>> {
        let schema = ConfigurationSchema::new(view);
        schema.state_hash()
    }

    fn tx_from_raw(&self, raw: RawTransaction) -> Result<Box<Transaction>, MessageError> {
        ConfigTx::from_raw(raw).map(|tx| Box::new(tx) as Box<Transaction>)
    }

    fn handle_commit(&self, state: &mut NodeState) -> StorageResult<()> {
        let old_cfg = state.actual_config().clone();

        let new_cfg = {
            let schema = Schema::new(state.view());
            schema.get_actual_configuration()?
        };

        if new_cfg != old_cfg {
            info!("Updated node config={:#?}", new_cfg);
            state.update_config(new_cfg);
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>#![feature(unboxed_closures, macro_rules, globs)]

extern crate shader_version;
extern crate event;
extern crate event_loop;
extern crate conrod;
extern crate graphics;
extern crate window;
extern crate opengl_graphics;
extern crate current;
extern crate sdl2_window;

use conrod::*;

use current::Set;
use event::Event;
use event_loop::{Events, Ups, MaxFps};
use opengl_graphics::Gl;
use opengl_graphics::glyph_cache::GlyphCache as Font;
use shader_version::opengl::OpenGL;
use sdl2_window::Sdl2Window;
use window::WindowSettings;

use std::borrow::ToOwned;
use std::default::Default;
use std::io::fs::{mod, PathExtensions};
use std::io::IoResult;
use std::thread::{Thread, JoinGuard};
use std::os;

pub struct FileDialog {
    title: String,
    dimen: [u32, ..2],
    samples: u8,
    background: Color,
    select: SelectType,
    starting_path: Path,
    font: Font,
    filter_hidden: bool,
}

impl FileDialog {
    pub fn new<'a, S: IntoCow<'a, String, str>>(title: S, font: Font) -> FileDialog {
        FileDialog {
            title: title.into_cow().into_owned(),
            dimen: [640, 480],
            samples: 4,
            background: Color::new(0.9, 0.9, 0.9, 1.0), // Should be a nice light-grey
            select: SelectType::File,
            // Possible panic! here, but unlikely.
            starting_path: os::homedir().unwrap_or_else(|| os::getcwd().unwrap()),
            font: font,
            filter_hidden: true,
        }
    }

    pub fn width(mut self, width: u32) -> FileDialog {
        self.dimen[0] = width;
        self
    }

    pub fn height(mut self, height: u32) -> FileDialog {
        self.dimen[1] = height;
        self
    }

    pub fn dimensions(mut self, width: u32, height: u32) -> FileDialog {
        self.dimen = [width, height];
        self
    }

    pub fn set_samples(mut self, samples: u8) -> FileDialog {
        self.samples = samples;
        self
    }

    pub fn set_background(mut self, background: Color) -> FileDialog {
        self.background = background;
        self
    }

    pub fn set_select(mut self, select: SelectType) -> FileDialog {
        self.select = select;
        self
    }

    pub fn set_starting_path(mut self, starting_path: Path) -> FileDialog {
        self.starting_path = starting_path;
        self
    }

    pub fn set_filter_hidden(mut self, filter_hidden: bool) -> FileDialog {
        self.filter_hidden = filter_hidden;
        self
    }

    // How should we format the trait bounds here?
    /// Show the dialog
    pub fn show(self, gl: OpenGL) -> JoinGuard<Option<Path>> {
        let dialog = DialogSettings {
            background: self.background,
            select: self.select,
            starting_path: self.starting_path,
            font: self.font,
            filter_hidden: self.filter_hidden,
        };

        let window =  WindowSettings {
            title: self.title,
            size: self.dimen,
            samples: self.samples,
            fullscreen: false,
            exit_on_esc: true,
        };

        Thread::spawn(move || render_file_dialog(dialog, window, gl))
    }
}

/// An enum describing the file selection behavior.
#[deriving(PartialEq, Eq)]
pub enum SelectType {
    /// User must select an existing file on the filesystem.
    File,
    /// User must select an existing folder on the filesystem, or create a new one.
    Folder,
    /// User must select the location to save a file, and the filename.
    /// The contained `Option<String>` is the default filename, if any.
    SaveFile(Option<String>),
}

impl SelectType {
    fn show_files(&self) -> bool {
        match *self {
            SelectType::File | SelectType::SaveFile(_) => true,
            _ => false,
        }
    }
}

struct DialogSettings {
    background: Color,
    select: SelectType,
    starting_path: Path,
    font: Font,
    filter_hidden: bool,
}

impl DialogSettings {
    fn into_state(self) -> (DialogState, Font) {
        (
            DialogState {
                dir: self.starting_path,
                selected: None,
                result: None,
                exit: false,
                paths: Vec::new(),
                background: self.background,
                select: self.select,
                dir_changed: true,
                pages: 0,
                cur_page: 0,
                filter_hidden: self.filter_hidden,
            },
            self.font,
        )
    }
}

fn render_file_dialog(dialog: DialogSettings, window: WindowSettings, gl: OpenGL) -> Option<Path> {
    let (mut state, font) = dialog.into_state();
    state.update_paths();

    let window = Sdl2Window::new(gl, window);
    let mut event_loop = Events::new(window).set(Ups(120)).set(MaxFps(60));
    let mut gl = Gl::new(gl);

    let ref mut uic = UiContext::new(font, Theme::default());
    let ref mut buf: Buffers = Default::default();

    if let SelectType::SaveFile(ref mut opt_file) = state.select {
        opt_file.take().map(|s: String| buf.filename = s);
    }

    for event in event_loop {
        if state.exit { break; }

        uic.handle_event(&event);
        match event {
            Event::Render(args) => {
                gl.draw([0, 0, args.width as i32, args.height as i32], |_, gl| {
                    draw_dialog_ui(gl, uic, &mut state, buf);
                });
            },
            _ => {}
        }
    }

    state.result
}

/// Like format! except writes to an existing string.
macro_rules! write_str(
    ($s:expr, $fmt:expr, $($arg:expr),+) => (
        {
            let vec = unsafe { $s.as_mut_vec() };
            // Should always be `Ok(())` unless something went wrong.
            (write!(vec, $fmt, $($arg),+)).unwrap();
        }
    )
);

#[deriving(Default)]
struct Buffers {
    dir: String,
    page: String,
    filename: String,
    selected: String,
}

impl Buffers {
    fn set_page(&mut self, page: uint, total: uint) {
        self.page.clear();
        write_str!(self.page, "Page: {} Total: {}", page, total);
    }

    fn set_selected(&mut self, selected: &Path) {
        self.selected.clear();
        write_str!(self.selected, "Selected: {}", selected.filename_display());
    }

    fn set_dir(&mut self, dir: &Path) {
        self.dir.clear();
        write_str!(self.dir, "{}", dir.display());
    }
}

struct DialogState {
    dir: Path,
    selected: Option<uint>,
    result: Option<Path>,
    exit: bool,
    paths: Vec<Path>,
    background: Color,
    select: SelectType,
    dir_changed: bool,
    pages: uint,
    cur_page: uint,
    filter_hidden: bool,
}

impl DialogState {
    fn update_dir(&mut self, new_dir: Path) {
        self.dir_changed = if new_dir.is_dir() {
            self.dir = new_dir;
            self.update_paths();
            true
        } else {
            false
        }
    }

    fn update_paths(&mut self) {
        self.selected = None;
        self.paths = entries(&self.dir, self.select.show_files(), self.filter_hidden).unwrap();

        let count = self.paths.len();
        self.pages = count / PER_PAGE;
        self.cur_page = 1;

        if count % PER_PAGE != 0 { self.pages += 1; }
    }

    fn up_dir(&mut self) {
        self.dir_changed = self.dir.pop();
        self.update_paths();
    }

    fn select(&mut self, num: uint, buf: &mut Buffers) {
        // Double-clicked
        if self.selected == Some(num) {
            let path = self.paths.remove(num).unwrap();

            if self.select.show_files() && !path.is_dir() {
                self.result = Some(path);
                self.exit = true;
            } else {
                self.update_dir(path);
            }
        } else {
            self.selected = Some(num);
            buf.set_selected(&self.paths[num]);
        }
    }

    fn save(&mut self, filename: &str) {
        self.result = Some(self.dir.join(filename));
        self.exit = true;
    }

    fn next_page(&mut self, buf: &mut Buffers) {
        if self.cur_page < self.pages {
            self.cur_page += 1;
            buf.set_page(self.cur_page, self.pages);
        }
    }

    fn prev_page(&mut self, buf: &mut Buffers) {
        if self.cur_page > 1 {
            self.cur_page -= 1;
            buf.set_page(self.cur_page, self.pages);
        }
    }
}

const COLS: uint = 5;
const ROWS: uint = 8;
const PER_PAGE: uint = COLS * ROWS;
const CHAR_LIMIT: uint = 24;

fn draw_dialog_ui(gl: &mut Gl, uic: &mut UiContext, state: &mut DialogState, buf: &mut Buffers) {
    uic.background().color(state.background).draw(gl);

    if state.dir_changed {
        buf.set_dir(&state.dir);
        buf.set_page(state.cur_page, state.pages);
        state.dir_changed = false;
    }

    const UP_DIR: u64 = 78;

    uic.button(UP_DIR)
        .position(605.0, 5.0)
        .dimensions(30.0, 30.0)
        .label("Up")
        .callback(|| state.up_dir())
        .draw(gl);

    uic.label(&*buf.dir)
        .position(5.0, 5.0)
        .size(24)
        .draw(gl);

    const PREV_PAGE: u64 = 199;

    uic.button(PREV_PAGE)
        .position(5.0, 445.0)
        .dimensions(90.0, 30.0)
        .label("Previous")
        .callback(|| state.prev_page(buf))
        .draw(gl);

    const NEXT_PAGE: u64 = PREV_PAGE + 1;

    uic.button(NEXT_PAGE)
        .right_from(PREV_PAGE, 125.0)
        .dimensions(90.0, 30.0)
        .label("Next")
        .callback(|| state.next_page(buf))
        .draw(gl);

    uic.label(&*buf.page)
        .right_from(PREV_PAGE, 15.0)
        .size(24)
        .draw(gl);

    const FILE_START_ID: u64 = 365;
    uic.widget_matrix(COLS, ROWS)
        .position(5.0, 35.0)
        .dimensions(600.0, 400.0)
        .cell_padding(5.0, 5.0)
        .each_widget(|uic, _, x, y, pt, dimen| {
            use std::cmp;
            let idx = (y * COLS * state.cur_page) + x;

            if idx >= state.paths.len() { return; }

            let label = state.paths[idx].filename_str().unwrap().to_owned();

            let button = uic.button(FILE_START_ID + idx as u64)
                .point(pt)
                .dimensions(dimen[0], dimen[1])
                .label(label.slice_to(cmp::min(label.len(), CHAR_LIMIT))).label_font_size(18);

                if state.selected == Some(idx) {
                    button.color(Color::new(0.5, 0.9, 0.5, 1.0))
                } else { button }
                .callback(|| state.select(idx, buf))
                .draw(gl);
        });

    const CANCEL: u64 = 205;
    const CONFIRM: u64 = 206;

    uic.button(CANCEL)
        .right_from(NEXT_PAGE, 140.0)
        .dimensions(90.0, 30.0)
        .callback(|| state.exit = true)
        .label("Cancel")
        .draw(gl);

    {
        let confirm = uic.button(CONFIRM)
            .right_from(CANCEL, 5.0)
            .dimensions(90.0, 30.0);

        if let Some(idx) = state.selected {
            confirm.label(match state.select {
                    SelectType::SaveFile(_) if !state.paths[idx].is_dir() => "Save",
                    _ => "Open",
                })
                .callback(|| state.select(idx, buf))
                .draw(gl);
        } else if state.select == SelectType::Folder {
            confirm.label("Select Folder")
                .callback(|| {
                    state.result = Some(state.dir.clone());
                    state.exit = true
                })
                .draw(gl);
        } else if let SelectType::SaveFile(_) = state.select {
            let confirm = confirm.label("Save");

            if !buf.filename.is_empty() {
                confirm.callback(|| state.save(&*buf.filename))
            } else {
                confirm
            }.draw(gl);
        } else {
            confirm.label("Open").draw(gl);
        }
    }

    const FILENAME: u64 = 210;
    if let SelectType::SaveFile(_) = state.select {
        uic.text_box(FILENAME, &mut buf.filename)
            .right_from(NEXT_PAGE, 5.0)
            .dimensions(130.0, 30.0)
            .draw(gl);
    } else if let Some(_) = state.selected {
        uic.label(&*buf.selected)
            .right_from(NEXT_PAGE, 5.0)
            .size(18)
            .draw(gl);
    }
}

fn entries(path: &Path, keep_files: bool, filter_hidden: bool) -> IoResult<Vec<Path>> {
    let mut entries = try!(fs::readdir(path));
    entries.retain(|file|
        (filter_hidden && !file.filename_str().unwrap().starts_with(".")) &&
        (keep_files || file.is_dir())
    );
    entries.sort();

    Ok(entries)
}

<commit_msg>Fix breakage with OIBIT for Send<commit_after>#![feature(unboxed_closures, macro_rules, globs)]

extern crate shader_version;
extern crate event;
extern crate event_loop;
extern crate conrod;
extern crate graphics;
extern crate window;
extern crate opengl_graphics;
extern crate current;
extern crate sdl2_window;

use conrod::*;

use current::Set;
use event::Event;
use event_loop::{Events, Ups, MaxFps};
use opengl_graphics::Gl;
use opengl_graphics::glyph_cache::GlyphCache as Font;
use shader_version::opengl::OpenGL;
use sdl2_window::Sdl2Window;
use window::WindowSettings;

use std::borrow::ToOwned;
use std::default::Default;
use std::io::fs::{mod, PathExtensions};
use std::io::IoResult;
use std::thread::{Thread, JoinGuard};
use std::os;

pub struct FileDialog {
    title: String,
    dimen: [u32, ..2],
    samples: u8,
    background: Color,
    select: SelectType,
    starting_path: Path,
    font: Font,
    filter_hidden: bool,
}

impl FileDialog {
    pub fn new<'a, S: IntoCow<'a, String, str>>(title: S, font: Font) -> FileDialog {
        FileDialog {
            title: title.into_cow().into_owned(),
            dimen: [640, 480],
            samples: 4,
            background: Color::new(0.9, 0.9, 0.9, 1.0), // Should be a nice light-grey
            select: SelectType::File,
            // Possible panic! here, but unlikely.
            starting_path: os::homedir().unwrap_or_else(|| os::getcwd().unwrap()),
            font: font,
            filter_hidden: true,
        }
    }

    pub fn width(mut self, width: u32) -> FileDialog {
        self.dimen[0] = width;
        self
    }

    pub fn height(mut self, height: u32) -> FileDialog {
        self.dimen[1] = height;
        self
    }

    pub fn dimensions(mut self, width: u32, height: u32) -> FileDialog {
        self.dimen = [width, height];
        self
    }

    pub fn set_samples(mut self, samples: u8) -> FileDialog {
        self.samples = samples;
        self
    }

    pub fn set_background(mut self, background: Color) -> FileDialog {
        self.background = background;
        self
    }

    pub fn set_select(mut self, select: SelectType) -> FileDialog {
        self.select = select;
        self
    }

    pub fn set_starting_path(mut self, starting_path: Path) -> FileDialog {
        self.starting_path = starting_path;
        self
    }

    pub fn set_filter_hidden(mut self, filter_hidden: bool) -> FileDialog {
        self.filter_hidden = filter_hidden;
        self
    }

    // How should we format the trait bounds here?
    /// Show the dialog
    pub fn show(self, gl: OpenGL) -> JoinGuard<Option<Path>> {

        Thread::spawn(move || {
            let dialog = DialogSettings {
                background: self.background,
                select: self.select,
                starting_path: self.starting_path,
                font: self.font,
                filter_hidden: self.filter_hidden,
            };

            let window =  WindowSettings {
                title: self.title,
                size: self.dimen,
                samples: self.samples,
                fullscreen: false,
                exit_on_esc: true,
            };

            render_file_dialog(dialog, window, gl)
        })
    }
}

unsafe impl Send for FileDialog {}

/// An enum describing the file selection behavior.
#[deriving(PartialEq, Eq)]
pub enum SelectType {
    /// User must select an existing file on the filesystem.
    File,
    /// User must select an existing folder on the filesystem, or create a new one.
    Folder,
    /// User must select the location to save a file, and the filename.
    /// The contained `Option<String>` is the default filename, if any.
    SaveFile(Option<String>),
}

impl SelectType {
    fn show_files(&self) -> bool {
        match *self {
            SelectType::File | SelectType::SaveFile(_) => true,
            _ => false,
        }
    }
}

struct DialogSettings {
    background: Color,
    select: SelectType,
    starting_path: Path,
    font: Font,
    filter_hidden: bool,
}

impl DialogSettings {
    fn into_state(self) -> (DialogState, Font) {
        (
            DialogState {
                dir: self.starting_path,
                selected: None,
                result: None,
                exit: false,
                paths: Vec::new(),
                background: self.background,
                select: self.select,
                dir_changed: true,
                pages: 0,
                cur_page: 0,
                filter_hidden: self.filter_hidden,
            },
            self.font,
        )
    }
}

fn render_file_dialog(dialog: DialogSettings, window: WindowSettings, gl: OpenGL) -> Option<Path> {
    let (mut state, font) = dialog.into_state();
    state.update_paths();

    let window = Sdl2Window::new(gl, window);
    let mut event_loop = Events::new(window).set(Ups(120)).set(MaxFps(60));
    let mut gl = Gl::new(gl);

    let ref mut uic = UiContext::new(font, Theme::default());
    let ref mut buf: Buffers = Default::default();

    if let SelectType::SaveFile(ref mut opt_file) = state.select {
        opt_file.take().map(|s: String| buf.filename = s);
    }

    for event in event_loop {
        if state.exit { break; }

        uic.handle_event(&event);
        match event {
            Event::Render(args) => {
                gl.draw([0, 0, args.width as i32, args.height as i32], |_, gl| {
                    draw_dialog_ui(gl, uic, &mut state, buf);
                });
            },
            _ => {}
        }
    }

    state.result
}

/// Like format! except writes to an existing string.
macro_rules! write_str(
    ($s:expr, $fmt:expr, $($arg:expr),+) => (
        {
            let vec = unsafe { $s.as_mut_vec() };
            // Should always be `Ok(())` unless something went wrong.
            (write!(vec, $fmt, $($arg),+)).unwrap();
        }
    )
);

#[deriving(Default)]
struct Buffers {
    dir: String,
    page: String,
    filename: String,
    selected: String,
}

impl Buffers {
    fn set_page(&mut self, page: uint, total: uint) {
        self.page.clear();
        write_str!(self.page, "Page: {} Total: {}", page, total);
    }

    fn set_selected(&mut self, selected: &Path) {
        self.selected.clear();
        write_str!(self.selected, "Selected: {}", selected.filename_display());
    }

    fn set_dir(&mut self, dir: &Path) {
        self.dir.clear();
        write_str!(self.dir, "{}", dir.display());
    }
}

struct DialogState {
    dir: Path,
    selected: Option<uint>,
    result: Option<Path>,
    exit: bool,
    paths: Vec<Path>,
    background: Color,
    select: SelectType,
    dir_changed: bool,
    pages: uint,
    cur_page: uint,
    filter_hidden: bool,
}

impl DialogState {
    fn update_dir(&mut self, new_dir: Path) {
        self.dir_changed = if new_dir.is_dir() {
            self.dir = new_dir;
            self.update_paths();
            true
        } else {
            false
        }
    }

    fn update_paths(&mut self) {
        self.selected = None;
        self.paths = entries(&self.dir, self.select.show_files(), self.filter_hidden).unwrap();

        let count = self.paths.len();
        self.pages = count / PER_PAGE;
        self.cur_page = 1;

        if count % PER_PAGE != 0 { self.pages += 1; }
    }

    fn up_dir(&mut self) {
        self.dir_changed = self.dir.pop();
        self.update_paths();
    }

    fn select(&mut self, num: uint, buf: &mut Buffers) {
        // Double-clicked
        if self.selected == Some(num) {
            let path = self.paths.remove(num).unwrap();

            if self.select.show_files() && !path.is_dir() {
                self.result = Some(path);
                self.exit = true;
            } else {
                self.update_dir(path);
            }
        } else {
            self.selected = Some(num);
            buf.set_selected(&self.paths[num]);
        }
    }

    fn save(&mut self, filename: &str) {
        self.result = Some(self.dir.join(filename));
        self.exit = true;
    }

    fn next_page(&mut self, buf: &mut Buffers) {
        if self.cur_page < self.pages {
            self.cur_page += 1;
            buf.set_page(self.cur_page, self.pages);
        }
    }

    fn prev_page(&mut self, buf: &mut Buffers) {
        if self.cur_page > 1 {
            self.cur_page -= 1;
            buf.set_page(self.cur_page, self.pages);
        }
    }
}

const COLS: uint = 5;
const ROWS: uint = 8;
const PER_PAGE: uint = COLS * ROWS;
const CHAR_LIMIT: uint = 24;

fn draw_dialog_ui(gl: &mut Gl, uic: &mut UiContext, state: &mut DialogState, buf: &mut Buffers) {
    uic.background().color(state.background).draw(gl);

    if state.dir_changed {
        buf.set_dir(&state.dir);
        buf.set_page(state.cur_page, state.pages);
        state.dir_changed = false;
    }

    const UP_DIR: u64 = 78;

    uic.button(UP_DIR)
        .position(605.0, 5.0)
        .dimensions(30.0, 30.0)
        .label("Up")
        .callback(|| state.up_dir())
        .draw(gl);

    uic.label(&*buf.dir)
        .position(5.0, 5.0)
        .size(24)
        .draw(gl);

    const PREV_PAGE: u64 = 199;

    uic.button(PREV_PAGE)
        .position(5.0, 445.0)
        .dimensions(90.0, 30.0)
        .label("Previous")
        .callback(|| state.prev_page(buf))
        .draw(gl);

    const NEXT_PAGE: u64 = PREV_PAGE + 1;

    uic.button(NEXT_PAGE)
        .right_from(PREV_PAGE, 125.0)
        .dimensions(90.0, 30.0)
        .label("Next")
        .callback(|| state.next_page(buf))
        .draw(gl);

    uic.label(&*buf.page)
        .right_from(PREV_PAGE, 15.0)
        .size(24)
        .draw(gl);

    const FILE_START_ID: u64 = 365;
    uic.widget_matrix(COLS, ROWS)
        .position(5.0, 35.0)
        .dimensions(600.0, 400.0)
        .cell_padding(5.0, 5.0)
        .each_widget(|uic, _, x, y, pt, dimen| {
            use std::cmp;
            let idx = (y * COLS * state.cur_page) + x;

            if idx >= state.paths.len() { return; }

            let label = state.paths[idx].filename_str().unwrap().to_owned();

            let button = uic.button(FILE_START_ID + idx as u64)
                .point(pt)
                .dimensions(dimen[0], dimen[1])
                .label(label.slice_to(cmp::min(label.len(), CHAR_LIMIT))).label_font_size(18);

                if state.selected == Some(idx) {
                    button.color(Color::new(0.5, 0.9, 0.5, 1.0))
                } else { button }
                .callback(|| state.select(idx, buf))
                .draw(gl);
        });

    const CANCEL: u64 = 205;
    const CONFIRM: u64 = 206;

    uic.button(CANCEL)
        .right_from(NEXT_PAGE, 140.0)
        .dimensions(90.0, 30.0)
        .callback(|| state.exit = true)
        .label("Cancel")
        .draw(gl);

    {
        let confirm = uic.button(CONFIRM)
            .right_from(CANCEL, 5.0)
            .dimensions(90.0, 30.0);

        if let Some(idx) = state.selected {
            confirm.label(match state.select {
                    SelectType::SaveFile(_) if !state.paths[idx].is_dir() => "Save",
                    _ => "Open",
                })
                .callback(|| state.select(idx, buf))
                .draw(gl);
        } else if state.select == SelectType::Folder {
            confirm.label("Select Folder")
                .callback(|| {
                    state.result = Some(state.dir.clone());
                    state.exit = true
                })
                .draw(gl);
        } else if let SelectType::SaveFile(_) = state.select {
            let confirm = confirm.label("Save");

            if !buf.filename.is_empty() {
                confirm.callback(|| state.save(&*buf.filename))
            } else {
                confirm
            }.draw(gl);
        } else {
            confirm.label("Open").draw(gl);
        }
    }

    const FILENAME: u64 = 210;
    if let SelectType::SaveFile(_) = state.select {
        uic.text_box(FILENAME, &mut buf.filename)
            .right_from(NEXT_PAGE, 5.0)
            .dimensions(130.0, 30.0)
            .draw(gl);
    } else if let Some(_) = state.selected {
        uic.label(&*buf.selected)
            .right_from(NEXT_PAGE, 5.0)
            .size(18)
            .draw(gl);
    }
}

fn entries(path: &Path, keep_files: bool, filter_hidden: bool) -> IoResult<Vec<Path>> {
    let mut entries = try!(fs::readdir(path));
    entries.retain(|file|
        (filter_hidden && !file.filename_str().unwrap().starts_with(".")) &&
        (keep_files || file.is_dir())
    );
    entries.sort();

    Ok(entries)
}

<|endoftext|>


<commit_before>#![feature(old_io)]
#![feature(old_path)]

extern crate time;

use std::old_io::IoError;
use std::old_io::net::udp::UdpSocket;
use std::old_io::net::ip::{ Ipv4Addr, SocketAddr };
use std::result;
use time::Tm;
use std::old_io::net::pipe::UnixStream;

/// A type alias for `Result<(), IoError>`, the result of writing a log message
pub type Result = result::Result<(), IoError>;

static NIL: &'static str = "-";

/// Syslog Facilites as defined by [rfc5424](https://tools.ietf.org/html/rfc5424#page-10)
#[derive(Copy,Clone)]
pub enum Facility {
  KERN     = 0,
  USER     = 1 << 3,
  MAIL     = 2 << 3,
  DAEMON   = 3 << 3,
  AUTH     = 4 << 3,
  SYSLOG   = 5 << 3,
  LINEPTR  = 6 << 3,
  NEWS     = 7 << 3,
  UUCP     = 8 << 3,
  CLOCK    = 9 << 3,
  AUTHPRIV = 10 << 3,
  FTP      = 11 << 3,
  LOCAL0   = 16 << 3,
  LOCAL1   = 17 << 3,
  LOCAL2   = 18 << 3,
  LOCAL3   = 19 << 3,
  LOCAL4   = 20 << 3,
  LOCAL5   = 21 << 3,
  LOCAL6   = 22 << 3,
  LOCAL7   = 23 << 3
}

/// Syslog Severity as defined by [rfc5424](https://tools.ietf.org/html/rfc5424#page-11)
pub enum Severity {
  EMERGENCY,
  ALERT,
  CRITICAL,
  ERROR,
  WARNING,
  NOTICE,
  INFO,
  DEBUG
}

trait Transport {
  fn send(&mut self, line: &str) -> Result;
}

impl Transport for (UdpSocket, SocketAddr) {
  fn send(&mut self, line: &str) -> Result {
    self.0.send_to(line.as_bytes(), self.1)
  }
}

impl Transport for UnixStream {
  fn send(&mut self, line: &str) -> Result {
    self.write_line(line)
  }
}

/// A rust interface for Syslog, a standard unix system logging service
pub struct Syslog {
  /// A Syslog facility to target when logging
  facility: Facility,
  host: Option<String>,
  /// An optional tag append to Syslog messages as defined by
  /// [rfc5424](https://tools.ietf.org/html/rfc5424#section-6.2.5)
  tag: Option<String>,
  transport: Box<Transport>
}

impl Syslog {
   /// Factory for a Syslog appender that writes to
   /// remote Syslog daemon listening a SocketAddr
   pub fn udp(host: SocketAddr) -> Syslog {
     let socket =
       match UdpSocket::bind(SocketAddr { ip: Ipv4Addr(0,0,0,0), port: 0 }) {
         Err(e) => panic!("error binding to local addr {}", e),
         Ok(s) => s
       };
      let tup = (socket, host);
      Syslog {
        facility: Facility::USER,
        host: None,
        tag: None,
        transport: Box::new(tup)
      }
  }

  /// Same as udp with providing local loopback address with the standard syslog port
  pub fn localudp() -> Syslog {
    Syslog::udp(SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 514 })
  }

  /// Factory for a Syslog appender that writes
  /// to a host-local Syslog daemon listening on a unix socket domain
  /// hosted at the given Path
  pub fn unix(path: Path) -> Syslog {
    let stream =
      match UnixStream::connect(&path) {
        Err(_) => panic!("failed to connect to socket"),
        Ok(s)  => s
      };
    Syslog {
      facility: Facility::USER,
      host: None,
      tag: None,
      transport: Box::new(stream)
    }
  }
  /// Returns a new Syslog appender configured to append with
  /// the provided Facility
  pub fn facility(self, facility: Facility) -> Syslog {
    Syslog {
      facility: facility,
      host: None,
      tag: self.tag,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender configured to append with
  /// the provided host addr
  pub fn host(self, local: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: Some(local.to_string()),
      tag: self.tag,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender, configured to append with
  /// the provided tag
  pub fn tag(self, tag: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: None,
      tag: Some(tag.to_string()),
      transport: self.transport
    }
  }

  pub fn debug(&mut self, msg: &str) -> Result {
    self.log(Severity::DEBUG, msg)
  }

  pub fn info(&mut self, msg: &str) -> Result {
    self.log(Severity::INFO, msg)
  }

  pub fn notice(&mut self, msg: &str) -> Result {
    self.log(Severity::NOTICE, msg)
  }

  pub fn warn(&mut self, msg: &str) -> Result {
    self.log(Severity::WARNING, msg)
  }

  pub fn err(&mut self, msg: &str) -> Result {
    self.log(Severity::ERROR, msg)
  }

  pub fn critical(&mut self, msg: &str) -> Result {
    self.log(Severity::CRITICAL, msg)
  }

  pub fn alert(&mut self, msg: &str) -> Result {
    self.log(Severity::ALERT, msg)
  }

  pub fn emergency(&mut self, msg: &str) -> Result {
    self.log(Severity::EMERGENCY, msg)
  }

  fn log(&mut self, severity: Severity,  msg: &str) -> Result {
    let formatted = Syslog::line(self.facility.clone(), severity, time::now(), self.host.clone(), self.tag.clone(), msg);
    self.transport.send(&formatted)
  }

  fn line(facility: Facility, severity: Severity, timestamp: Tm, host: Option<String>, tag: Option<String>, msg: &str) -> String {
    format!(
      "<{:?}> {} {} {} {}",
        Syslog::priority(facility, severity), timestamp.rfc3339(),
        host.unwrap_or(NIL.to_string()), tag.unwrap_or(NIL.to_string()), msg)
  }

  // computes the priority of a message based on a facility and severity
  fn priority(facility: Facility, severity: Severity) -> u8 {
    facility as u8 | severity as u8
  }
}

#[cfg(test)]
mod tests {
  use super::{Syslog, Facility, NIL, Severity};
  use time;
  #[test]
  fn test_syslog_line_defaults() {
    let ts = time::now();
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, "yo"),
      format!("<134> {} {} {} yo", ts.rfc3339(), NIL, NIL));
  }

  #[test]
  fn test_syslog_line_host() {
    let ts = time::now();
    let host = "foo.local";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, Some(host.to_string()), None, "yo"),
      format!("<134> {} {} {} yo", ts.rfc3339(), host, NIL));
  }

  #[test]
  fn test_syslog_line_tag() {
    let ts = time::now();
    let app = "sysly";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, Some(app.to_string()), "yo"),
      format!("<134> {} {} {} yo", ts.rfc3339(), NIL, app));
  }
}
<commit_msg>not propegating host field<commit_after>#![feature(old_io)]
#![feature(old_path)]

extern crate time;

use std::old_io::IoError;
use std::old_io::net::udp::UdpSocket;
use std::old_io::net::ip::{ Ipv4Addr, SocketAddr };
use std::result;
use time::Tm;
use std::old_io::net::pipe::UnixStream;

/// A type alias for `Result<(), IoError>`, the result of writing a log message
pub type Result = result::Result<(), IoError>;

static NIL: &'static str = "-";

/// Syslog Facilites as defined by [rfc5424](https://tools.ietf.org/html/rfc5424#page-10)
#[derive(Copy,Clone)]
pub enum Facility {
  KERN     = 0,
  USER     = 1 << 3,
  MAIL     = 2 << 3,
  DAEMON   = 3 << 3,
  AUTH     = 4 << 3,
  SYSLOG   = 5 << 3,
  LINEPTR  = 6 << 3,
  NEWS     = 7 << 3,
  UUCP     = 8 << 3,
  CLOCK    = 9 << 3,
  AUTHPRIV = 10 << 3,
  FTP      = 11 << 3,
  LOCAL0   = 16 << 3,
  LOCAL1   = 17 << 3,
  LOCAL2   = 18 << 3,
  LOCAL3   = 19 << 3,
  LOCAL4   = 20 << 3,
  LOCAL5   = 21 << 3,
  LOCAL6   = 22 << 3,
  LOCAL7   = 23 << 3
}

/// Syslog Severity as defined by [rfc5424](https://tools.ietf.org/html/rfc5424#page-11)
pub enum Severity {
  EMERGENCY,
  ALERT,
  CRITICAL,
  ERROR,
  WARNING,
  NOTICE,
  INFO,
  DEBUG
}

trait Transport {
  fn send(&mut self, line: &str) -> Result;
}

impl Transport for (UdpSocket, SocketAddr) {
  fn send(&mut self, line: &str) -> Result {
    self.0.send_to(line.as_bytes(), self.1)
  }
}

impl Transport for UnixStream {
  fn send(&mut self, line: &str) -> Result {
    self.write_line(line)
  }
}

/// A rust interface for Syslog, a standard unix system logging service
pub struct Syslog {
  /// A Syslog facility to target when logging
  facility: Facility,
  /// A Syslog host entry as defined by
  /// [rfc5424#section-6.2.4](https://tools.ietf.org/html/rfc5424#section-6.2.4)
  host: Option<String>,
  /// An optional tag append to Syslog messages as defined by
  /// [rfc5424#section-6.2.5](https://tools.ietf.org/html/rfc5424#section-6.2.5)
  tag: Option<String>,
  transport: Box<Transport>
}

impl Syslog {
   /// Factory for a Syslog appender that writes to
   /// remote Syslog daemon listening a SocketAddr
   pub fn udp(host: SocketAddr) -> Syslog {
     let socket =
       match UdpSocket::bind(SocketAddr { ip: Ipv4Addr(0,0,0,0), port: 0 }) {
         Err(e) => panic!("error binding to local addr {}", e),
         Ok(s) => s
       };
      let tup = (socket, host);
      Syslog {
        facility: Facility::USER,
        host: None,
        tag: None,
        transport: Box::new(tup)
      }
  }

  /// Same as udp with providing local loopback address with the standard syslog port
  pub fn localudp() -> Syslog {
    Syslog::udp(SocketAddr { ip: Ipv4Addr(127,0,0,1), port: 514 })
  }

  /// Factory for a Syslog appender that writes
  /// to a host-local Syslog daemon listening on a unix socket domain
  /// hosted at the given Path
  pub fn unix(path: Path) -> Syslog {
    let stream =
      match UnixStream::connect(&path) {
        Err(_) => panic!("failed to connect to socket"),
        Ok(s)  => s
      };
    Syslog {
      facility: Facility::USER,
      host: None,
      tag: None,
      transport: Box::new(stream)
    }
  }
  /// Returns a new Syslog appender configured to append with
  /// the provided Facility
  pub fn facility(self, facility: Facility) -> Syslog {
    Syslog {
      facility: facility,
      host: self.host,
      tag: self.tag,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender configured to append with
  /// the provided host addr
  pub fn host(self, local: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: Some(local.to_string()),
      tag: self.tag,
      transport: self.transport
    }
  }

  /// Returns a new Syslog appender, configured to append with
  /// the provided tag
  pub fn tag(self, tag: &str) -> Syslog {
    Syslog {
      facility: self.facility,
      host: self.host,
      tag: Some(tag.to_string()),
      transport: self.transport
    }
  }

  pub fn debug(&mut self, msg: &str) -> Result {
    self.log(Severity::DEBUG, msg)
  }

  pub fn info(&mut self, msg: &str) -> Result {
    self.log(Severity::INFO, msg)
  }

  pub fn notice(&mut self, msg: &str) -> Result {
    self.log(Severity::NOTICE, msg)
  }

  pub fn warn(&mut self, msg: &str) -> Result {
    self.log(Severity::WARNING, msg)
  }

  pub fn err(&mut self, msg: &str) -> Result {
    self.log(Severity::ERROR, msg)
  }

  pub fn critical(&mut self, msg: &str) -> Result {
    self.log(Severity::CRITICAL, msg)
  }

  pub fn alert(&mut self, msg: &str) -> Result {
    self.log(Severity::ALERT, msg)
  }

  pub fn emergency(&mut self, msg: &str) -> Result {
    self.log(Severity::EMERGENCY, msg)
  }

  fn log(&mut self, severity: Severity,  msg: &str) -> Result {
    let formatted = Syslog::line(self.facility.clone(), severity, time::now(), self.host.clone(), self.tag.clone(), msg);
    self.transport.send(&formatted)
  }

  fn line(facility: Facility, severity: Severity, timestamp: Tm, host: Option<String>, tag: Option<String>, msg: &str) -> String {
    format!(
      "<{:?}> {} {} {} {}",
        Syslog::priority(facility, severity), timestamp.rfc3339(),
        host.unwrap_or(NIL.to_string()), tag.unwrap_or(NIL.to_string()), msg)
  }

  // computes the priority of a message based on a facility and severity
  fn priority(facility: Facility, severity: Severity) -> u8 {
    facility as u8 | severity as u8
  }
}

#[cfg(test)]
mod tests {
  use super::{Syslog, Facility, NIL, Severity};
  use time;
  #[test]
  fn test_syslog_line_defaults() {
    let ts = time::now();
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, None, "yo"),
      format!("<134> {} {} {} yo", ts.rfc3339(), NIL, NIL));
  }

  #[test]
  fn test_syslog_line_host() {
    let ts = time::now();
    let host = "foo.local";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, Some(host.to_string()), None, "yo"),
      format!("<134> {} {} {} yo", ts.rfc3339(), host, NIL));
  }

  #[test]
  fn test_syslog_line_tag() {
    let ts = time::now();
    let app = "sysly";
    assert_eq!(Syslog::line(
      Facility::LOCAL0, Severity::INFO, ts, None, Some(app.to_string()), "yo"),
      format!("<134> {} {} {} yo", ts.rfc3339(), NIL, app));
  }
}
<|endoftext|>


<commit_before>/// Encapsulates a floating point number in the range [0, 1] including both endpoints.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Closed01<F>(F);

impl Closed01<f32> {
    #[inline(always)]
    pub fn new(f: f32) -> Closed01<f32> {
        assert!(f >= 0.0 && f <= 1.0);
        Closed01(f)
    }

    #[inline(always)]
    pub fn zero() -> Closed01<f32> {
        Closed01(0.0)
    }

    #[inline(always)]
    pub fn one() -> Closed01<f32> {
        Closed01(1.0)
    }

    #[inline(always)]
    /// Returns the smaller of the two.
    pub fn min(self, other: Closed01<f32>) -> Closed01<f32> {
        if self.0 <= other.0 {
            self
        } else {
            other
        }
    }

    #[inline(always)]
    /// Returns the greater of the two.
    pub fn max(self, other: Closed01<f32>) -> Closed01<f32> {
        if self.0 >= other.0 {
            self
        } else {
            other
        }
    }

    #[inline(always)]
    /// Returns the distance between the two numbers.
    pub fn distance(self, other: Closed01<f32>) -> Closed01<f32> {
        let d = (self.0 - other.0).abs();
        debug_assert!(d >= 0.0 && d <= 1.0);
        Closed01(d)
    }

    #[inline(always)]
    pub fn get(self) -> f32 {
        debug_assert!(self.0 >= 0.0 && self.0 <= 1.0);
        self.0
    }

    #[inline(always)]
    /// The average of two values.
    pub fn average(a: Closed01<f32>, b: Closed01<f32>) -> Closed01<f32> {
        let avg = (a.get() + b.get()) / 2.0;
        debug_assert!(avg >= 0.0 && avg <= 1.0);
        Closed01(avg)
    }

    #[inline(always)]
    pub fn scale(&self, scalar: Closed01<f32>) -> Closed01<f32> {
        let s = self.get() * scalar.get();
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }
}

#[test]
fn minmax() {
    let a = Closed01::new(0.4);
    let b = Closed01::new(0.5);
    assert_eq!(a, a.min(b));
    assert_eq!(a, b.min(a));
    assert_eq!(b, a.max(b));
    assert_eq!(b, b.max(a));
}
<commit_msg>Add Into trait<commit_after>/// Encapsulates a floating point number in the range [0, 1] including both endpoints.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Closed01<F>(F);

impl Closed01<f32> {
    #[inline(always)]
    pub fn new(f: f32) -> Closed01<f32> {
        assert!(f >= 0.0 && f <= 1.0);
        Closed01(f)
    }

    #[inline(always)]
    pub fn zero() -> Closed01<f32> {
        Closed01(0.0)
    }

    #[inline(always)]
    pub fn one() -> Closed01<f32> {
        Closed01(1.0)
    }

    #[inline(always)]
    /// Returns the smaller of the two.
    pub fn min(self, other: Closed01<f32>) -> Closed01<f32> {
        if self.0 <= other.0 {
            self
        } else {
            other
        }
    }

    #[inline(always)]
    /// Returns the greater of the two.
    pub fn max(self, other: Closed01<f32>) -> Closed01<f32> {
        if self.0 >= other.0 {
            self
        } else {
            other
        }
    }

    #[inline(always)]
    /// Returns the distance between the two numbers.
    pub fn distance(self, other: Closed01<f32>) -> Closed01<f32> {
        let d = (self.0 - other.0).abs();
        debug_assert!(d >= 0.0 && d <= 1.0);
        Closed01(d)
    }

    #[inline(always)]
    pub fn get(self) -> f32 {
        debug_assert!(self.0 >= 0.0 && self.0 <= 1.0);
        self.0
    }

    #[inline(always)]
    /// The average of two values.
    pub fn average(a: Closed01<f32>, b: Closed01<f32>) -> Closed01<f32> {
        let avg = (a.get() + b.get()) / 2.0;
        debug_assert!(avg >= 0.0 && avg <= 1.0);
        Closed01(avg)
    }

    #[inline(always)]
    pub fn scale(&self, scalar: Closed01<f32>) -> Closed01<f32> {
        let s = self.get() * scalar.get();
        debug_assert!(s >= 0.0 && s <= 1.0);
        Closed01(s)
    }
}

impl Into<f32> for Closed01<f32> {
    fn into(self) -> f32 {
        self.0
    }
}

#[test]
fn test_into() {
    assert_eq!(0.5f32, Closed01::new(0.5).into());
}

#[test]
fn test_minmax() {
    let a = Closed01::new(0.4);
    let b = Closed01::new(0.5);
    assert_eq!(a, a.min(b));
    assert_eq!(a, b.min(a));
    assert_eq!(b, a.max(b));
    assert_eq!(b, b.max(a));
}
<|endoftext|>


<commit_before>//! Linkify finds links such as URLs and email addresses in plain text.
//! It's smart about where a link ends, such as with trailing punctuation.
//!
//! Your reaction might be: "Do I need a library for this? Why not a regex?".
//! Let's look at a few cases:
//!
//! * In `http://example.com/.` the link should not include the trailing dot
//! * `http://example.com/,` should not include the trailing comma
//! * `(http://example.com/)` should not include the parens
//!
//! Seems simple enough. But then we also have these cases:
//!
//! * `https://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)` should include the trailing paren
//! * `http://üñîçøðé.com/ä` should also work for Unicode (including Emoji and Punycode)
//! * `<http://example.com/>` should not include angle brackets
//!
//! This library behaves as you'd expect in the above cases and many more.
//! It uses a simple scan with linear runtime.
//!
//! In addition to URLs, it can also find emails.
//!
//! ### Usage
//!
//! Basic usage:
//!
//! ```
//! use linkify::{LinkFinder, LinkKind};
//!
//! let input = "Have you seen http://example.com?";
//! let finder = LinkFinder::new();
//! let links: Vec<_> = finder.links(input).collect();
//!
//! assert_eq!(1, links.len());
//! let link = &links[0];
//!
//! assert_eq!("http://example.com", link.as_str());
//! assert_eq!(14, link.start());
//! assert_eq!(32, link.end());
//! assert_eq!(&LinkKind::Url, link.kind());
//! ```
//!
//! Restrict the kinds of links:
//!
//! ```
//! use linkify::{LinkFinder, LinkKind};
//!
//! let input = "http://example.com and foo@example.com";
//! let mut finder = LinkFinder::new();
//! finder.kinds(&[LinkKind::Email]);
//! let links: Vec<_> = finder.links(input).collect();
//!
//! assert_eq!(1, links.len());
//! let link = &links[0];
//! assert_eq!("foo@example.com", link.as_str());
//! assert_eq!(&LinkKind::Email, link.kind());
//! ```
//!
//! Split the text into consecutive spans (mixed links and plain text).
//!
//! ```
//! use linkify::{LinkFinder, LinkKind};
//!
//! let input = "Have you seen http://example.com?";
//! let finder = LinkFinder::new();
//! let spans: Vec<_> = finder.spans(input).collect();
//!
//! assert_eq!(3, spans.len());
//!
//! assert_eq!("Have you seen ", spans[0].as_str());
//! assert_eq!(0, spans[0].start());
//! assert_eq!(14, spans[0].end());
//! assert_eq!(None, spans[0].kind());
//!
//! assert_eq!("http://example.com", spans[1].as_str());
//! assert_eq!(14, spans[1].start());
//! assert_eq!(32, spans[1].end());
//! assert_eq!(Some(&LinkKind::Url), spans[1].kind());
//!
//! assert_eq!("?", spans[2].as_str());
//! assert_eq!(32, spans[2].start());
//! assert_eq!(33, spans[2].end());
//! assert_eq!(None, spans[2].kind());
//! ```
//!
//! ### Conformance
//!
//! This crates makes an effort to respect the various standards, namely:
//!
//! * [RFC 3986] and [RFC 3987] for URLs
//! * [RFC 5321] and [RFC 6531] for emails (except IP addresses and quoting)
//!
//! At the same time, it does not guarantee that the returned links are valid.
//! If in doubt, it rather returns a link than skipping it.
//!
//! If you need to validate URLs, e.g. for checking TLDs, use another library on
//! the returned links.
//!
//! [RFC 3986]: https://tools.ietf.org/search/rfc3986
//! [RFC 3987]: https://tools.ietf.org/search/rfc3987
//! [RFC 5321]: https://tools.ietf.org/search/rfc5321
//! [RFC 6531]: https://tools.ietf.org/search/rfc6531

#![doc(html_root_url = "https://docs.rs/linkify/0.7.0")]
#![deny(warnings)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]

mod email;
mod finder;
mod scanner;
mod url;

pub use crate::finder::Link;
pub use crate::finder::LinkFinder;
pub use crate::finder::LinkKind;
pub use crate::finder::Links;
pub use crate::finder::{Span, Spans};

#[cfg(doctest)]
doc_comment::doctest!("../README.md");
<commit_msg>Document in lib.rs as well<commit_after>//! Linkify finds links such as URLs and email addresses in plain text.
//! It's smart about where a link ends, such as with trailing punctuation.
//!
//! Your reaction might be: "Do I need a library for this? Why not a regex?".
//! Let's look at a few cases:
//!
//! * In `http://example.com/.` the link should not include the trailing dot
//! * `http://example.com/,` should not include the trailing comma
//! * `(http://example.com/)` should not include the parens
//!
//! Seems simple enough. But then we also have these cases:
//!
//! * `https://en.wikipedia.org/wiki/Link_(The_Legend_of_Zelda)` should include the trailing paren
//! * `http://üñîçøðé.com/ä` should also work for Unicode (including Emoji and Punycode)
//! * `<http://example.com/>` should not include angle brackets
//!
//! This library behaves as you'd expect in the above cases and many more.
//! It uses a simple scan with linear runtime.
//!
//! In addition to URLs, it can also find emails.
//!
//! ### Usage
//!
//! Basic usage:
//!
//! ```
//! use linkify::{LinkFinder, LinkKind};
//!
//! let input = "Have you seen http://example.com?";
//! let finder = LinkFinder::new();
//! let links: Vec<_> = finder.links(input).collect();
//!
//! assert_eq!(1, links.len());
//! let link = &links[0];
//!
//! assert_eq!("http://example.com", link.as_str());
//! assert_eq!(14, link.start());
//! assert_eq!(32, link.end());
//! assert_eq!(&LinkKind::Url, link.kind());
//! ```
//!
//! Option to allow URLs without schemes:
//!
//! ```
//! use linkify::LinkFinder;
//!
//! let input = "Look, no scheme: example.org/foo";
//! let mut finder = LinkFinder::new();
//!
//! // true by default
//! finder.url_must_have_scheme(false);
//!
//! let links: Vec<_> = finder.links(input).collect();
//! assert_eq!(links[0].as_str(), "example.org/foo");
//! ```
//!
//! Restrict the kinds of links:
//!
//! ```
//! use linkify::{LinkFinder, LinkKind};
//!
//! let input = "http://example.com and foo@example.com";
//! let mut finder = LinkFinder::new();
//! finder.kinds(&[LinkKind::Email]);
//! let links: Vec<_> = finder.links(input).collect();
//!
//! assert_eq!(1, links.len());
//! let link = &links[0];
//! assert_eq!("foo@example.com", link.as_str());
//! assert_eq!(&LinkKind::Email, link.kind());
//! ```
//!
//! Split the text into consecutive spans (mixed links and plain text).
//!
//! ```
//! use linkify::{LinkFinder, LinkKind};
//!
//! let input = "Have you seen http://example.com?";
//! let finder = LinkFinder::new();
//! let spans: Vec<_> = finder.spans(input).collect();
//!
//! assert_eq!(3, spans.len());
//!
//! assert_eq!("Have you seen ", spans[0].as_str());
//! assert_eq!(0, spans[0].start());
//! assert_eq!(14, spans[0].end());
//! assert_eq!(None, spans[0].kind());
//!
//! assert_eq!("http://example.com", spans[1].as_str());
//! assert_eq!(14, spans[1].start());
//! assert_eq!(32, spans[1].end());
//! assert_eq!(Some(&LinkKind::Url), spans[1].kind());
//!
//! assert_eq!("?", spans[2].as_str());
//! assert_eq!(32, spans[2].start());
//! assert_eq!(33, spans[2].end());
//! assert_eq!(None, spans[2].kind());
//! ```
//!
//! ### Conformance
//!
//! This crates makes an effort to respect the various standards, namely:
//!
//! * [RFC 3986] and [RFC 3987] for URLs
//! * [RFC 5321] and [RFC 6531] for emails (except IP addresses and quoting)
//!
//! At the same time, it does not guarantee that the returned links are valid.
//! If in doubt, it rather returns a link than skipping it.
//!
//! If you need to validate URLs, e.g. for checking TLDs, use another library on
//! the returned links.
//!
//! [RFC 3986]: https://tools.ietf.org/search/rfc3986
//! [RFC 3987]: https://tools.ietf.org/search/rfc3987
//! [RFC 5321]: https://tools.ietf.org/search/rfc5321
//! [RFC 6531]: https://tools.ietf.org/search/rfc6531

#![doc(html_root_url = "https://docs.rs/linkify/0.7.0")]
#![deny(warnings)]
#![deny(missing_docs)]
#![deny(missing_debug_implementations)]

mod email;
mod finder;
mod scanner;
mod url;

pub use crate::finder::Link;
pub use crate::finder::LinkFinder;
pub use crate::finder::LinkKind;
pub use crate::finder::Links;
pub use crate::finder::{Span, Spans};

#[cfg(doctest)]
doc_comment::doctest!("../README.md");
<|endoftext|>