<commit_before>//! Test harness for Exonum blockchain framework, allowing to test service APIs synchronously
//! and in the same process as the harness.

#![deny(missing_docs)]

extern crate exonum;
extern crate futures;
extern crate mount;
extern crate iron;
extern crate iron_test;
extern crate serde;
extern crate serde_json;

use std::collections::BTreeMap;

use exonum::blockchain::{ApiContext, Blockchain, Service, Transaction, GenesisConfig,
                         SharedNodeState};
use exonum::crypto;
// A bit hacky, `exonum::events` is hidden from docs.
use exonum::events::{Event as ExonumEvent, EventHandler};
use exonum::node::{ApiSender, NodeChannel, NodeHandler, Configuration, ListenerConfig,
                   ServiceConfig, DefaultSystemState, State as NodeState};
use exonum::storage::{MemoryDB, Snapshot};
use futures::Stream;
use futures::executor;
use iron::headers::{Headers, ContentType};
use iron_test::{request, response};
use mount::Mount;
use serde::{Serialize, Deserialize};

mod greedy_fold;
#[doc(hidden)]
pub use greedy_fold::GreedilyFoldable;

const STATE_UPDATE_TIMEOUT: u64 = 10_000;

/// Harness for testing blockchain with a single-node, no-network setup.
pub struct TestHarness {
    handler: NodeHandler,
    channel: NodeChannel,
    api_context: ApiContext,
}

impl TestHarness {
    /// Initializes a harness with a blockchain and no service-specific configuration.
    pub fn new(blockchain: Blockchain) -> Self {
        TestHarness::with_config(blockchain, BTreeMap::new())
    }

    /// Initializes a harness with a blockchain that hosts a given set of services.
    /// The blockchain uses `MemoryDB` for storage.
    pub fn with_services<I>(services: I) -> Self
    where
        I: IntoIterator<Item = Box<Service>>,
    {
        let db = MemoryDB::new();
        let blockchain = Blockchain::new(Box::new(db), services.into_iter().collect());
        TestHarness::new(blockchain)
    }

    /// Initializes a harness with a blockchain and a service-specific configuration.
    pub fn with_config(
        mut blockchain: Blockchain,
        _services_configs: BTreeMap<String, serde_json::Value>,
    ) -> Self {
        crypto::init();

        let (consensus_public_key, consensus_secret_key) = crypto::gen_keypair();
        let (service_public_key, service_secret_key) = crypto::gen_keypair();

        let validator_keys = exonum::blockchain::config::ValidatorKeys {
            consensus_key: consensus_public_key,
            service_key: service_public_key,
        };
        let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

        blockchain.create_genesis_block(genesis.clone()).unwrap();

        let listen_address = "0.0.0.0:2000".parse().unwrap();
        let config = Configuration {
            listener: ListenerConfig {
                consensus_public_key,
                consensus_secret_key: consensus_secret_key.clone(),
                whitelist: Default::default(),
                address: listen_address,
            },
            service: ServiceConfig {
                service_public_key,
                service_secret_key: service_secret_key.clone(),
            },
            mempool: Default::default(),
            network: Default::default(),
            peer_discovery: vec![],
        };

        let api_state = SharedNodeState::new(STATE_UPDATE_TIMEOUT);
        let system_state = Box::new(DefaultSystemState(listen_address));
        let channel = NodeChannel::new(Default::default());
        let api_sender = ApiSender::new(channel.api_requests.0.clone());

        let api_context = ApiContext::from_parts(
            &blockchain,
            api_sender,
            &service_public_key,
            &service_secret_key,
        );

        let handler = NodeHandler::new(
            blockchain,
            listen_address,
            channel.node_sender(),
            system_state,
            config,
            api_state,
        );

        TestHarness {
            handler,
            channel,
            api_context,
        }
    }

    /// Returns the node state of the harness.
    pub fn state(&self) -> &NodeState {
        self.handler.state()
    }

    /// Creates a mounting point for public APIs used by the blockchain.
    // TODO: consider making public
    fn public_api_mount(&self) -> Mount {
        self.api_context.mount_public_api()
    }

    /// Creates a mounting point for public APIs used by the blockchain.
    // TODO: consider making public
    fn private_api_mount(&self) -> Mount {
        self.api_context.mount_private_api()
    }

    /// Creates an instance of `HarnessApi` to test the API provided by services.
    pub fn api(&self) -> HarnessApi {
        HarnessApi::new(self)
    }

    /// Polls the *existing* events from the event loop until exhaustion. Does not wait
    /// until new events arrive.
    pub fn poll_events(&mut self) -> Option<Result<(), ()>> {
        // XXX: Creating a new executor each time seems very inefficient, but sharing
        // a single executor seems to be impossible
        // because `handler` needs to be borrowed mutably into the closure. Use `RefCell`?
        let handler = &mut self.handler;
        let event_stream = self.channel.api_requests.1.by_ref().greedy_fold(
            (),
            |_, event| {
                handler.handle_event(ExonumEvent::Api(event))
            },
        );
        let mut event_exec = executor::spawn(event_stream);
        event_exec.wait_stream()
    }

    /// Returns a snapshot of the current blockchain state.
    pub fn snapshot(&self) -> Box<Snapshot> {
        self.handler.blockchain.snapshot()
    }

    fn do_create_block(&mut self, tx_hashes: &[crypto::Hash]) {
        use std::time::SystemTime;
        use exonum::helpers::Round;
        use exonum::messages::{Message, Propose, Precommit};

        let validator_id = self.state().validator_id().expect(
            "Tested node is not a validator",
        );
        let height = self.state().height();
        let last_hash = *self.state().last_hash();
        let consensus_secret_key = self.state().consensus_secret_key().clone();
        let round = Round::first();

        let handler = &mut self.handler;
        let (block_hash, patch) = handler.create_block(validator_id, height.next(), tx_hashes);
        handler.state.add_block(
            block_hash,
            patch,
            tx_hashes.to_vec(),
            validator_id,
        );

        let propose = Propose::new(
            validator_id,
            height,
            round,
            &last_hash,
            tx_hashes,
            &consensus_secret_key,
        );
        let precommit = Precommit::new(
            validator_id,
            height,
            round,
            &propose.hash(),
            &block_hash,
            SystemTime::now(),
            &consensus_secret_key,
        );

        handler.commit(block_hash, vec![precommit].iter(), Some(round));
    }

    /// Creates block with the specified transactions. The transactions must be previously
    /// sent to the node via API or directly put into the `channel()`.
    ///
    /// # Panics
    ///
    /// In the case any of transaction hashes are not in the mempool.
    pub fn create_block_with_transactions(&mut self, tx_hashes: &[crypto::Hash]) {
        self.poll_events();

        {
            let txs = self.state().transactions().read().expect(
                "Cannot read transactions from node",
            );
            for hash in tx_hashes {
                assert!(txs.contains_key(hash));
            }
        }

        self.do_create_block(tx_hashes);
    }

    /// Creates block with all transactions in the mempool.
    pub fn create_block(&mut self) {
        self.poll_events();

        let tx_hashes: Vec<_> = {
            let txs = self.state().transactions().read().expect(
                "Cannot read transactions from node",
            );
            txs.keys().cloned().collect()
        };

        self.do_create_block(&tx_hashes);
    }
}

/// API encapsulation for the test harness. Allows to execute and synchronously retrieve results
/// for REST-ful endpoints of services.
pub struct HarnessApi {
    public_mount: Mount,
    private_mount: Mount,
    api_sender: ApiSender,
}

impl HarnessApi {
    /// Creates a new instance of Api.
    fn new(harness: &TestHarness) -> Self {
        HarnessApi {
            public_mount: harness.public_api_mount(),
            private_mount: harness.private_api_mount(),
            api_sender: harness.api_context.node_channel().clone(),
        }
    }

    /// Sends a transaction to the node via `ApiSender`.
    pub fn send<T: Transaction>(&self, transaction: T) {
        self.api_sender.send(Box::new(transaction)).expect(
            "Cannot send transaction",
        );
    }

    fn do_get<D>(&self, service_name: &str, endpoint: &str, mount: &Mount) -> D
    where
        for<'de> D: Deserialize<'de>,
    {
        let url = format!("http://localhost:3000/{}/{}", service_name, endpoint);
        let resp = request::get(&url, Headers::new(), mount).unwrap();
        let resp = response::extract_body_to_string(resp);
        // TODO: check status
        serde_json::from_str(&resp).unwrap()
    }

    fn do_post<T, D>(&self, service_name: &str, endpoint: &str, mount: &Mount, transaction: &T) -> D
    where
        T: Transaction + Serialize,
        for<'de> D: Deserialize<'de>,
    {
        let url = format!("http://localhost:3000/{}/{}", service_name, endpoint);
        let resp =
            request::post(
                &url,
                {
                    let mut headers = Headers::new();
                    headers.set(ContentType::json());
                    headers
                },
                &serde_json::to_string(&transaction).expect("Cannot serialize transaction to JSON"),
                mount,
            ).expect("Cannot send transaction");

        let resp = response::extract_body_to_string(resp);
        serde_json::from_str(&resp).expect("Cannot parse result")
    }

    /// Retrieves information from a service using a `GET` method of the public API.
    pub fn get<D>(&self, service_name: &str, endpoint: &str) -> D
    where
        for<'de> D: Deserialize<'de>,
    {
        self.do_get(service_name, endpoint, &self.public_mount)
    }

    /// Posts a transaction to the service using the public API. The returned value is the result
    /// of synchronous transaction processing, which includes running the API shim
    /// and `Transaction.verify()`. `Transaction.execute()` is not run until the transaction
    /// gets to a block via one of `create_block*()` methods.
    pub fn post<T, D>(&self, service_name: &str, endpoint: &str, transaction: &T) -> D
    where
        T: Transaction + Serialize,
        for<'de> D: Deserialize<'de>,
    {
        self.do_post(service_name, endpoint, &self.public_mount, transaction)
    }

    /// Posts a transaction to the service using the private API. The returned value is the result
    /// of synchronous transaction processing, which includes running the API shim
    /// and `Transaction.verify()`. `Transaction.execute()` is not run until the transaction
    /// gets to a block via one of `create_block*()` methods.
    pub fn post_private<T, D>(&self, service_name: &str, endpoint: &str, transaction: &T) -> D
    where
        T: Transaction + Serialize,
        for<'de> D: Deserialize<'de>,
    {
        self.do_post(service_name, endpoint, &self.private_mount, transaction)
    }
}
<commit_msg>Implement transaction probing<commit_after>//! Test harness for Exonum blockchain framework, allowing to test service APIs synchronously
//! and in the same process as the harness.

#![deny(missing_docs)]

extern crate exonum;
extern crate futures;
extern crate mount;
extern crate iron;
extern crate iron_test;
extern crate serde;
extern crate serde_json;

use std::collections::BTreeMap;

use exonum::blockchain::{ApiContext, Blockchain, Service, Transaction, GenesisConfig,
                         SharedNodeState, Schema as CoreSchema};
use exonum::crypto;
// A bit hacky, `exonum::events` is hidden from docs.
use exonum::events::{Event as ExonumEvent, EventHandler};
use exonum::node::{ApiSender, NodeChannel, NodeHandler, Configuration, ListenerConfig,
                   ServiceConfig, DefaultSystemState, State as NodeState, TransactionSend};
use exonum::storage::{MemoryDB, Snapshot};
use futures::Stream;
use futures::executor;
use iron::headers::{Headers, ContentType};
use iron_test::{request, response};
use mount::Mount;
use serde::{Serialize, Deserialize};

mod greedy_fold;
#[doc(hidden)]
pub use greedy_fold::GreedilyFoldable;

const STATE_UPDATE_TIMEOUT: u64 = 10_000;

/// Harness for testing blockchain with a single-node, no-network setup.
pub struct TestHarness {
    handler: NodeHandler,
    channel: NodeChannel,
    api_context: ApiContext,
}

impl TestHarness {
    /// Initializes a harness with a blockchain and no service-specific configuration.
    pub fn new(blockchain: Blockchain) -> Self {
        TestHarness::with_config(blockchain, BTreeMap::new())
    }

    /// Initializes a harness with a blockchain that hosts a given set of services.
    /// The blockchain uses `MemoryDB` for storage.
    pub fn with_services<I>(services: I) -> Self
    where
        I: IntoIterator<Item = Box<Service>>,
    {
        let db = MemoryDB::new();
        let blockchain = Blockchain::new(Box::new(db), services.into_iter().collect());
        TestHarness::new(blockchain)
    }

    /// Initializes a harness with a blockchain and a service-specific configuration.
    pub fn with_config(
        mut blockchain: Blockchain,
        _services_configs: BTreeMap<String, serde_json::Value>,
    ) -> Self {
        crypto::init();

        let (consensus_public_key, consensus_secret_key) = crypto::gen_keypair();
        let (service_public_key, service_secret_key) = crypto::gen_keypair();

        let validator_keys = exonum::blockchain::config::ValidatorKeys {
            consensus_key: consensus_public_key,
            service_key: service_public_key,
        };
        let genesis = GenesisConfig::new(vec![validator_keys].into_iter());

        blockchain.create_genesis_block(genesis.clone()).unwrap();

        let listen_address = "0.0.0.0:2000".parse().unwrap();
        let config = Configuration {
            listener: ListenerConfig {
                consensus_public_key,
                consensus_secret_key: consensus_secret_key.clone(),
                whitelist: Default::default(),
                address: listen_address,
            },
            service: ServiceConfig {
                service_public_key,
                service_secret_key: service_secret_key.clone(),
            },
            mempool: Default::default(),
            network: Default::default(),
            peer_discovery: vec![],
        };

        let api_state = SharedNodeState::new(STATE_UPDATE_TIMEOUT);
        let system_state = Box::new(DefaultSystemState(listen_address));
        let channel = NodeChannel::new(Default::default());
        let api_sender = ApiSender::new(channel.api_requests.0.clone());

        let api_context = ApiContext::from_parts(
            &blockchain,
            api_sender,
            &service_public_key,
            &service_secret_key,
        );

        let handler = NodeHandler::new(
            blockchain,
            listen_address,
            channel.node_sender(),
            system_state,
            config,
            api_state,
        );

        TestHarness {
            handler,
            channel,
            api_context,
        }
    }

    /// Returns the node state of the harness.
    pub fn state(&self) -> &NodeState {
        self.handler.state()
    }

    /// Creates a mounting point for public APIs used by the blockchain.
    // TODO: consider making public
    fn public_api_mount(&self) -> Mount {
        self.api_context.mount_public_api()
    }

    /// Creates a mounting point for public APIs used by the blockchain.
    // TODO: consider making public
    fn private_api_mount(&self) -> Mount {
        self.api_context.mount_private_api()
    }

    /// Creates an instance of `HarnessApi` to test the API provided by services.
    pub fn api(&self) -> HarnessApi {
        HarnessApi::new(self)
    }

    /// Polls the *existing* events from the event loop until exhaustion. Does not wait
    /// until new events arrive.
    pub fn poll_events(&mut self) -> Option<Result<(), ()>> {
        // XXX: Creating a new executor each time seems very inefficient, but sharing
        // a single executor seems to be impossible
        // because `handler` needs to be borrowed mutably into the closure. Use `RefCell`?
        let handler = &mut self.handler;
        let event_stream = self.channel.api_requests.1.by_ref().greedy_fold(
            (),
            |_, event| {
                handler.handle_event(ExonumEvent::Api(event))
            },
        );
        let mut event_exec = executor::spawn(event_stream);
        event_exec.wait_stream()
    }

    /// Returns a snapshot of the current blockchain state.
    pub fn snapshot(&self) -> Box<Snapshot> {
        self.handler.blockchain.snapshot()
    }

    /// Executes a list of transactions given the current state of the blockchain, but does not
    /// commit execution results to the blockchain. The execution result is the same
    /// as if transactions were included into a new block; for example,
    /// transactions included into one of previous blocks do not lead to any state changes.
    ///
    /// # Panics
    ///
    /// If there are duplicate transactions.
    pub fn probe_all(&self, transactions: Vec<Box<Transaction>>) -> Box<Snapshot> {
        let validator_id = self.state().validator_id().expect(
            "Tested node is not a validator",
        );
        let height = self.state().height();

        let (transaction_map, hashes) = {
            let mut transaction_map = BTreeMap::new();
            let mut hashes = Vec::with_capacity(transactions.len());

            let core_schema = CoreSchema::new(self.snapshot());
            let committed_txs = core_schema.transactions();

            for tx in transactions {
                let hash = tx.hash();
                if committed_txs.contains(&hash) {
                    continue;
                }

                hashes.push(hash);
                transaction_map.insert(hash, tx);
            }

            assert_eq!(
                hashes.len(),
                transaction_map.len(),
                "Duplicate transactions in probe"
            );

            (transaction_map, hashes)
        };

        let (_, patch) = self.handler.blockchain.create_patch(
            validator_id,
            height,
            &hashes,
            &transaction_map,
        );

        let mut fork = self.handler.blockchain.fork();
        fork.merge(patch);
        Box::new(fork)
    }

    /// Executes a transaction given the current state of the blockchain but does not
    /// commit execution results to the blockchain. The execution result is the same
    /// as if a transaction was included into a new block; for example,
    /// a transaction included into one of previous blocks does not lead to any state changes.
    pub fn probe<T: Transaction>(&self, transaction: T) -> Box<Snapshot> {
        self.probe_all(vec![Box::new(transaction)])
    }

    fn do_create_block(&mut self, tx_hashes: &[crypto::Hash]) {
        use std::time::SystemTime;
        use exonum::helpers::Round;
        use exonum::messages::{Message, Propose, Precommit};

        let validator_id = self.state().validator_id().expect(
            "Tested node is not a validator",
        );
        let height = self.state().height();
        let last_hash = *self.state().last_hash();
        let consensus_secret_key = self.state().consensus_secret_key().clone();
        let round = Round::first();

        let handler = &mut self.handler;
        let (block_hash, patch) = handler.create_block(validator_id, height.next(), tx_hashes);
        handler.state.add_block(
            block_hash,
            patch,
            tx_hashes.to_vec(),
            validator_id,
        );

        let propose = Propose::new(
            validator_id,
            height,
            round,
            &last_hash,
            tx_hashes,
            &consensus_secret_key,
        );
        let precommit = Precommit::new(
            validator_id,
            height,
            round,
            &propose.hash(),
            &block_hash,
            SystemTime::now(),
            &consensus_secret_key,
        );

        handler.commit(block_hash, vec![precommit].iter(), Some(round));
    }

    /// Creates block with the specified transactions. The transactions must be previously
    /// sent to the node via API or directly put into the `channel()`.
    ///
    /// # Panics
    ///
    /// In the case any of transaction hashes are not in the mempool.
    pub fn create_block_with_transactions(&mut self, tx_hashes: &[crypto::Hash]) {
        self.poll_events();

        {
            let txs = self.state().transactions().read().expect(
                "Cannot read transactions from node",
            );
            for hash in tx_hashes {
                assert!(txs.contains_key(hash));
            }
        }

        self.do_create_block(tx_hashes);
    }

    /// Creates block with all transactions in the mempool.
    pub fn create_block(&mut self) {
        self.poll_events();

        let tx_hashes: Vec<_> = {
            let txs = self.state().transactions().read().expect(
                "Cannot read transactions from node",
            );
            txs.keys().cloned().collect()
        };

        self.do_create_block(&tx_hashes);
    }
}

/// API encapsulation for the test harness. Allows to execute and synchronously retrieve results
/// for REST-ful endpoints of services.
pub struct HarnessApi {
    public_mount: Mount,
    private_mount: Mount,
    api_sender: ApiSender,
}

impl HarnessApi {
    /// Creates a new instance of Api.
    fn new(harness: &TestHarness) -> Self {
        HarnessApi {
            public_mount: harness.public_api_mount(),
            private_mount: harness.private_api_mount(),
            api_sender: harness.api_context.node_channel().clone(),
        }
    }

    /// Sends a transaction to the node via `ApiSender`.
    pub fn send<T: Transaction>(&self, transaction: T) {
        self.api_sender.send(Box::new(transaction)).expect(
            "Cannot send transaction",
        );
    }

    fn do_get<D>(&self, service_name: &str, endpoint: &str, mount: &Mount) -> D
    where
        for<'de> D: Deserialize<'de>,
    {
        let url = format!("http://localhost:3000/{}/{}", service_name, endpoint);
        let resp = request::get(&url, Headers::new(), mount).unwrap();
        let resp = response::extract_body_to_string(resp);
        // TODO: check status
        serde_json::from_str(&resp).unwrap()
    }

    fn do_post<T, D>(&self, service_name: &str, endpoint: &str, mount: &Mount, transaction: &T) -> D
    where
        T: Transaction + Serialize,
        for<'de> D: Deserialize<'de>,
    {
        let url = format!("http://localhost:3000/{}/{}", service_name, endpoint);
        let resp =
            request::post(
                &url,
                {
                    let mut headers = Headers::new();
                    headers.set(ContentType::json());
                    headers
                },
                &serde_json::to_string(&transaction).expect("Cannot serialize transaction to JSON"),
                mount,
            ).expect("Cannot send transaction");

        let resp = response::extract_body_to_string(resp);
        serde_json::from_str(&resp).expect("Cannot parse result")
    }

    /// Retrieves information from a service using a `GET` method of the public API.
    pub fn get<D>(&self, service_name: &str, endpoint: &str) -> D
    where
        for<'de> D: Deserialize<'de>,
    {
        self.do_get(service_name, endpoint, &self.public_mount)
    }

    /// Posts a transaction to the service using the public API. The returned value is the result
    /// of synchronous transaction processing, which includes running the API shim
    /// and `Transaction.verify()`. `Transaction.execute()` is not run until the transaction
    /// gets to a block via one of `create_block*()` methods.
    pub fn post<T, D>(&self, service_name: &str, endpoint: &str, transaction: &T) -> D
    where
        T: Transaction + Serialize,
        for<'de> D: Deserialize<'de>,
    {
        self.do_post(service_name, endpoint, &self.public_mount, transaction)
    }

    /// Posts a transaction to the service using the private API. The returned value is the result
    /// of synchronous transaction processing, which includes running the API shim
    /// and `Transaction.verify()`. `Transaction.execute()` is not run until the transaction
    /// gets to a block via one of `create_block*()` methods.
    pub fn post_private<T, D>(&self, service_name: &str, endpoint: &str, transaction: &T) -> D
    where
        T: Transaction + Serialize,
        for<'de> D: Deserialize<'de>,
    {
        self.do_post(service_name, endpoint, &self.private_mount, transaction)
    }
}
<|endoftext|>


<commit_before>//! The `grok` library allows you to quickly parse and match potentially unstructured data
//! into a structed result. It is especially helpful when parsing logfiles of all kinds. This
//! [Rust](http://rust-lang.org) version is mainly a port from the [java version](https://github.com/thekrakken/java-grok)
//! which in drew inspiration from the original [ruby version](https://github.com/logstash-plugins/logstash-filter-grok).
#![doc(html_root_url = "https://docs.rs/grok/0.1.0")]
extern crate regex;

use regex::{Captures, Regex};
use std::collections::BTreeMap;

const GROK_PATTERN: &'static str = r"%\{(?P<name>(?P<pattern>[A-z0-9]+)(?::(?P<alias>[A-z0-9_:;/\s\.]+))?)(?:=(?P<definition>(?:(?:[^{}]+|\.+)+)+))?\}";

/// The `Matches` represent matched results from a `Pattern` against text.
pub struct Matches<'a> {
    captures: Captures<'a>,
    alias: &'a BTreeMap<String, String>
}

impl<'a> Matches<'a> {
    /// Instantiates the matches for a pattern after the match.
    pub fn new(captures: Captures<'a>, alias: &'a BTreeMap<String, String>) -> Self {
        Matches { captures: captures, alias: alias }
    }

    /// Gets the value for the name (or) alias if found, `None` otherwise.
    pub fn get(&self, name_or_alias: &str) -> Option<&str> {
        match self.alias.get(name_or_alias) {
            Some(real) => self.captures.name(real).map(|m| m.as_str()),
            None => None,
        }
    }

    /// Returns the number of matches.
    pub fn len(&self) -> usize {
        self.captures.len() - 1
    }

    /// Returns true if there are no matches, false otherwise.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

/// The `Pattern` represents a compiled regex, ready to be matched against arbitrary text.
pub struct Pattern {
    regex: Regex,
    alias: BTreeMap<String, String>,
}

impl Pattern {
    /// Creates a new pattern from a raw regex string and an alias map to identify the
    /// fields properly.
    pub fn new(regex: &str, alias: BTreeMap<String, String>) -> Self {
        Pattern {
            regex: Regex::new(regex).expect("Could not compile regex!"),
            alias: alias,
        }
    }

    /// Matches this compiled `Pattern` against the text and returns the matches.
    pub fn match_against<'a>(&'a self, text: &'a str) -> Option<Matches<'a>> {
        self.regex.captures(text).map(|cap| Matches::new(cap, &self.alias))
    }
}

/// The basic structure to manage patterns, entry point for common usage.
pub struct Grok {
    definitions: BTreeMap<String, String>,
}

impl Grok {
    /// Creates a new `Grok` instance with no patterns.
    pub fn empty() -> Self {
        Grok {
            definitions: BTreeMap::new(),
        }
    }

    /// Inserts a custom pattern.
    pub fn insert_definition<S: Into<String>>(&mut self, name: S, pattern: S) {
        self.definitions.insert(name.into(), pattern.into());
    }

    /// Compiles the given pattern, making it ready for matching.
    pub fn compile(&mut self, pattern: &str, with_alias_only: bool) -> Pattern {
        let mut named_regex = String::from(pattern);
        let original_grok_pattern = pattern;
        let mut alias: BTreeMap<String, String> = BTreeMap::new();

        let mut index = 0;
        let mut iteration_left = 1000;
        let mut continue_iteration = true;

        let grok_regex = Regex::new(GROK_PATTERN).unwrap();
        while continue_iteration {
            continue_iteration = false;
            if iteration_left <= 0 {
                panic!(
                    "Deep recursion pattern compilation of {:?}",
                    original_grok_pattern
                );
            }
            iteration_left -= 1;

            let capture = named_regex.clone();
            if let Some(m) = grok_regex.captures(&capture) {
                continue_iteration = true;

                let mut name = String::from(m.name("name").unwrap().as_str());
                if let Some(definition) = m.name("definition") {
                    self.insert_definition(m.name("pattern").unwrap().as_str(), definition.as_str());
                    name = format!("{}={}", name, definition.as_str());
                }

                let n = format!("%{{{}}}", name);
                let count = named_regex.matches(&n).count();
                for _ in 0..count {
                    let definition_of_pattern = self.definitions.get(m.name("pattern").unwrap().as_str());
                    if definition_of_pattern.is_none() {
                        panic!("No definition for key '{}' found, aborting", m.name("pattern").unwrap().as_str());
                    }

                    let replacement = if with_alias_only && m.name("alias").is_none() {
                        format!("(?:{})", definition_of_pattern.unwrap())
                    } else {
                        format!("(?P<name{}>{})", index, definition_of_pattern.unwrap())
                    };

                    let name_and_index = format!("name{}", index);
                    if m.name("alias").is_some() {
                        alias.insert(m.name("alias").unwrap().as_str().into(), name_and_index);
                    } else {
                        alias.insert(name.clone(), name_and_index);
                    }

                    let search_string = format!("%{{{}}}", name);
                    named_regex = named_regex.replacen(&search_string, &replacement, 1);
                    index += 1;
                }
            }
        }

        if named_regex.is_empty() {
            panic!("Pattern not found");
        }
        Pattern::new(&named_regex, alias)
    }
}

impl Default for Grok {
    fn default() -> Grok {
        Grok::empty()
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_simple_anonymous_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("USERNAME", r"[a-zA-Z0-9._-]+");
        let pattern = grok.compile("%{USERNAME}", false);

        let matches = pattern.match_against("root").expect("No matches found!");
        assert_eq!("root", matches.get("USERNAME").unwrap());
        assert_eq!(1, matches.len());
        let matches = pattern.match_against("john doe").expect("No matches found!");
        assert_eq!("john", matches.get("USERNAME").unwrap());
        assert_eq!(1, matches.len());
    }

    #[test]
    fn test_simple_named_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("USERNAME", r"[a-zA-Z0-9._-]+");
        let pattern = grok.compile("%{USERNAME:usr}", false);

        let matches = pattern.match_against("root").expect("No matches found!");
        assert_eq!("root", matches.get("usr").unwrap());
        assert_eq!(1, matches.len());
        let matches = pattern.match_against("john doe").expect("No matches found!");
        assert_eq!("john", matches.get("usr").unwrap());
        assert_eq!(1, matches.len());
    }

    #[test]
    fn test_alias_anonymous_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("USERNAME", r"[a-zA-Z0-9._-]+");
        grok.insert_definition("USER", r"%{USERNAME}");
        let pattern = grok.compile("%{USER}", false);

        let matches = pattern.match_against("root").expect("No matches found!");
        assert_eq!("root", matches.get("USER").unwrap());
        let matches = pattern.match_against("john doe").expect("No matches found!");
        assert_eq!("john", matches.get("USER").unwrap());
    }

    #[test]
    fn test_ailas_named_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("USERNAME", r"[a-zA-Z0-9._-]+");
        grok.insert_definition("USER", r"%{USERNAME}");
        let pattern = grok.compile("%{USER:usr}", false);

        let matches = pattern.match_against("root").expect("No matches found!");
        assert_eq!("root", matches.get("usr").unwrap());
        let matches = pattern.match_against("john doe").expect("No matches found!");
        assert_eq!("john", matches.get("usr").unwrap());
    }

    #[test]
    fn test_composite_or_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("MAC", r"(?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC})");
        grok.insert_definition("CISCOMAC", r"(?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4})");
        grok.insert_definition("WINDOWSMAC", r"(?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})");
        grok.insert_definition("COMMONMAC", r"(?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})");
        let pattern = grok.compile("%{MAC}", false);

        let matches = pattern.match_against("5E:FF:56:A2:AF:15").expect("No matches found!");
        assert_eq!("5E:FF:56:A2:AF:15", matches.get("MAC").unwrap());
        assert_eq!(4, matches.len());
        let matches = pattern.match_against("hello! 5E:FF:56:A2:AF:15 what?").expect("No matches found!");
        assert_eq!("5E:FF:56:A2:AF:15", matches.get("MAC").unwrap());
        assert_eq!(true, pattern.match_against("5E:FF").is_none());
    }

    #[test]
    fn test_multiple_patterns() {
        let mut grok = Grok::default();
        grok.insert_definition("YEAR", r"(\d\d){1,2}");
        grok.insert_definition("MONTH", r"\b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\b");
        grok.insert_definition("DAY", r"(?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)");
        let pattern = grok.compile("%{DAY} %{MONTH} %{YEAR}", false);

        let matches = pattern.match_against("Monday March 2012").expect("No matches found!");
        assert_eq!("Monday", matches.get("DAY").unwrap());
        assert_eq!("March", matches.get("MONTH").unwrap());
        assert_eq!("2012", matches.get("YEAR").unwrap());
        assert_eq!(None, matches.get("unknown"));
    }

    #[test]
    fn test_with_alias_only() {
        let mut grok = Grok::default();
        grok.insert_definition("MAC", r"(?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC})");
        grok.insert_definition("CISCOMAC", r"(?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4})");
        grok.insert_definition("WINDOWSMAC", r"(?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})");
        grok.insert_definition("COMMONMAC", r"(?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})");
        let pattern = grok.compile("%{MAC:macaddr}", true);

        let matches = pattern.match_against("5E:FF:56:A2:AF:15").expect("No matches found!");
        assert_eq!("5E:FF:56:A2:AF:15", matches.get("macaddr").unwrap());
        assert_eq!(1, matches.len());
        let matches = pattern.match_against("hello! 5E:FF:56:A2:AF:15 what?").expect("No matches found!");
        assert_eq!("5E:FF:56:A2:AF:15", matches.get("macaddr").unwrap());
        assert_eq!(true, pattern.match_against("5E:FF").is_none());
    }
}
<commit_msg>first batch of refactoring the compilation<commit_after>//! The `grok` library allows you to quickly parse and match potentially unstructured data
//! into a structed result. It is especially helpful when parsing logfiles of all kinds. This
//! [Rust](http://rust-lang.org) version is mainly a port from the [java version](https://github.com/thekrakken/java-grok)
//! which in drew inspiration from the original [ruby version](https://github.com/logstash-plugins/logstash-filter-grok).
#![doc(html_root_url = "https://docs.rs/grok/0.1.0")]
extern crate regex;

use regex::{Captures, Regex};
use std::collections::BTreeMap;

const MAX_RECURSION: usize = 1024;
const GROK_PATTERN: &'static str = r"%\{(?P<name>(?P<pattern>[A-z0-9]+)(?::(?P<alias>[A-z0-9_:;/\s\.]+))?)(?:=(?P<definition>(?:(?:[^{}]+|\.+)+)+))?\}";

/// The `Matches` represent matched results from a `Pattern` against text.
pub struct Matches<'a> {
    captures: Captures<'a>,
    alias: &'a BTreeMap<String, String>
}

impl<'a> Matches<'a> {
    /// Instantiates the matches for a pattern after the match.
    pub fn new(captures: Captures<'a>, alias: &'a BTreeMap<String, String>) -> Self {
        Matches { captures: captures, alias: alias }
    }

    /// Gets the value for the name (or) alias if found, `None` otherwise.
    pub fn get(&self, name_or_alias: &str) -> Option<&str> {
        match self.alias.get(name_or_alias) {
            Some(real) => self.captures.name(real).map(|m| m.as_str()),
            None => None,
        }
    }

    /// Returns the number of matches.
    pub fn len(&self) -> usize {
        self.captures.len() - 1
    }

    /// Returns true if there are no matches, false otherwise.
    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }
}

/// The `Pattern` represents a compiled regex, ready to be matched against arbitrary text.
pub struct Pattern {
    regex: Regex,
    alias: BTreeMap<String, String>,
}

impl Pattern {
    /// Creates a new pattern from a raw regex string and an alias map to identify the
    /// fields properly.
    pub fn new(regex: &str, alias: BTreeMap<String, String>) -> Self {
        Pattern {
            regex: Regex::new(regex).expect("Could not compile regex!"),
            alias: alias,
        }
    }

    /// Matches this compiled `Pattern` against the text and returns the matches.
    pub fn match_against<'a>(&'a self, text: &'a str) -> Option<Matches<'a>> {
        self.regex.captures(text).map(|cap| Matches::new(cap, &self.alias))
    }
}

/// The basic structure to manage patterns, entry point for common usage.
pub struct Grok {
    definitions: BTreeMap<String, String>,
}

impl Grok {
    /// Creates a new `Grok` instance with no patterns.
    pub fn empty() -> Self {
        Grok {
            definitions: BTreeMap::new(),
        }
    }

    /// Inserts a custom pattern.
    pub fn insert_definition<S: Into<String>>(&mut self, name: S, pattern: S) {
        self.definitions.insert(name.into(), pattern.into());
    }

    /// Compiles the given pattern, making it ready for matching.
    pub fn compile(&mut self, pattern: &str, with_alias_only: bool) -> Pattern {
        let mut named_regex = String::from(pattern);
        let original_grok_pattern = pattern;
        let mut alias: BTreeMap<String, String> = BTreeMap::new();

        let mut index = 0;
        let mut iteration_left = MAX_RECURSION;
        let mut continue_iteration = true;

        let grok_regex = Regex::new(GROK_PATTERN).unwrap();
        while continue_iteration {
            continue_iteration = false;
            if iteration_left <= 0 {
                panic!(
                    "Deep recursion pattern compilation of {:?}",
                    original_grok_pattern
                );
            }
            iteration_left -= 1;

            if let Some(m) = grok_regex.captures(&named_regex.clone()) {
                continue_iteration = true;
                let raw_pattern = m.name("pattern").unwrap().as_str();

                let mut name = String::from(m.name("name").unwrap().as_str());
                if let Some(definition) = m.name("definition") {
                    self.insert_definition(raw_pattern, definition.as_str());
                    name = format!("{}={}", name, definition.as_str());
                }

                // Since a pattern with a given name can show up more than once, we need to
                // loop through the number of matches found and apply the transformations
                // on each of them.
                for _ in 0..named_regex.matches(&format!("%{{{}}}", name)).count() {

                    // Check if we have a definition for the raw pattern key and fail quickly
                    // if not.
                    let pattern_definition = match self.definitions.get(raw_pattern) {
                        Some(d) => d,
                        None => panic!("No definition for key '{}' found, aborting", raw_pattern),
                    };

                    // If no alias is specified and all but with alias are ignored, the replacement
                    // tells the regex engine to ignore the matches. Otherwise, the definition is
                    // turned into a regex that the engine understands and uses a named group.
                    let replacement = if with_alias_only && m.name("alias").is_none() {
                        format!("(?:{})", pattern_definition)
                    } else {
                        format!("(?P<name{}>{})", index, pattern_definition)
                    };

                    // If an alias is specified by the user use that one to match the name<index>
                    // conversion, oterhwise just use the name of the pattern definition directly.
                    alias.insert(match m.name("alias") {
                        Some(a) => a.as_str().into(),
                        None => name.clone(),
                    }, format!("name{}", index));

                    // Finally, look for the original %{...} style pattern and replace it
                    // with our replacement (only the first occurence since we are iterating
                    // one by one).
                    named_regex = named_regex.replacen(&format!("%{{{}}}", name), &replacement, 1);

                    index += 1;
                }
            }
        }

        if named_regex.is_empty() {
            panic!("Pattern not found");
        }
        Pattern::new(&named_regex, alias)
    }
}

impl Default for Grok {
    fn default() -> Grok {
        Grok::empty()
    }
}

#[cfg(test)]
mod tests {

    use super::*;

    #[test]
    fn test_simple_anonymous_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("USERNAME", r"[a-zA-Z0-9._-]+");
        let pattern = grok.compile("%{USERNAME}", false);

        let matches = pattern.match_against("root").expect("No matches found!");
        assert_eq!("root", matches.get("USERNAME").unwrap());
        assert_eq!(1, matches.len());
        let matches = pattern.match_against("john doe").expect("No matches found!");
        assert_eq!("john", matches.get("USERNAME").unwrap());
        assert_eq!(1, matches.len());
    }

    #[test]
    fn test_simple_named_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("USERNAME", r"[a-zA-Z0-9._-]+");
        let pattern = grok.compile("%{USERNAME:usr}", false);

        let matches = pattern.match_against("root").expect("No matches found!");
        assert_eq!("root", matches.get("usr").unwrap());
        assert_eq!(1, matches.len());
        let matches = pattern.match_against("john doe").expect("No matches found!");
        assert_eq!("john", matches.get("usr").unwrap());
        assert_eq!(1, matches.len());
    }

    #[test]
    fn test_alias_anonymous_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("USERNAME", r"[a-zA-Z0-9._-]+");
        grok.insert_definition("USER", r"%{USERNAME}");
        let pattern = grok.compile("%{USER}", false);

        let matches = pattern.match_against("root").expect("No matches found!");
        assert_eq!("root", matches.get("USER").unwrap());
        let matches = pattern.match_against("john doe").expect("No matches found!");
        assert_eq!("john", matches.get("USER").unwrap());
    }

    #[test]
    fn test_ailas_named_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("USERNAME", r"[a-zA-Z0-9._-]+");
        grok.insert_definition("USER", r"%{USERNAME}");
        let pattern = grok.compile("%{USER:usr}", false);

        let matches = pattern.match_against("root").expect("No matches found!");
        assert_eq!("root", matches.get("usr").unwrap());
        let matches = pattern.match_against("john doe").expect("No matches found!");
        assert_eq!("john", matches.get("usr").unwrap());
    }

    #[test]
    fn test_composite_or_pattern() {
        let mut grok = Grok::default();
        grok.insert_definition("MAC", r"(?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC})");
        grok.insert_definition("CISCOMAC", r"(?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4})");
        grok.insert_definition("WINDOWSMAC", r"(?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})");
        grok.insert_definition("COMMONMAC", r"(?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})");
        let pattern = grok.compile("%{MAC}", false);

        let matches = pattern.match_against("5E:FF:56:A2:AF:15").expect("No matches found!");
        assert_eq!("5E:FF:56:A2:AF:15", matches.get("MAC").unwrap());
        assert_eq!(4, matches.len());
        let matches = pattern.match_against("hello! 5E:FF:56:A2:AF:15 what?").expect("No matches found!");
        assert_eq!("5E:FF:56:A2:AF:15", matches.get("MAC").unwrap());
        assert_eq!(true, pattern.match_against("5E:FF").is_none());
    }

    #[test]
    fn test_multiple_patterns() {
        let mut grok = Grok::default();
        grok.insert_definition("YEAR", r"(\d\d){1,2}");
        grok.insert_definition("MONTH", r"\b(?:Jan(?:uary)?|Feb(?:ruary)?|Mar(?:ch)?|Apr(?:il)?|May|Jun(?:e)?|Jul(?:y)?|Aug(?:ust)?|Sep(?:tember)?|Oct(?:ober)?|Nov(?:ember)?|Dec(?:ember)?)\b");
        grok.insert_definition("DAY", r"(?:Mon(?:day)?|Tue(?:sday)?|Wed(?:nesday)?|Thu(?:rsday)?|Fri(?:day)?|Sat(?:urday)?|Sun(?:day)?)");
        let pattern = grok.compile("%{DAY} %{MONTH} %{YEAR}", false);

        let matches = pattern.match_against("Monday March 2012").expect("No matches found!");
        assert_eq!("Monday", matches.get("DAY").unwrap());
        assert_eq!("March", matches.get("MONTH").unwrap());
        assert_eq!("2012", matches.get("YEAR").unwrap());
        assert_eq!(None, matches.get("unknown"));
    }

    #[test]
    fn test_with_alias_only() {
        let mut grok = Grok::default();
        grok.insert_definition("MAC", r"(?:%{CISCOMAC}|%{WINDOWSMAC}|%{COMMONMAC})");
        grok.insert_definition("CISCOMAC", r"(?:(?:[A-Fa-f0-9]{4}\.){2}[A-Fa-f0-9]{4})");
        grok.insert_definition("WINDOWSMAC", r"(?:(?:[A-Fa-f0-9]{2}-){5}[A-Fa-f0-9]{2})");
        grok.insert_definition("COMMONMAC", r"(?:(?:[A-Fa-f0-9]{2}:){5}[A-Fa-f0-9]{2})");
        let pattern = grok.compile("%{MAC:macaddr}", true);

        let matches = pattern.match_against("5E:FF:56:A2:AF:15").expect("No matches found!");
        assert_eq!("5E:FF:56:A2:AF:15", matches.get("macaddr").unwrap());
        assert_eq!(1, matches.len());
        let matches = pattern.match_against("hello! 5E:FF:56:A2:AF:15 what?").expect("No matches found!");
        assert_eq!("5E:FF:56:A2:AF:15", matches.get("macaddr").unwrap());
        assert_eq!(true, pattern.match_against("5E:FF").is_none());
    }
}
<|endoftext|>


<commit_before>#![feature(core)]

extern crate atom;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use std::mem;
use std::ptr;
use atom::Atom;

use std::boxed::FnBox;

pub use select::Select;
pub use barrier::Barrier;
mod select;
mod barrier;

/// Drop rules
/// This may be freed iff state is Pulsed | Dropped
/// and Waiting is Dropped
struct Inner {
    state: AtomicUsize,
    waiting: Atom<Waiting, Box<Waiting>>
}

const PULSED: usize = 1;
const TX_DROPPED: usize = 2;
const RX_DROPPED: usize = 4;

pub enum Waiting {
    Thread(thread::Thread),
    Select(select::Handle),
    Barrier(barrier::Handle),
    Callback(Box<FnBox() + Send>)
}

impl Waiting {
    fn trigger(s: Box<Self>, id: usize) {
        match *s {
            Waiting::Thread(thread) => thread.unpark(),
            Waiting::Select(select) => {
                let trigger = {
                    let mut guard = select.0.lock().unwrap();
                    guard.ready.push(id);
                    guard.trigger.take()
                };
                trigger.map(|x| x.pulse());
            }
            Waiting::Barrier(barrier) => {
                let count = barrier.0.count.fetch_sub(1, Ordering::Relaxed);
                if count == 1 {
                    let mut guard = barrier.0.trigger.lock().unwrap();
                    if let Some(t) = guard.take() {
                        t.pulse();
                    }
                }
            }
            Waiting::Callback(cb) => cb()
        }
    }

    pub fn thread() -> Waiting {
        Waiting::Thread(thread::current())
    }
}

unsafe impl Send for Trigger {}

pub struct Trigger(*mut Inner);

impl Drop for Trigger {
    fn drop(&mut self) {
        let flag = self.set(TX_DROPPED);
        self.wake();
        self.free(flag);
    }
}

impl Trigger {
    fn free(&mut self, flag: usize) {
        if (flag & RX_DROPPED) == RX_DROPPED {
            let inner: Box<Inner> = unsafe {
                mem::transmute(self.0)
            };
            drop(inner);
        }
        self.0 = ptr::null_mut();
    }

    fn inner(&self) -> &Inner {
        unsafe { mem::transmute(self.0) }
    }

    fn set(&self, state: usize) -> usize {
        self.inner().state.fetch_or(state, Ordering::Relaxed)
    }

    fn wake(&self) {
        let id = unsafe { mem::transmute(self.0) };
        match self.inner().waiting.take(Ordering::Acquire) {
            None => (),
            Some(v) => Waiting::trigger(v, id)
        }
    }

    /// Trigger an pulse, this can only occure once
    /// Returns true if this triggering triggered the pulse
    pub fn pulse(mut self) {
        let flag = self.set(PULSED | TX_DROPPED);
        self.wake();

        // We manually dropped ourselves to avoid to overhead
        // of a third atomic operation
        self.free(flag);
        unsafe {mem::forget(self)};
    }
}

unsafe impl Send for Pulse {}

pub struct Pulse(*mut Inner);

impl Drop for Pulse {
    fn drop(&mut self) {
        let flag = self.inner().state.fetch_or(RX_DROPPED, Ordering::Relaxed);
        if (flag & TX_DROPPED) == TX_DROPPED {
            let inner: Box<Inner> = unsafe {
                mem::transmute(self.0)
            };
            drop(inner);
        }
    }
}

impl Pulse {
    pub fn new() -> (Pulse, Trigger) {
        let inner = Box::new(Inner {
            state: AtomicUsize::new(0),
            waiting: Atom::empty()
        });

        let inner = unsafe {mem::transmute(inner)};

        (Pulse(inner), Trigger(inner))
    }

    fn inner(&self) -> &Inner {
        unsafe { mem::transmute(self.0) }
    }

    // Read out the state of the Pulse
    fn state(&self) -> usize {
        self.inner().state.load(Ordering::Relaxed)
    }

    // Check to see if the pulse is pending or not
    pub fn is_pending(&self) -> bool {
        self.state() == 0
    }

    /// Arm a pulse to wake
    pub fn arm(&self, waiter: Box<Waiting>) {
        let old = self.inner().waiting.swap(
            waiter,
            Ordering::AcqRel
        );

        // if armed fire now
        if !self.is_pending() {
            if let Some(t) = self.inner().waiting.take(Ordering::Acquire) {
                Waiting::trigger(t, self.id());
            }
        }

        if old.is_some() {
            panic!("Pulse cannot be waited on by multiple clients");
        }
    }

    /// Disarm a pulse
    pub fn disarm(&self) {
        self.inner().waiting.take(Ordering::Acquire);
    }

    /// Wait for an pulse to be triggered
    ///
    /// Panics if something else is waiting on this already
    pub fn wait(&self) -> Result<(), WaitError> {
        loop {
            if !self.is_pending() {
                let state = self.state();
                return if (state & PULSED) == PULSED {
                    Ok(())
                } else {
                    Err(WaitError(state))
                };
            }

            self.arm(Box::new(Waiting::thread()));

            if self.is_pending() {
                // wait for wake
                thread::park();
            }
            self.disarm();
        }
    }

    pub fn id(&self) -> usize {
        unsafe { mem::transmute_copy(&self.0) }
    }

    pub fn recycle(&self) -> Trigger {
        if self.is_pending() {
            panic!("Attempted to recycle pending Pulse")
        }
        self.inner().state.store(0, Ordering::Relaxed);
        Trigger(self.0)
    }

    pub fn on_complete<F>(self, f: F) where F: FnOnce() + Send + 'static {
        self.arm(Box::new(Waiting::Callback(Box::new(f))));
    }
}

#[derive(Debug)]
pub struct WaitError(usize);<commit_msg>Move some of the api back to internal use<commit_after>#![feature(core)]

extern crate atom;

use std::sync::atomic::{AtomicUsize, Ordering};
use std::thread;
use std::mem;
use std::ptr;
use atom::Atom;

use std::boxed::FnBox;

pub use select::Select;
pub use barrier::Barrier;
mod select;
mod barrier;

/// Drop rules
/// This may be freed iff state is Pulsed | Dropped
/// and Waiting is Dropped
struct Inner {
    state: AtomicUsize,
    waiting: Atom<Waiting, Box<Waiting>>
}

const PULSED: usize = 0x8000_0000;
const TX_DROPPED: usize = 0x4000_0000;
const RX_DROPPED: usize = 0x2000_0000;

pub enum Waiting {
    Thread(thread::Thread),
    Select(select::Handle),
    Barrier(barrier::Handle),
    Callback(Box<FnBox() + Send>)
}

impl Waiting {
    fn trigger(s: Box<Self>, id: usize) {
        match *s {
            Waiting::Thread(thread) => thread.unpark(),
            Waiting::Select(select) => {
                let trigger = {
                    let mut guard = select.0.lock().unwrap();
                    guard.ready.push(id);
                    guard.trigger.take()
                };
                trigger.map(|x| x.pulse());
            }
            Waiting::Barrier(barrier) => {
                let count = barrier.0.count.fetch_sub(1, Ordering::Relaxed);
                if count == 1 {
                    let mut guard = barrier.0.trigger.lock().unwrap();
                    if let Some(t) = guard.take() {
                        t.pulse();
                    }
                }
            }
            Waiting::Callback(cb) => cb()
        }
    }

    fn thread() -> Waiting {
        Waiting::Thread(thread::current())
    }
}

unsafe impl Send for Trigger {}

pub struct Trigger(*mut Inner);

impl Drop for Trigger {
    fn drop(&mut self) {
        let flag = self.set(TX_DROPPED);
        self.wake();
        self.free(flag);
    }
}

impl Trigger {
    fn free(&mut self, flag: usize) {
        if (flag & RX_DROPPED) == RX_DROPPED {
            let inner: Box<Inner> = unsafe {
                mem::transmute(self.0)
            };
            drop(inner);
        }
        self.0 = ptr::null_mut();
    }

    fn inner(&self) -> &Inner {
        unsafe { mem::transmute(self.0) }
    }

    fn set(&self, state: usize) -> usize {
        self.inner().state.fetch_or(state, Ordering::Relaxed)
    }

    fn wake(&self) {
        let id = unsafe { mem::transmute(self.0) };
        match self.inner().waiting.take(Ordering::Acquire) {
            None => (),
            Some(v) => Waiting::trigger(v, id)
        }
    }

    /// Trigger an pulse, this can only occure once
    /// Returns true if this triggering triggered the pulse
    pub fn pulse(mut self) {
        let flag = self.set(PULSED | TX_DROPPED);
        self.wake();

        // We manually dropped ourselves to avoid to overhead
        // of a third atomic operation
        self.free(flag);
        unsafe {mem::forget(self)};
    }
}

unsafe impl Send for Pulse {}

pub struct Pulse(*mut Inner);

impl Drop for Pulse {
    fn drop(&mut self) {
        let flag = self.inner().state.fetch_or(RX_DROPPED, Ordering::Relaxed);
        if (flag & TX_DROPPED) == TX_DROPPED {
            let inner: Box<Inner> = unsafe {
                mem::transmute(self.0)
            };
            drop(inner);
        }
    }
}

impl Pulse {
    pub fn new() -> (Pulse, Trigger) {
        let inner = Box::new(Inner {
            state: AtomicUsize::new(0),
            waiting: Atom::empty()
        });

        let inner = unsafe {mem::transmute(inner)};

        (Pulse(inner), Trigger(inner))
    }

    fn inner(&self) -> &Inner {
        unsafe { mem::transmute(self.0) }
    }

    // Read out the state of the Pulse
    fn state(&self) -> usize {
        self.inner().state.load(Ordering::Relaxed)
    }

    // Check to see if the pulse is pending or not
    pub fn is_pending(&self) -> bool {
        self.state() == 0
    }

    /// Arm a pulse to wake
    fn arm(&self, waiter: Box<Waiting>) {
        let old = self.inner().waiting.swap(
            waiter,
            Ordering::AcqRel
        );

        // if armed fire now
        if !self.is_pending() {
            if let Some(t) = self.inner().waiting.take(Ordering::Acquire) {
                Waiting::trigger(t, self.id());
            }
        }

        if old.is_some() {
            panic!("Pulse cannot be waited on by multiple clients");
        }
    }

    /// Disarm a pulse
    fn disarm(&self) {
        self.inner().waiting.take(Ordering::Acquire);
    }

    /// Wait for an pulse to be triggered
    ///
    /// Panics if something else is waiting on this already
    pub fn wait(&self) -> Result<(), WaitError> {
        loop {
            if !self.is_pending() {
                let state = self.state();
                return if (state & PULSED) == PULSED {
                    Ok(())
                } else {
                    Err(WaitError(state))
                };
            }

            self.arm(Box::new(Waiting::thread()));

            if self.is_pending() {
                // wait for wake
                thread::park();
            }
            self.disarm();
        }
    }

    pub fn id(&self) -> usize {
        unsafe { mem::transmute_copy(&self.0) }
    }

    pub fn recycle(&self) -> Trigger {
        if self.is_pending() {
            panic!("Attempted to recycle pending Pulse")
        }
        self.inner().state.store(0, Ordering::Relaxed);
        Trigger(self.0)
    }

    pub fn on_complete<F>(self, f: F) where F: FnOnce() + Send + 'static {
        self.arm(Box::new(Waiting::Callback(Box::new(f))));
    }
}

#[derive(Debug)]
pub struct WaitError(usize);<|endoftext|>


<commit_before>// The MIT License (MIT)
//
// Copyright (c) 2015 FaultyRAM
//
// 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.

//! ngLog processing utilities.
//!
//! ngLog is a textual file format designed for recording gameplay events. Each
//! line of text in an ngLog-formatted file represents an *event*, consisting
//! of several parameters in the following order, each separated by an ASCII
//! TAB control code:
//!     * A timestamp: a floating-point number representing the time in
//!       seconds that have elapsed since gameplay began;
//!     * An optional event class, describing the category to which the event
//!       belongs;
//!     * An event ID, describing the type of event which occurred;
//!     * Zero or more *event parameters*, each representing an arbitrary data
//!       point associated with the event.
//! ngLog was used in conjunction with ngStats and ngWorldStats to provide both
//! local and online statistical analysis and tracking. Supported video games
//! would create two copies of an ngLog file upon gameplay completion: a copy
//! for local processing, and an encoded copy to be sent to a *world server*.
//! This crate provides functionality for processing both forms, from either a
//! `String` or a type that implements `std::io::Read`. For example:
//!
//! ```rust
//! use ng_log::NgLog;
//!
//! use std::fs::OpenOptions;
//! use std::io::Read;
//! use std::string::ToString;
//!
//! let mut file = OpenOptions::new()
//! 	.read(true)
//! 	.open("./tests/ngLog_Example_Log_File.log.txt")
//! 	.unwrap();
//! let log = NgLog::local_from_reader(&mut file).unwrap();
//! println!("{}", log.to_string());
//! ```

use std::io::Error as IoError;
use std::io::ErrorKind as IoErrorKind;
use std::io::Result as IoResult;
use std::io::Read;
use std::string::ToString;

pub struct NgLog {
	pub events: Vec<NgEvent>,
}

pub struct NgEvent {
	pub timestamp:    String,
	pub event_class:  Option<String>,
	pub event_id:     String,
	pub event_params: Vec<String>,
}

impl NgLog {
	pub fn new(capacity: usize) -> NgLog {
		NgLog {
			events: Vec::with_capacity(capacity),
		}
	}

	pub fn local_from_reader<T>(reader: &mut T) -> IoResult<NgLog> where
	T: Read {
		let mut data: Vec<u8> = Vec::with_capacity(0);
		try!(reader.read_to_end(&mut data));
		NgLog::from_string(&try!(String::from_utf8(data).map_err(|e|
			IoError::new(IoErrorKind::InvalidData, format!("{}", e))
		)))
	}

	pub fn world_from_reader<T>(reader: &mut T) -> IoResult<NgLog> where
	T: Read {
		let mut data: Vec<u8> = Vec::with_capacity(0);
		try!(reader.read_to_end(&mut data));
		if data.len() % 2 != 0 {
			return Err(IoError::new(IoErrorKind::InvalidData, "Non-even log length"))
		}
		// TODO: check if this format is used by games other than UT99.
		let mut decoded: Vec<u8> = Vec::with_capacity(data.len() / 2);
		for v in data.chunks(2) {
			decoded.push(v[0] ^ v[1]);
		}
		NgLog::from_string(&try!(String::from_utf8(decoded).map_err(|e|
			IoError::new(IoErrorKind::InvalidData, format!("{}", e))
		)))
	}

	pub fn from_string(s: &String) -> IoResult<NgLog> {
		let mut log = NgLog::new(s.len());
		for line in s.lines() {
			let event = try!(NgEvent::from_string(&String::from(line)));
			log.events.push(event);
		}
		Ok(log)
	}
}

impl ToString for NgLog {
	fn to_string(&self) -> String {
		let mut s = String::with_capacity(0);
		for v in &self.events {
			s = s + &v.to_string() + &"\n";
		}
		s
	}
}

impl NgEvent {
	pub fn new(timestamp: String, class: Option<String>, id: String, params: Vec<String>) -> NgEvent {
		NgEvent {
			timestamp:    timestamp,
			event_class:  class,
			event_id:     id,
			event_params: params,
		}
	}

	pub fn from_string(s: &String) -> IoResult<NgEvent> {
		let mut columns: Vec<String> = s.split('\t').map(|s| String::from(s)).collect();
		if columns.len() < 2 {
			return Err(IoError::new(IoErrorKind::InvalidData, "Bad event string"))
		} else if columns.len() == 2 {
			Ok(NgEvent::new(
				columns.remove(0),
				None,
				columns.remove(0),
				Vec::with_capacity(0)
			))
		} else {
			Ok(NgEvent::new(
				columns.remove(0),
				Some(columns.remove(0)),
				columns.remove(0),
				columns.clone()
			))
		}
	}
}

impl ToString for NgEvent {
	fn to_string(&self) -> String {
		let mut s = String::with_capacity(0);
		s = s + &self.timestamp;
		if let Some(v) = self.event_class.clone() {
			s = s + &"\t" + &v;
		}
		s = s + &"\t" + &self.event_id;
		for v in self.event_params.iter() {
			s = s + &"\t" + &v;
		}
		s
	}
}
<commit_msg>More formatting<commit_after>// The MIT License (MIT)
//
// Copyright (c) 2015 FaultyRAM
//
// 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.

//! ngLog processing utilities.
//!
//! ngLog is a textual file format designed for recording gameplay events. Each
//! line of text in an ngLog-formatted file represents an *event*, consisting
//! of several parameters in the following order, each separated by an ASCII
//! TAB control code:
//!
//! * A *timestamp*: a floating-point number representing the time in seconds
//!   that have elapsed since gameplay began;
//! * An optional *event class*, describing the category to which the event
//!   belongs;
//! * An *event ID*, describing the type of event which occurred;
//! * Zero or more *event parameters*, each representing an arbitrary data
//!   point associated with the event.
//!
//! ngLog was used in conjunction with ngStats and ngWorldStats to provide both
//! local and online statistical analysis and tracking. Supported video games
//! would create two copies of an ngLog file upon gameplay completion: a copy
//! for local processing, and an encoded copy to be sent to a *world server*.
//! This crate provides functionality for processing both forms, from either a
//! `String` or a type that implements `std::io::Read`. For example:
//!
//! ```rust
//! use ng_log::NgLog;
//!
//! use std::fs::OpenOptions;
//! use std::io::Read;
//! use std::string::ToString;
//!
//! let mut file = OpenOptions::new()
//! 	.read(true)
//! 	.open("./tests/ngLog_Example_Log_File.log.txt")
//! 	.unwrap();
//! let log = NgLog::local_from_reader(&mut file).unwrap();
//! println!("{}", log.to_string());
//! ```

use std::io::Error as IoError;
use std::io::ErrorKind as IoErrorKind;
use std::io::Result as IoResult;
use std::io::Read;
use std::string::ToString;

pub struct NgLog {
	pub events: Vec<NgEvent>,
}

pub struct NgEvent {
	pub timestamp:    String,
	pub event_class:  Option<String>,
	pub event_id:     String,
	pub event_params: Vec<String>,
}

impl NgLog {
	pub fn new(capacity: usize) -> NgLog {
		NgLog {
			events: Vec::with_capacity(capacity),
		}
	}

	pub fn local_from_reader<T>(reader: &mut T) -> IoResult<NgLog> where
	T: Read {
		let mut data: Vec<u8> = Vec::with_capacity(0);
		try!(reader.read_to_end(&mut data));
		NgLog::from_string(&try!(String::from_utf8(data).map_err(|e|
			IoError::new(IoErrorKind::InvalidData, format!("{}", e))
		)))
	}

	pub fn world_from_reader<T>(reader: &mut T) -> IoResult<NgLog> where
	T: Read {
		let mut data: Vec<u8> = Vec::with_capacity(0);
		try!(reader.read_to_end(&mut data));
		if data.len() % 2 != 0 {
			return Err(IoError::new(IoErrorKind::InvalidData, "Non-even log length"))
		}
		// TODO: check if this format is used by games other than UT99.
		let mut decoded: Vec<u8> = Vec::with_capacity(data.len() / 2);
		for v in data.chunks(2) {
			decoded.push(v[0] ^ v[1]);
		}
		NgLog::from_string(&try!(String::from_utf8(decoded).map_err(|e|
			IoError::new(IoErrorKind::InvalidData, format!("{}", e))
		)))
	}

	pub fn from_string(s: &String) -> IoResult<NgLog> {
		let mut log = NgLog::new(s.len());
		for line in s.lines() {
			let event = try!(NgEvent::from_string(&String::from(line)));
			log.events.push(event);
		}
		Ok(log)
	}
}

impl ToString for NgLog {
	fn to_string(&self) -> String {
		let mut s = String::with_capacity(0);
		for v in &self.events {
			s = s + &v.to_string() + &"\n";
		}
		s
	}
}

impl NgEvent {
	pub fn new(timestamp: String, class: Option<String>, id: String, params: Vec<String>) -> NgEvent {
		NgEvent {
			timestamp:    timestamp,
			event_class:  class,
			event_id:     id,
			event_params: params,
		}
	}

	pub fn from_string(s: &String) -> IoResult<NgEvent> {
		let mut columns: Vec<String> = s.split('\t').map(|s| String::from(s)).collect();
		if columns.len() < 2 {
			return Err(IoError::new(IoErrorKind::InvalidData, "Bad event string"))
		} else if columns.len() == 2 {
			Ok(NgEvent::new(
				columns.remove(0),
				None,
				columns.remove(0),
				Vec::with_capacity(0)
			))
		} else {
			Ok(NgEvent::new(
				columns.remove(0),
				Some(columns.remove(0)),
				columns.remove(0),
				columns.clone()
			))
		}
	}
}

impl ToString for NgEvent {
	fn to_string(&self) -> String {
		let mut s = String::with_capacity(0);
		s = s + &self.timestamp;
		if let Some(v) = self.event_class.clone() {
			s = s + &"\t" + &v;
		}
		s = s + &"\t" + &self.event_id;
		for v in self.event_params.iter() {
			s = s + &"\t" + &v;
		}
		s
	}
}
<|endoftext|>


<commit_before>//! Parse and write Iridium Short Burst Data (SBD) messages.
//!
//! Iridium is both a [satellite constellation](https://en.wikipedia.org/wiki/Iridium_satellite_constellation)
//! and a [company](https://en.wikipedia.org/wiki/Iridium_Communications) that provides satellite
//! communications. The Iridium network is used by phones, modems, and other communication devices.
//!
//! One mode of transmitting data over the Iridium network is via Short Burst Data (SBD) messages.
//! These messages carry a payload of some small number of bytes, usually less than one thousand.
//! Messages can be Mobile-Originated (MO), meaning that they are sent *from* an Iridium modem, or
//! Mobile-Terminated (MT), meaning that the are sent *to* an Iridium modem. Mobile-originated
//! messages are delivered either to an email address via MIME attachment, or directly to a given
//! IP address and port via TCP; this second method is called DirectIP.
//!
//! This is a simple library for reading mobile originated SBD messages from a stream, decoding
//! their headers and data payloads, and writing them back to a stream. This library does not
//! handle mobile terminated messages.
//!
//! Most of the functionality of this library is exposed by a single executable, named `sbd` that
//! is part of this package. Use the `sbd` executable to inspect raw sbd files stores on a
//! filesystem, discover sbd files on a filesystem, and start a forever-running server to receive
//! Iridium SBD DirectIP messages.

#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs, trivial_casts,
        trivial_numeric_casts, unsafe_code, unstable_features, unused_extern_crates,
        unused_import_braces, unused_qualifications)]

pub mod directip;
mod information_element;
pub mod logger;
pub mod filesystem;
pub mod message;

pub use message::Message;

extern crate byteorder;
extern crate chrono;
extern crate glob;
#[macro_use] extern crate log;

use std::result;

/// Crate-specific errors
#[derive(Debug)]
pub enum SbdError {
    /// An error while reading bytes from a stream with the byteorder crate.
    ByteorderError(byteorder::Error),
    /// A wrapper around a std::io::Error.
    IoError(std::io::Error),
    /// Invalid IMEI number.
    InvalidImei,
    /// Invalid protocol revision number.
    InvalidProtocolRevisionNumber(u8),
    /// Wrapper around a glob error.
    GlobError(glob::GlobError),
    /// Missing mobile originated header.
    MissingMobileOriginatedHeader,
    /// Missing mobile originated payload.
    MissingMobileOriginatedPayload,
    /// An oversized message.
    ///
    /// Oversized doesn't demand a size since we don't want to find out how much there really is.
    Oversized,
    /// Wrapper around a glob::PatternError.
    PatternError(glob::PatternError),
    /// An undersized message.
    Undersized(usize),
}

impl From<byteorder::Error> for SbdError {
    fn from(err: byteorder::Error) -> SbdError {
        SbdError::ByteorderError(err)
    }
}

impl From<glob::PatternError> for SbdError {
    fn from(err: glob::PatternError) -> SbdError {
        SbdError::PatternError(err)
    }
}

impl From<glob::GlobError> for SbdError {
    fn from(err: glob::GlobError) -> SbdError {
        SbdError::GlobError(err)
    }
}

impl From<std::io::Error> for SbdError {
    fn from(err: std::io::Error) -> SbdError {
        SbdError::IoError(err)
    }
}

/// Create-specific `Result`.
pub type Result<T> = result::Result<T, SbdError>;
<commit_msg>Remove Error suffix from some custom errors<commit_after>//! Parse and write Iridium Short Burst Data (SBD) messages.
//!
//! Iridium is both a [satellite constellation](https://en.wikipedia.org/wiki/Iridium_satellite_constellation)
//! and a [company](https://en.wikipedia.org/wiki/Iridium_Communications) that provides satellite
//! communications. The Iridium network is used by phones, modems, and other communication devices.
//!
//! One mode of transmitting data over the Iridium network is via Short Burst Data (SBD) messages.
//! These messages carry a payload of some small number of bytes, usually less than one thousand.
//! Messages can be Mobile-Originated (MO), meaning that they are sent *from* an Iridium modem, or
//! Mobile-Terminated (MT), meaning that the are sent *to* an Iridium modem. Mobile-originated
//! messages are delivered either to an email address via MIME attachment, or directly to a given
//! IP address and port via TCP; this second method is called DirectIP.
//!
//! This is a simple library for reading mobile originated SBD messages from a stream, decoding
//! their headers and data payloads, and writing them back to a stream. This library does not
//! handle mobile terminated messages.
//!
//! Most of the functionality of this library is exposed by a single executable, named `sbd` that
//! is part of this package. Use the `sbd` executable to inspect raw sbd files stores on a
//! filesystem, discover sbd files on a filesystem, and start a forever-running server to receive
//! Iridium SBD DirectIP messages.

#![deny(missing_copy_implementations, missing_debug_implementations, missing_docs, trivial_casts,
        trivial_numeric_casts, unsafe_code, unstable_features, unused_extern_crates,
        unused_import_braces, unused_qualifications)]

pub mod directip;
mod information_element;
pub mod logger;
pub mod filesystem;
pub mod message;

pub use message::Message;

extern crate byteorder;
extern crate chrono;
extern crate glob;
#[macro_use] extern crate log;

use std::result;

/// Crate-specific errors
#[derive(Debug)]
pub enum SbdError {
    /// An error while reading bytes from a stream with the byteorder crate.
    Byteorder(byteorder::Error),
    /// A wrapper around a std::io::Error.
    Io(std::io::Error),
    /// Invalid IMEI number.
    InvalidImei,
    /// Invalid protocol revision number.
    InvalidProtocolRevisionNumber(u8),
    /// Wrapper around a glob error.
    Glob(glob::GlobError),
    /// Missing mobile originated header.
    MissingMobileOriginatedHeader,
    /// Missing mobile originated payload.
    MissingMobileOriginatedPayload,
    /// An oversized message.
    ///
    /// Oversized doesn't demand a size since we don't want to find out how much there really is.
    Oversized,
    /// Wrapper around a glob::PatternError.
    Pattern(glob::PatternError),
    /// An undersized message.
    Undersized(usize),
}

impl From<byteorder::Error> for SbdError {
    fn from(err: byteorder::Error) -> SbdError {
        SbdError::Byteorder(err)
    }
}

impl From<glob::PatternError> for SbdError {
    fn from(err: glob::PatternError) -> SbdError {
        SbdError::Pattern(err)
    }
}

impl From<glob::GlobError> for SbdError {
    fn from(err: glob::GlobError) -> SbdError {
        SbdError::Glob(err)
    }
}

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

/// Create-specific `Result`.
pub type Result<T> = result::Result<T, SbdError>;
<|endoftext|>


<commit_before>#![feature(plugin_registrar, rustc_private)]

extern crate rustc;
extern crate rustc_plugin;
extern crate syntax;

use rustc_plugin::Registry;
use std::rc::Rc;
use syntax::ast::TokenTree;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult};
use syntax::parse::token::{self, DelimToken, Token, IdentStyle};

mod parser_any_macro;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("interpolate_idents", interpolate_idents);
}

fn interpolate_idents<'a>(cx: &'a mut ExtCtxt,
              _: Span,
              tts: &[TokenTree]) -> Box<MacResult + 'a> {
    fn concat_idents(tts: &Vec<TokenTree>, delim: DelimToken) -> Option<TokenTree> {
        match delim {
            DelimToken::Bracket => {
                let mut new_ident = String::new();
                let mut new_span: Option<Span> = None;

                for token in tts.iter() {
                    match token {
                        &TokenTree::Token(ref span, Token::Ident(ref ident, IdentStyle::Plain)) => {
                            match new_span {
                                Some(ref mut s) => { s.hi = span.hi; },
                                None => { new_span = Some(span.clone()); },
                            }
                            new_ident.push_str(&ident.name.as_str());
                        },
                        _ => return None,
                    }
                }

                match new_span {
                    Some(s) => {
                        let new_ident = token::str_to_ident(&new_ident[..]);
                        Some(TokenTree::Token(s, Token::Ident(new_ident, IdentStyle::Plain)))
                    },
                    None => None
                }
            },
            _ => None,
        }
    }

    fn map_tts(tts: &[TokenTree]) -> Vec<TokenTree> {
        tts.iter().map(|t| {
            match t {
                &TokenTree::Delimited(ref s, ref d) => {
                    match concat_idents(&d.tts, d.delim) {
                        Some(t) => t,
                        None => {
                            TokenTree::Delimited(s.clone(), Rc::new(syntax::ast::Delimited {
                                delim: d.delim,
                                open_span: d.open_span,
                                tts: map_tts(&*d.tts),
                                close_span: d.close_span,
                            }))
                        },
                    }
                },
                &TokenTree::Sequence(ref s, ref d) => {
                    TokenTree::Sequence(s.clone(), Rc::new(syntax::ast::SequenceRepetition {
                        tts: map_tts(&*d.tts),
                        separator: d.separator.clone(),
                        op: d.op,
                        num_captures: d.num_captures,
                    }))
                },
                _ => t.clone(),
            }
        }).collect()
    }

    let parser = cx.new_parser_from_tts(&map_tts(tts)[..]);
    Box::new(parser_any_macro::ParserAnyMacro::new(parser)) as Box<MacResult>
}
<commit_msg>Update for nightly 2/22/2016 (476fe6eef).<commit_after>#![feature(plugin_registrar, rustc_private)]

extern crate rustc;
extern crate rustc_plugin;
extern crate syntax;

use rustc_plugin::Registry;
use std::rc::Rc;
use syntax::ast::TokenTree;
use syntax::codemap::Span;
use syntax::ext::base::{ExtCtxt, MacResult};
use syntax::parse::token::{self, DelimToken, Token};

mod parser_any_macro;

#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
    reg.register_macro("interpolate_idents", interpolate_idents);
}

fn interpolate_idents<'a>(cx: &'a mut ExtCtxt,
              _: Span,
              tts: &[TokenTree]) -> Box<MacResult + 'a> {
    fn concat_idents(tts: &Vec<TokenTree>, delim: DelimToken) -> Option<TokenTree> {
        match delim {
            DelimToken::Bracket => {
                let mut new_ident = String::new();
                let mut new_span: Option<Span> = None;

                for token in tts.iter() {
                    match token {
                        &TokenTree::Token(ref span, Token::Ident(ref ident)) => {
                            match new_span {
                                Some(ref mut s) => { s.hi = span.hi; },
                                None => { new_span = Some(span.clone()); },
                            }
                            new_ident.push_str(&ident.name.as_str());
                        },
                        _ => return None,
                    }
                }

                match new_span {
                    Some(s) => {
                        let new_ident = token::str_to_ident(&new_ident[..]);
                        Some(TokenTree::Token(s, Token::Ident(new_ident)))
                    },
                    None => None
                }
            },
            _ => None,
        }
    }

    fn map_tts(tts: &[TokenTree]) -> Vec<TokenTree> {
        tts.iter().map(|t| {
            match t {
                &TokenTree::Delimited(ref s, ref d) => {
                    match concat_idents(&d.tts, d.delim) {
                        Some(t) => t,
                        None => {
                            TokenTree::Delimited(s.clone(), Rc::new(syntax::ast::Delimited {
                                delim: d.delim,
                                open_span: d.open_span,
                                tts: map_tts(&*d.tts),
                                close_span: d.close_span,
                            }))
                        },
                    }
                },
                &TokenTree::Sequence(ref s, ref d) => {
                    TokenTree::Sequence(s.clone(), Rc::new(syntax::ast::SequenceRepetition {
                        tts: map_tts(&*d.tts),
                        separator: d.separator.clone(),
                        op: d.op,
                        num_captures: d.num_captures,
                    }))
                },
                _ => t.clone(),
            }
        }).collect()
    }

    let parser = cx.new_parser_from_tts(&map_tts(tts)[..]);
    Box::new(parser_any_macro::ParserAnyMacro::new(parser)) as Box<MacResult>
}
<|endoftext|>