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

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

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

// Import necessary types from crates.

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

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

// Define service ID for the service trait.

const SERVICE_ID: u16 = 1;

// Define constants for transaction types within the service.

const TX_CREATE_WALLET_ID: u16 = 1;

const TX_TRANSFER_ID: u16 = 2;

// Define initial balance of a newly created wallet.

const INIT_BALANCE: u64 = 100;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

/// Define the service.
pub struct CurrencyService;

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

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

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

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

//! Demo [Exonum][exonum] service implementing a simplified cryptocurrency.
//! See [the documentation][docs] for a detailed step-by-step guide how to approach this demo,
//! and [the reporitory README][readme] on how to use, test, and contribute to it.
//!
//! **Note.** The service in this crate is intended for demo purposes only. It is not intended
//! for use in production.
//!
//! [exonum]: https://github.com/exonum/exonum
//! [docs]: https://exonum.com/doc/get-started/create-service
//! [readme]: https://github.com/exonum/cryptocurrency#readme

#![deny(missing_docs)]

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

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

// Import necessary types from crates.

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

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

/// Service ID for the `Service` trait.
const SERVICE_ID: u16 = 1;

// Constants for transaction types within the service.
const TX_CREATE_WALLET_ID: u16 = 1;
const TX_TRANSFER_ID: u16 = 2;

/// Initial balance of a newly created wallet.
const INIT_BALANCE: u64 = 100;

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

// Declare the data to be stored in the blockchain, namely wallets with balances.
// See [serialization docs][1] for details.
//
// [1]: https://exonum.com/doc/architecture/serialization

encoding_struct! {
    /// Wallet struct used to persist data within the service.
    struct Wallet {
        const SIZE = 48;

        /// Public key of the wallet owner.
        field pub_key:            &PublicKey  [00 => 32]
        /// Name of the wallet owner.
        field name:               &str        [32 => 40]
        /// Current balance.
        field balance:            u64         [40 => 48]
    }
}

/// Additional methods for managing balance of the wallet in an immutable fashion.
impl Wallet {
    /// Returns a copy of this wallet with the balance increased by the specified amount.
    pub fn increase(self, amount: u64) -> Self {
        let balance = self.balance() + amount;
        Self::new(self.pub_key(), self.name(), balance)
    }

    /// Returns a copy of this wallet with the balance decreased by the specified amount.
    pub fn decrease(self, amount: u64) -> Self {
        debug_assert!(self.balance() >= amount);
        let balance = self.balance() - amount;
        Self::new(self.pub_key(), self.name(), balance)
    }
}

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

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

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

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

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

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

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

message! {
    /// Transaction type for creating a new wallet.
    ///
    /// See [the `Transaction` trait implementation](#impl-Transaction) for details how
    /// `TxCreateWallet` transactions are processed.
    struct TxCreateWallet {
        const TYPE = SERVICE_ID;
        const ID = TX_CREATE_WALLET_ID;
        const SIZE = 40;

        /// Public key of the wallet's owner.
        field pub_key:     &PublicKey  [00 => 32]
        /// UTF-8 string with the owner's name.
        field name:        &str        [32 => 40]
    }
}

message! {
    /// Transaction type for transferring tokens between two wallets.
    ///
    /// See [the `Transaction` trait implementation](#impl-Transaction) for details how
    /// `TxTransfer` transactions are processed.
    struct TxTransfer {
        const TYPE = SERVICE_ID;
        const ID = TX_TRANSFER_ID;
        const SIZE = 80;

        /// Public key of the sender.
        field from:        &PublicKey  [00 => 32]
        /// Public key of the receiver.
        field to:          &PublicKey  [32 => 64]
        /// Number of tokens to transfer from sender's account to receiver's account.
        field amount:      u64         [64 => 72]
        /// Auxiliary number to guarantee [non-idempotence][idempotence] of transactions.
        ///
        /// [idempotence]: https://en.wikipedia.org/wiki/Idempotence
        field seed:        u64         [72 => 80]
    }
}

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

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

    /// If a wallet with the specified public key is not registered, then creates a new wallet
    /// with the specified public key and name, and an initial balance of 100.
    /// Otherwise, performs no op.
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema::new(view);
        if schema.wallet(self.pub_key()).is_none() {
            let wallet = Wallet::new(self.pub_key(), self.name(), INIT_BALANCE);
            println!("Create the wallet: {:?}", wallet);
            schema.wallets_mut().put(self.pub_key(), wallet)
        }
    }

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

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

    /// Retrieves two wallets to apply the transfer; they should be previously registered
    /// with the help of [`TxCreateWallet`] transactions. Checks the sender's
    /// balance and applies changes to the balances of the wallets if the sender's balance
    /// is sufficient. Otherwise, performs no op.
    ///
    /// [`TxCreateWallet`]: struct.TxCreateWallet.html
    fn execute(&self, view: &mut Fork) {
        let mut schema = CurrencySchema::new(view);
        let sender = schema.wallet(self.from());
        let receiver = schema.wallet(self.to());
        if let (Some(sender), Some(receiver)) = (sender, receiver) {
            let amount = self.amount();
            if sender.balance() >= amount {
                let sender = sender.decrease(amount);
                let receiver = receiver.increase(amount);
                println!("Transfer between wallets: {:?} => {:?}", sender, receiver);
                let mut wallets = schema.wallets_mut();
                wallets.put(self.from(), sender);
                wallets.put(self.to(), receiver);
            }
        }
    }

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

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

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

/// The structure returned by the REST API in response to transaction POST requests.
#[derive(Serialize, Deserialize)]
pub struct TransactionResponse {
    /// Hash of the `POST`ed transaction.
    pub tx_hash: Hash,
}

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

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

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

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

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

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

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

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

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

/// Demo cryptocurrency service.
///
/// See [the crate documentation](index.html) for context.
///
/// # Public REST API
///
/// In all APIs, the request body (if applicable) and response are JSON-encoded.
///
/// ## Retrieve single wallet
///
/// GET `v1/wallet/:pub_key`
///
/// Returns information about a wallet with the specified public key (hex-encoded).
/// If a wallet with the specified pubkey is not in the storage, returns a string
/// `"Wallet not found"` with the HTTP 404 status.
///
/// ## Dump wallets
///
/// GET `v1/wallets`
///
/// Returns an array of all wallets in the storage.
///
/// ## Create new wallet
///
/// POST `v1/wallets`
///
/// Accepts a [`TxCreateWallet`] transaction from an external client. Returns the hex-encoded
/// hash of the transaction encumbered in an object: `{ "tx_hash": <hash> }`.
///
/// ## Transfer between wallets
///
/// POST `v1/wallets/transfer`
///
/// Accepts a [`TxTransfer`] transaction from an external client. Returns the hex-encoded
/// hash of the transaction encumbered in an object: `{ "tx_hash": <hash> }`.
///
/// [`TxCreateWallet`]: struct.TxCreateWallet.html
/// [`TxTransfer`]: struct.TxTransfer.html
pub struct CurrencyService;

impl Service for CurrencyService {
    fn service_name(&self) -> &'static str {
        "cryptocurrency"
    }

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

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

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


<commit_before>#![doc(html_root_url = "https://radare.github.io/radeco/")]
pub mod frontend;
pub mod backend;
<commit_msg>Added logo to lib.rs<commit_after>#![doc(html_root_url = "https://radare.github.io/radeco/")]
#![doc(html_logo_url = "http://rada.re/r/img/r2logo3.png")]

pub mod frontend;
pub mod backend;
<|endoftext|>


<commit_before>use std::sync::Arc;

pub type Action<E> = Box<Fn(E)>;
type StoredAction<E> = Arc<Action<E>>;

#[derive(Clone)]
struct Transition<S: Into<u32> + Clone, E: Into<u32> + Clone> {
	event_type: E,
	next_state: S,
	action: StoredAction<E>,
}

#[derive(Clone)]
struct StateTransitions<S: Into<u32> + Clone, E: Into<u32> + Clone> {
	start_state: S,
	edges: Vec<Option<Transition<S, E>>>,
}

#[derive(Clone)]
pub struct Machine<S: Into<u32> + Clone, E: Into<u32> + Clone> {
	state: S,
	transitions: Vec<Option<StateTransitions<S, E>>>,
}

impl<S: Into<u32> + Clone, E: Into<u32> + Clone> Machine<S, E> {
	pub fn new(initial_state: S) -> Machine<S, E> {
		Machine {
			state: initial_state,
			transitions: Vec::new(),
		}
	}

	pub fn add_transition(&mut self, in_state: S, on_event: E, next_state: S, action: Action<E>) -> bool {
		let state_index: u32 = in_state.clone().into();
		let event_index: u32 = on_event.clone().into();
		let mut result = false;

		if state_index as usize >= self.transitions.capacity() {
			let new_len = self.transitions.len() * 2;
			self.transitions.reserve(new_len);
		}
		while state_index as usize >= self.transitions.len() {
			self.transitions.push(None);
		}

		let create_state_entry = match self.transitions[state_index as usize] {
			Some(_) => false,
			None => true,
		};

		if create_state_entry {
			self.transitions[state_index as usize] = Some(StateTransitions {
				start_state: in_state,
				edges: Vec::new(),
			});
		}

		if let &mut Some(ref mut transitions) = &mut self.transitions[state_index as usize] {
			if event_index as usize >= transitions.edges.len() {
				let new_len = transitions.edges.len() * 2;
				transitions.edges.reserve(new_len);
			}
			while event_index as usize >= transitions.edges.len() {
				transitions.edges.push(None);
			}

			let transition = &mut transitions.edges[event_index as usize];

			*transition = match &*transition {
				&Some(ref t) => Some((*t).clone()),
				&None => {
					result = true;
					Some(Transition {
						event_type: on_event,
						next_state: next_state,
						action: Arc::new(action),
					})
				}
			};
		}

		result
	}

	pub fn current_state(&self) -> &S {
		&self.state
	}

	pub fn on_event(&mut self, event_type: E) {
		let event_index: u32 = event_type.clone().into();
		let state_index: u32 = self.state.clone().into();

		if state_index as usize >= self.transitions.len() {
			return;
		}

		if let &Some(ref state_transitions) = &self.transitions[state_index as usize] {
			if event_index as usize >= state_transitions.edges.len() {
				return;
			}

			if let &Some(ref transition) = &state_transitions.edges[event_index as usize] {
				self.state = transition.next_state.clone();
				(*(*transition).action)(event_type);
			}
		}
	}
}

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

	#[derive(Clone, Debug, Eq, PartialEq)]
	enum TurnStyleState {
		Locked,
		Unlocked,
	}

	#[derive(Clone, Debug, Eq, PartialEq)]
	enum TurnStyleEvent {
		Push,
		InsertCoin,
	}

	impl Into<u32> for TurnStyleState {
		fn into(self) -> u32 {
			self as u32
		}
	}

	impl Into<u32> for TurnStyleEvent {
		fn into(self) -> u32 {
			self as u32
		}
	}

	#[test]
	fn test_machine() {
		let mut machine = Machine::new(TurnStyleState::Locked);
		machine.add_transition(
			TurnStyleState::Locked, TurnStyleEvent::InsertCoin,
			TurnStyleState::Unlocked, Box::new(|_| println!("unlocked"))
		);
		machine.add_transition(
			TurnStyleState::Unlocked, TurnStyleEvent::Push,
			TurnStyleState::Locked, Box::new(|_| println!("locked"))
		);
		assert!(*machine.current_state() == TurnStyleState::Locked);
		machine.on_event(TurnStyleEvent::Push);
		assert!(*machine.current_state() == TurnStyleState::Locked);
		machine.on_event(TurnStyleEvent::InsertCoin);
		assert!(*machine.current_state() == TurnStyleState::Unlocked);
		machine.on_event(TurnStyleEvent::InsertCoin);
		assert!(*machine.current_state() == TurnStyleState::Unlocked);
		machine.on_event(TurnStyleEvent::Push);
		assert!(*machine.current_state() == TurnStyleState::Locked);
	}
}
<commit_msg>added documentation strings, but no examples yet<commit_after>use std::sync::Arc;

/// Actions are just boxed immutable functions that take an argument of the event that triggered them
pub type Action<E> = Box<Fn(E)>;
/// StoredActions just enable structures holding the Action to be cloned
type StoredAction<E> = Arc<Action<E>>;

/// The Transition records, for a given current state, what event type triggers it to move to
/// what state, performing a specific action on the transition
#[derive(Clone)]
struct Transition<S: Into<u32> + Clone, E: Into<u32> + Clone> {
	event_type: E,
	next_state: S,
	action: StoredAction<E>,
}

/// The StateTransition records all Transitions for a given state
#[derive(Clone)]
struct StateTransitions<S: Into<u32> + Clone, E: Into<u32> + Clone> {
	start_state: S,
	edges: Vec<Option<Transition<S, E>>>,
}

/// The Machine is the Finite State Machine, which has a current state and set of all valid
/// transitions
#[derive(Clone)]
pub struct Machine<S: Into<u32> + Clone, E: Into<u32> + Clone> {
	state: S,
	transitions: Vec<Option<StateTransitions<S, E>>>,
}

impl<S: Into<u32> + Clone, E: Into<u32> + Clone> Machine<S, E> {
	/// Constructs a new FSM with a given initial state
	pub fn new(initial_state: S) -> Machine<S, E> {
		Machine {
			state: initial_state,
			transitions: Vec::new(),
		}
	}

	/// Registers a new valid transition with the FSM
	pub fn add_transition(&mut self, in_state: S, on_event: E, next_state: S, action: Action<E>) -> bool {
		let state_index: u32 = in_state.clone().into();
		let event_index: u32 = on_event.clone().into();
		let mut result = false;

		if state_index as usize >= self.transitions.capacity() {
			let new_len = self.transitions.len() * 2;
			self.transitions.reserve(new_len);
		}
		while state_index as usize >= self.transitions.len() {
			self.transitions.push(None);
		}

		let create_state_entry = match self.transitions[state_index as usize] {
			Some(_) => false,
			None => true,
		};

		if create_state_entry {
			self.transitions[state_index as usize] = Some(StateTransitions {
				start_state: in_state,
				edges: Vec::new(),
			});
		}

		if let &mut Some(ref mut transitions) = &mut self.transitions[state_index as usize] {
			if event_index as usize >= transitions.edges.len() {
				let new_len = transitions.edges.len() * 2;
				transitions.edges.reserve(new_len);
			}
			while event_index as usize >= transitions.edges.len() {
				transitions.edges.push(None);
			}

			let transition = &mut transitions.edges[event_index as usize];

			*transition = match &*transition {
				&Some(ref t) => Some((*t).clone()),
				&None => {
					result = true;
					Some(Transition {
						event_type: on_event,
						next_state: next_state,
						action: Arc::new(action),
					})
				}
			};
		}

		result
	}

	/// Retrieves a reference to the current state
	pub fn current_state(&self) -> &S {
		&self.state
	}

	/// Tick the State Machine with an Event
	pub fn on_event(&mut self, event_type: E) {
		let event_index: u32 = event_type.clone().into();
		let state_index: u32 = self.state.clone().into();

		if state_index as usize >= self.transitions.len() {
			return;
		}

		if let &Some(ref state_transitions) = &self.transitions[state_index as usize] {
			if event_index as usize >= state_transitions.edges.len() {
				return;
			}

			if let &Some(ref transition) = &state_transitions.edges[event_index as usize] {
				self.state = transition.next_state.clone();
				(*(*transition).action)(event_type);
			}
		}
	}
}

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

	#[derive(Clone, Debug, Eq, PartialEq)]
	enum TurnStyleState {
		Locked,
		Unlocked,
	}

	#[derive(Clone, Debug, Eq, PartialEq)]
	enum TurnStyleEvent {
		Push,
		InsertCoin,
	}

	impl Into<u32> for TurnStyleState {
		fn into(self) -> u32 {
			self as u32
		}
	}

	impl Into<u32> for TurnStyleEvent {
		fn into(self) -> u32 {
			self as u32
		}
	}

	#[test]
	fn test_machine() {
		let mut machine = Machine::new(TurnStyleState::Locked);
		machine.add_transition(
			TurnStyleState::Locked, TurnStyleEvent::InsertCoin,
			TurnStyleState::Unlocked, Box::new(|_| println!("unlocked"))
		);
		machine.add_transition(
			TurnStyleState::Unlocked, TurnStyleEvent::Push,
			TurnStyleState::Locked, Box::new(|_| println!("locked"))
		);
		assert!(*machine.current_state() == TurnStyleState::Locked);
		machine.on_event(TurnStyleEvent::Push);
		assert!(*machine.current_state() == TurnStyleState::Locked);
		machine.on_event(TurnStyleEvent::InsertCoin);
		assert!(*machine.current_state() == TurnStyleState::Unlocked);
		machine.on_event(TurnStyleEvent::InsertCoin);
		assert!(*machine.current_state() == TurnStyleState::Unlocked);
		machine.on_event(TurnStyleEvent::Push);
		assert!(*machine.current_state() == TurnStyleState::Locked);
	}
}
<|endoftext|>


<commit_before>extern crate libc;

use std::os::raw;
use std::ffi::CString;
use std::mem::transmute;

#[link(name = "rust")]
extern {
  fn klee_make_symbolic(data: *const raw::c_void, length: libc::size_t, name: *const raw::c_char);
}

pub fn any(data: *const raw::c_void, length: usize, name: &str) {
  let name_cstr = CString::new(name).unwrap();
  unsafe {
    klee_make_symbolic(data, length as libc::size_t, name_cstr.as_ptr());
  }
}

pub fn i32(data: *const i32, name: &str) {
  unsafe { any(transmute(data), 4, name); }
}

pub fn u32(data: *const u32, name: &str) {
  unsafe { any(transmute(data), 4, name); }
}

pub fn str(data: &str, name: &str) {
  unsafe { any(transmute(data), data.len(), name); }
}
<commit_msg>Update lib.rs<commit_after>extern crate libc;

use std::os::raw;
use std::ffi::CString;
use std::mem::transmute;

#[link(name = "rust")]
extern {
  fn klee_make_symbolic(data: *const raw::c_void, length: libc::size_t, name: *const raw::c_char);
}

pub fn any(data: *const raw::c_void, length: usize, name: &str) {
  let name_cstr = CString::new(name).unwrap();
  unsafe {
    klee_make_symbolic(data, length as libc::size_t, name_cstr.as_ptr());
  }
}

pub fn i32(data: *const i32, name: &str) {
  unsafe { any(transmute(data), 4, name); }
}

pub fn u32(data: *const u32, name: &str) {
  unsafe { any(transmute(data), 4, name); }
}

pub fn str(data: *const &str, name: &str) {
  unsafe { any(transmute(data), data.len(), name); }
}
<|endoftext|>


<commit_before>use chip8::video;
use chip8::video::Video;
use chip8::input::Input;

pub static MAIN: u16 = 0x200;
static END: u16 = 0xFFF;

static GLYPHS: u16 = MAIN - 5 * 0xF;
static DISPLAY: u16 = END - 256;

static MEM_SIZE: u16 = DISPLAY - MAIN;

pub struct Memory {
    main: [u8, ..MEM_SIZE],
    stack: Stack,
    video: Video,
    input: Input
}

impl Memory {
    pub fn new() -> Memory {
        Memory {
            main: [0x0, ..MEM_SIZE],
            stack: Stack::new(),
            video: Video::new(),
            input: Input::new()
        }
    }

    /// Reads a byte
    pub fn rb(&self, addr: u16) -> u8 {
        *(self.map(addr))
    }

    /// Reads a word
    pub fn rw(&self, addr: u16) -> u16 {
        (self.rb(addr) as u16) << 8 | (self.rb(addr + 1) as u16)
    }

    /// Writes a byte
    pub fn wb(&mut self, addr: u16, val: u8) {
        *(self.map_mut(addr)) = val;
    }

    /// Writes a word
    pub fn ww(&mut self, addr: u16, val: u16) {
        self.wb(addr, (val >> 8) as u8);
        self.wb(addr+1, val as u8);
    }

    /// Map to mutable address
    fn map_mut<'a>(&'a mut self, addr: u16) -> &'a mut u8 {
        assert!(addr < END, "Invalid address");

        if addr < GLYPHS {
            // Not sure what maps to this
            fail!("Reserved address")
        }
        else if addr < MAIN {
            // Glyphs are read_only
            fail!("Read only memory")
        }
        else if addr < DISPLAY {
            &'a mut self.main[addr - MAIN]
        }
        else {
            &'a mut self.video.repr[addr - DISPLAY]
        }
    }

    /// Map to immutable address
    fn map<'a>(&'a self, addr: u16) -> &'a u8 {
        assert!(addr < END, "Invalid address");

        if addr < GLYPHS {
            // Not sure what maps to this
            fail!("Reserved address")
        }
        else if addr < MAIN {
            &'a video::GLYPHS[addr - GLYPHS]
        }
        else if addr < DISPLAY {
            &'a self.main[addr - MAIN]
        }
        else {
            &'a self.video.repr[addr - DISPLAY]
        }
    }

    ///
    /// Graphics operations
    ///

    /// Get the address
    pub fn glyph_addr(&self, val: u8) -> u16 {
        assert!(val <= 0xF, "Invalid glyph");
        GLYPHS + val as u16 * 5
    }

    /// Clear the display
    pub fn clear_disp(&mut self) {
        self.video.clear();
    }

    /// Draws a sprite at addr on the screen at x, y
    /// Returns 1 if any screen pixels are flipped from set to unset when the sprite is drawn,
    /// or 0 if that doesn't happen.
    pub fn draw(&mut self, x: u8, y: u8, h: u8, addr: u16) -> u8 {
        let mut flipped = 0x0;
        for dy in range(0, h) {
            let row = *(self.map(addr + dy as u16));
            flipped |= self.video.draw(x, y + dy, row);
        }
        flipped
    }
}

// Stack size supports 16 levels of nesting
static STACK_SIZE: uint = 16;

struct Stack {
    priv values: [u16, ..STACK_SIZE],
    priv i: uint
}

impl Stack {
    pub fn new() -> Stack {
        Stack {
            values: [0, ..STACK_SIZE],
            i: 0
        }
    }

    pub fn push(&mut self, val: u16) {
        assert!(self.i < STACK_SIZE, "Stack overflow");

        self.i += 1;
        self.values[self.i] = val;
    }

    pub fn pop(&mut self) -> u16 {
        assert!(self.i != 0, "Stack failure");

        let val = self.values[self.i];
        self.i -= 1;
        val
    }
}

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

    #[test]
    fn test_read_write_byte() {
        let mut mem = Memory::new();
        assert_eq!(mem.rb(MAIN as u16), 0x00);
        mem.wb(MAIN as u16, 0xFF);
        assert_eq!(mem.rb(MAIN as u16), 0xFF);
    }

    #[test]
    fn test_read_write_word() {
        let mut mem = Memory::new();
        assert_eq!(mem.rw(MAIN as u16), 0x0000);
        mem.ww(MAIN as u16, 0xFFFF);
        assert_eq!(mem.rw(MAIN as u16), 0xFFFF);
    }
}
<commit_msg>Fix incorrect memory mapping to font, and remove unused functions<commit_after>use chip8::video;
use chip8::video::Video;
use chip8::input::Input;

pub static MAIN: u16 = 0x200;
static END: u16 = 0xFFF;

static GLYPHS: u16 = MAIN - 5 * 0x10;
static DISPLAY: u16 = END - 256;

static MEM_SIZE: u16 = DISPLAY - MAIN;

pub struct Memory {
    main: [u8, ..MEM_SIZE],
    stack: Stack,
    video: Video,
    input: Input
}

impl Memory {
    pub fn new() -> Memory {
        Memory {
            main: [0x0, ..MEM_SIZE],
            stack: Stack::new(),
            video: Video::new(),
            input: Input::new()
        }
    }

    /// Reads a byte
    pub fn rb(&self, addr: u16) -> u8 {
        *(self.map(addr))
    }

    /// Reads a word
    pub fn rw(&self, addr: u16) -> u16 {
        (self.rb(addr) as u16) << 8 | (self.rb(addr + 1) as u16)
    }

    /// Writes a byte
    pub fn wb(&mut self, addr: u16, val: u8) {
        *(self.map_mut(addr)) = val;
    }

    /// Map to mutable address
    fn map_mut<'a>(&'a mut self, addr: u16) -> &'a mut u8 {
        assert!(addr < END, "Invalid address");

        if addr < GLYPHS {
            // Not sure what maps to this
            fail!("Reserved address")
        }
        else if addr < MAIN {
            // Glyphs are read_only
            fail!("Read only memory")
        }
        else if addr < DISPLAY {
            &'a mut self.main[addr - MAIN]
        }
        else {
            &'a mut self.video.repr[addr - DISPLAY]
        }
    }

    /// Map to immutable address
    fn map<'a>(&'a self, addr: u16) -> &'a u8 {
        assert!(addr < END, "Invalid address");

        if addr < GLYPHS {
            // Not sure what maps to this
            fail!("Reserved address")
        }
        else if addr < MAIN {
            &'a video::GLYPHS[addr - GLYPHS]
        }
        else if addr < DISPLAY {
            &'a self.main[addr - MAIN]
        }
        else {
            &'a self.video.repr[addr - DISPLAY]
        }
    }

    ///
    /// Graphics operations
    ///

    /// Get the address
    pub fn glyph_addr(&self, val: u8) -> u16 {
        assert!(val <= 0xF, "Invalid glyph");
        GLYPHS + val as u16 * 5
    }

    /// Clear the display
    pub fn clear_disp(&mut self) {
        self.video.clear();
    }

    /// Draws a sprite at addr on the screen at x, y
    /// Returns 1 if any screen pixels are flipped from set to unset when the sprite is drawn,
    /// or 0 if that doesn't happen.
    pub fn draw(&mut self, x: u8, y: u8, h: u8, addr: u16) -> u8 {
        let mut flipped = 0x0;
        for dy in range(0, h) {
            let row = *(self.map(addr + dy as u16));
            flipped |= self.video.draw(x, y + dy, row);
        }
        flipped
    }
}

// Stack size supports 16 levels of nesting
static STACK_SIZE: uint = 16;

struct Stack {
    priv values: [u16, ..STACK_SIZE],
    priv i: uint
}

impl Stack {
    pub fn new() -> Stack {
        Stack {
            values: [0, ..STACK_SIZE],
            i: 0
        }
    }

    pub fn push(&mut self, val: u16) {
        assert!(self.i < STACK_SIZE, "Stack overflow");

        self.i += 1;
        self.values[self.i] = val;
    }

    pub fn pop(&mut self) -> u16 {
        assert!(self.i != 0, "Stack failure");

        let val = self.values[self.i];
        self.i -= 1;
        val
    }
}

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

    #[test]
    fn test_read_write_byte() {
        let mut mem = Memory::new();
        assert_eq!(mem.rb(MAIN as u16), 0x00);
        mem.wb(MAIN as u16, 0xFF);
        assert_eq!(mem.rb(MAIN as u16), 0xFF);
    }

    #[test]
    fn test_read_write_word() {
        let mut mem = Memory::new();
        assert_eq!(mem.rw(MAIN as u16), 0x0000);
        mem.ww(MAIN as u16, 0xFFFF);
        assert_eq!(mem.rw(MAIN as u16), 0xFFFF);
    }
}
<|endoftext|>


<commit_before>//! Open URLs in the web browsers available on a platform.
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library.
//!
//! ## Examples
//!
//! ```no_run
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//!     // ...
//! }
//! ```
//!
//! ## Platform Support Status
//!
//! | Platform | Supported | Browsers | Test status |
//! |----------|-----------|----------|-------------|
//! | macos    | ✅        | default + [others](https://docs.rs/webbrowser/latest/webbrowser/enum.Browser.html) | ✅ |
//! | windows  | ✅        | default only | ✅ |
//! | linux/*bsd  | ✅     | default only (respects $BROWSER env var, so can be used with other browsers) | ✅ |
//! | android  | ✅        | default only | ✅ |
//! | wasm     | ✅        | default only | ✅ |
//! | haiku    | ✅ (experimental) | default only | ❌ |
//! | ios      | ❌        | unsupported | ❌ |
//!
//! ## Consistent Behaviour
//! `webbrowser` defines consistent behaviour on all platforms as follows:
//! * **Non-Blocking** for GUI based browsers (e.g. Firefox, Chrome etc.), while **Blocking** for text based browser (e.g. lynx etc.)
//! * **Suppressed output** by default for GUI based browsers, so that their stdout/stderr don't pollute the main program's output. This can be overridden by `webbrowser::open_browser_with_options`.

#[cfg_attr(target_os = "macos", path = "macos.rs")]
#[cfg_attr(target_os = "android", path = "android.rs")]
#[cfg_attr(target_arch = "wasm32", path = "wasm.rs")]
#[cfg_attr(windows, path = "windows.rs")]
#[cfg_attr(
    any(
        target_os = "linux",
        target_os = "freebsd",
        target_os = "netbsd",
        target_os = "openbsd",
        target_os = "haiku"
    ),
    path = "unix.rs"
)]
mod os;

#[cfg(not(any(
    target_os = "android",
    target_os = "windows",
    target_os = "macos",
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku",
    target_arch = "wasm32"
)))]
compile_error!("Only Windows, Mac OS, Linux, *BSD and Haiku and Wasm32 are currently supported");

use std::default::Default;
use std::io::{Error, ErrorKind, Result};
use std::str::FromStr;
use std::{error, fmt};

#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
/// Browser types available
pub enum Browser {
    ///Operating system's default browser
    Default,

    ///Mozilla Firefox
    Firefox,

    ///Microsoft's Internet Explorer
    InternetExplorer,

    ///Google Chrome
    Chrome,

    ///Opera
    Opera,

    ///Mac OS Safari
    Safari,

    ///Haiku's WebPositive
    WebPositive,
}

impl Browser {
    /// Returns true if there is likely a browser detected in the system
    pub fn is_available() -> bool {
        Browser::Default.exists()
    }

    /// Returns true if this specific browser is detected in the system
    pub fn exists(&self) -> bool {
        open_browser_with_options(
            *self,
            "https://rootnet.in",
            BrowserOptions::new().with_dry_run(true),
        )
        .is_ok()
    }
}

///The Error type for parsing a string into a Browser.
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub struct ParseBrowserError;

impl fmt::Display for ParseBrowserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("Invalid browser given")
    }
}

impl error::Error for ParseBrowserError {
    fn description(&self) -> &str {
        "invalid browser"
    }
}

impl Default for Browser {
    fn default() -> Self {
        Browser::Default
    }
}

impl fmt::Display for Browser {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Browser::Default => f.write_str("Default"),
            Browser::Firefox => f.write_str("Firefox"),
            Browser::InternetExplorer => f.write_str("Internet Explorer"),
            Browser::Chrome => f.write_str("Chrome"),
            Browser::Opera => f.write_str("Opera"),
            Browser::Safari => f.write_str("Safari"),
            Browser::WebPositive => f.write_str("WebPositive"),
        }
    }
}

impl FromStr for Browser {
    type Err = ParseBrowserError;

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        match s {
            "firefox" => Ok(Browser::Firefox),
            "default" => Ok(Browser::Default),
            "ie" | "internet explorer" | "internetexplorer" => Ok(Browser::InternetExplorer),
            "chrome" => Ok(Browser::Chrome),
            "opera" => Ok(Browser::Opera),
            "safari" => Ok(Browser::Safari),
            "webpositive" => Ok(Browser::WebPositive),
            _ => Err(ParseBrowserError),
        }
    }
}

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
/// BrowserOptions to override certain default behaviour. Any option named as a `hint` is
/// not guaranteed to be honoured.
///
/// e.g. by default, we suppress stdout/stderr, but that behaviour can be overridden here
pub struct BrowserOptions {
    suppress_output: bool,
    target_hint: String,
    dry_run: bool,
}

impl fmt::Display for BrowserOptions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_fmt(format_args!(
            "BrowserOptions(supress_output={}, target_hint={}, dry_run={})",
            self.suppress_output, self.target_hint, self.dry_run
        ))
    }
}

impl std::default::Default for BrowserOptions {
    fn default() -> Self {
        let target_hint = String::from(option_env!("WEBBROWSER_WASM_TARGET").unwrap_or("_blank"));
        BrowserOptions {
            suppress_output: true,
            target_hint,
            dry_run: false,
        }
    }
}

impl BrowserOptions {
    pub fn new() -> Self {
        Self::default()
    }

    /// Determines whether stdout/stderr of the appropriate browser command is suppressed
    /// or not
    pub fn with_suppress_output(&mut self, suppress_output: bool) -> &mut Self {
        self.suppress_output = suppress_output;
        self
    }

    /// Hint to the browser to open the url in the corresponding
    /// [target](https://www.w3schools.com/tags/att_a_target.asp). Note that this is just
    /// a hint, it may or may not be honoured (currently guaranteed only in wasm).
    pub fn with_target_hint(&mut self, target_hint: &str) -> &mut Self {
        self.target_hint = target_hint.to_owned();
        self
    }

    /// Do not do an actual execution, just return true if this would've likely
    /// succeeded. Note the "likely" here - it's still indicative than guaranteed.
    pub fn with_dry_run(&mut self, dry_run: bool) -> &mut Self {
        self.dry_run = dry_run;
        self
    }
}

/// Opens the URL on the default browser of this platform
///
/// Returns Ok(..) so long as the browser invocation was successful. An Err(..) is returned only if
/// there was an error in running the command, or if the browser was not found.
///
/// Equivalent to:
/// ```no_run
/// # use webbrowser::{Browser, open_browser};
/// # let url = "http://example.com";
/// open_browser(Browser::Default, url);
/// ```
///
/// # Examples
/// ```no_run
/// use webbrowser;
///
/// if webbrowser::open("http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open(url: &str) -> Result<()> {
    open_browser(Browser::Default, url)
}

/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser, Browser};
///
/// if open_browser(Browser::Firefox, "http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open_browser(browser: Browser, url: &str) -> Result<()> {
    open_browser_with_options(browser, url, &BrowserOptions::default())
}

/// Opens the specified URL on the specific browser (if available) requested, while overriding the
/// default options.
///
/// Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser_with_options, Browser, BrowserOptions};
///
/// if open_browser_with_options(Browser::Default, "http://github.com", BrowserOptions::new().with_suppress_output(false)).is_ok() {
///     // ...
/// }
/// ```
pub fn open_browser_with_options(
    browser: Browser,
    url: &str,
    options: &BrowserOptions,
) -> Result<()> {
    let url_s: String = match url::Url::parse(url) {
        Ok(u) => u.as_str().into(),
        Err(_) => url.into(),
    };
    os::open_browser_internal(browser, &url_s, options)
}

#[test]
#[ignore]
fn test_open_firefox() {
    assert!(open_browser(Browser::Firefox, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_chrome() {
    assert!(open_browser(Browser::Chrome, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_safari() {
    assert!(open_browser(Browser::Safari, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_webpositive() {
    assert!(open_browser(Browser::WebPositive, "http://github.com").is_ok());
}
<commit_msg>clearer doc for BrowserOptions [skip ci]<commit_after>//! Open URLs in the web browsers available on a platform.
//!
//! Inspired by the [webbrowser](https://docs.python.org/2/library/webbrowser.html) python library.
//!
//! ## Examples
//!
//! ```no_run
//! use webbrowser;
//!
//! if webbrowser::open("http://github.com").is_ok() {
//!     // ...
//! }
//! ```
//!
//! ## Platform Support Status
//!
//! | Platform | Supported | Browsers | Test status |
//! |----------|-----------|----------|-------------|
//! | macos    | ✅        | default + [others](https://docs.rs/webbrowser/latest/webbrowser/enum.Browser.html) | ✅ |
//! | windows  | ✅        | default only | ✅ |
//! | linux/*bsd  | ✅     | default only (respects $BROWSER env var, so can be used with other browsers) | ✅ |
//! | android  | ✅        | default only | ✅ |
//! | wasm     | ✅        | default only | ✅ |
//! | haiku    | ✅ (experimental) | default only | ❌ |
//! | ios      | ❌        | unsupported | ❌ |
//!
//! ## Consistent Behaviour
//! `webbrowser` defines consistent behaviour on all platforms as follows:
//! * **Non-Blocking** for GUI based browsers (e.g. Firefox, Chrome etc.), while **Blocking** for text based browser (e.g. lynx etc.)
//! * **Suppressed output** by default for GUI based browsers, so that their stdout/stderr don't pollute the main program's output. This can be overridden by `webbrowser::open_browser_with_options`.

#[cfg_attr(target_os = "macos", path = "macos.rs")]
#[cfg_attr(target_os = "android", path = "android.rs")]
#[cfg_attr(target_arch = "wasm32", path = "wasm.rs")]
#[cfg_attr(windows, path = "windows.rs")]
#[cfg_attr(
    any(
        target_os = "linux",
        target_os = "freebsd",
        target_os = "netbsd",
        target_os = "openbsd",
        target_os = "haiku"
    ),
    path = "unix.rs"
)]
mod os;

#[cfg(not(any(
    target_os = "android",
    target_os = "windows",
    target_os = "macos",
    target_os = "linux",
    target_os = "freebsd",
    target_os = "netbsd",
    target_os = "openbsd",
    target_os = "haiku",
    target_arch = "wasm32"
)))]
compile_error!("Only Windows, Mac OS, Linux, *BSD and Haiku and Wasm32 are currently supported");

use std::default::Default;
use std::io::{Error, ErrorKind, Result};
use std::str::FromStr;
use std::{error, fmt};

#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
/// Browser types available
pub enum Browser {
    ///Operating system's default browser
    Default,

    ///Mozilla Firefox
    Firefox,

    ///Microsoft's Internet Explorer
    InternetExplorer,

    ///Google Chrome
    Chrome,

    ///Opera
    Opera,

    ///Mac OS Safari
    Safari,

    ///Haiku's WebPositive
    WebPositive,
}

impl Browser {
    /// Returns true if there is likely a browser detected in the system
    pub fn is_available() -> bool {
        Browser::Default.exists()
    }

    /// Returns true if this specific browser is detected in the system
    pub fn exists(&self) -> bool {
        open_browser_with_options(
            *self,
            "https://rootnet.in",
            BrowserOptions::new().with_dry_run(true),
        )
        .is_ok()
    }
}

///The Error type for parsing a string into a Browser.
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
pub struct ParseBrowserError;

impl fmt::Display for ParseBrowserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("Invalid browser given")
    }
}

impl error::Error for ParseBrowserError {
    fn description(&self) -> &str {
        "invalid browser"
    }
}

impl Default for Browser {
    fn default() -> Self {
        Browser::Default
    }
}

impl fmt::Display for Browser {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Browser::Default => f.write_str("Default"),
            Browser::Firefox => f.write_str("Firefox"),
            Browser::InternetExplorer => f.write_str("Internet Explorer"),
            Browser::Chrome => f.write_str("Chrome"),
            Browser::Opera => f.write_str("Opera"),
            Browser::Safari => f.write_str("Safari"),
            Browser::WebPositive => f.write_str("WebPositive"),
        }
    }
}

impl FromStr for Browser {
    type Err = ParseBrowserError;

    fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
        match s {
            "firefox" => Ok(Browser::Firefox),
            "default" => Ok(Browser::Default),
            "ie" | "internet explorer" | "internetexplorer" => Ok(Browser::InternetExplorer),
            "chrome" => Ok(Browser::Chrome),
            "opera" => Ok(Browser::Opera),
            "safari" => Ok(Browser::Safari),
            "webpositive" => Ok(Browser::WebPositive),
            _ => Err(ParseBrowserError),
        }
    }
}

#[derive(Debug, Eq, PartialEq, Clone, Hash)]
/// BrowserOptions to override certain default behaviour. Any option named as a `hint` is
/// not guaranteed to be honoured. Use [BrowserOptions::new()] to create.
///
/// e.g. by default, we suppress stdout/stderr, but that behaviour can be overridden here
pub struct BrowserOptions {
    suppress_output: bool,
    target_hint: String,
    dry_run: bool,
}

impl fmt::Display for BrowserOptions {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_fmt(format_args!(
            "BrowserOptions(supress_output={}, target_hint={}, dry_run={})",
            self.suppress_output, self.target_hint, self.dry_run
        ))
    }
}

impl std::default::Default for BrowserOptions {
    fn default() -> Self {
        let target_hint = String::from(option_env!("WEBBROWSER_WASM_TARGET").unwrap_or("_blank"));
        BrowserOptions {
            suppress_output: true,
            target_hint,
            dry_run: false,
        }
    }
}

impl BrowserOptions {
    /// Create a new instance. Configure it with one of the `with_` methods.
    pub fn new() -> Self {
        Self::default()
    }

    /// Determines whether stdout/stderr of the appropriate browser command is suppressed
    /// or not
    pub fn with_suppress_output(&mut self, suppress_output: bool) -> &mut Self {
        self.suppress_output = suppress_output;
        self
    }

    /// Hint to the browser to open the url in the corresponding
    /// [target](https://www.w3schools.com/tags/att_a_target.asp). Note that this is just
    /// a hint, it may or may not be honoured (currently guaranteed only in wasm).
    pub fn with_target_hint(&mut self, target_hint: &str) -> &mut Self {
        self.target_hint = target_hint.to_owned();
        self
    }

    /// Do not do an actual execution, just return true if this would've likely
    /// succeeded. Note the "likely" here - it's still indicative than guaranteed.
    pub fn with_dry_run(&mut self, dry_run: bool) -> &mut Self {
        self.dry_run = dry_run;
        self
    }
}

/// Opens the URL on the default browser of this platform
///
/// Returns Ok(..) so long as the browser invocation was successful. An Err(..) is returned only if
/// there was an error in running the command, or if the browser was not found.
///
/// Equivalent to:
/// ```no_run
/// # use webbrowser::{Browser, open_browser};
/// # let url = "http://example.com";
/// open_browser(Browser::Default, url);
/// ```
///
/// # Examples
/// ```no_run
/// use webbrowser;
///
/// if webbrowser::open("http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open(url: &str) -> Result<()> {
    open_browser(Browser::Default, url)
}

/// Opens the specified URL on the specific browser (if available) requested. Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser, Browser};
///
/// if open_browser(Browser::Firefox, "http://github.com").is_ok() {
///     // ...
/// }
/// ```
pub fn open_browser(browser: Browser, url: &str) -> Result<()> {
    open_browser_with_options(browser, url, &BrowserOptions::default())
}

/// Opens the specified URL on the specific browser (if available) requested, while overriding the
/// default options.
///
/// Return semantics are
/// the same as for [open](fn.open.html).
///
/// # Examples
/// ```no_run
/// use webbrowser::{open_browser_with_options, Browser, BrowserOptions};
///
/// if open_browser_with_options(Browser::Default, "http://github.com", BrowserOptions::new().with_suppress_output(false)).is_ok() {
///     // ...
/// }
/// ```
pub fn open_browser_with_options(
    browser: Browser,
    url: &str,
    options: &BrowserOptions,
) -> Result<()> {
    let url_s: String = match url::Url::parse(url) {
        Ok(u) => u.as_str().into(),
        Err(_) => url.into(),
    };
    os::open_browser_internal(browser, &url_s, options)
}

#[test]
#[ignore]
fn test_open_firefox() {
    assert!(open_browser(Browser::Firefox, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_chrome() {
    assert!(open_browser(Browser::Chrome, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_safari() {
    assert!(open_browser(Browser::Safari, "http://github.com").is_ok());
}

#[test]
#[ignore]
fn test_open_webpositive() {
    assert!(open_browser(Browser::WebPositive, "http://github.com").is_ok());
}
<|endoftext|>


<commit_before>use std::io::{ Write, Result as IoResult };

use exec::{Step, Message};
use eval::Expr;
use data::DataMode;
use session::ValueID;

type StateId = u32;
type CounterId = u32;

#[derive(Clone, Debug)]
pub struct Nfa {
    states: Vec<State>,
    initial: StateId,
    success: StateId,
}

impl Nfa {
    pub fn new() -> Nfa {
        let mut nfa = Nfa { states: vec![], initial: 0, success: 0 };
        nfa.initial = nfa.add_state();
        nfa.success = nfa.add_state();
        nfa
    }

    pub fn add_state(&mut self) -> StateId {
        let id = self.states.len() as StateId;
        self.states.push(State::new());
        id
    }

    pub fn add_transition(&mut self, from: StateId, to: StateId, action: Action) {
        self.states[from as usize].transitions.push(Transition { target: to, action: action});
    }

    pub fn to_graphviz(&self, f: &mut Write) -> IoResult<()> {
        try!(writeln!(f, "digraph G {{"));
        for (id, state) in self.states.iter().enumerate() {
            for transition in &state.transitions {
                let colorstr = match &transition.action {
                    &Action::Epsilon => r#"fontcolor="gray""#,
                    &Action::Lower(..) => r#"fontcolor="blue""#,
                    &Action::UpperBegin(..) | &Action::UpperEnd(..) => r#"fontcolor="green""#,
                    _ => ""
                };
                try!(writeln!(f, "{} -> {} [ label=\"{:?}\" {}];", id, transition.target, transition.action, colorstr));
            }
        }
        try!(writeln!(f, "}}"));
        Ok(())
    }
}

#[derive(Clone, Debug)]
pub struct State {
    transitions: Vec<Transition>,
}

impl State {
    fn new() -> State {
        State { transitions: vec![] }
    }
}

#[derive(Clone, Debug)]
pub struct Transition {
    target: StateId,
    action: Action,
}

#[derive(Clone, Debug)]
pub enum Action {
    Epsilon,
    Lower(Message),
    UpperBegin(Message),
    UpperEnd(Message),

    RepeatDnInit(CounterId, Expr), // Down-evaluate count, initialize counter to zero
    RepeatDnBack(CounterId), // Increment counter, guard on counter < count
    RepeatDnExit(CounterId), // Guard on counter == count

    RepeatUpInit(CounterId), // initialize counter to zero
    RepeatUpBack(CounterId), // increment counter
    RepeatUpExit(CounterId, Expr), // Guard on up-evaluate count

    // Down-evaluate outer variables, pop first inner variables down, set counter to zero
    ForInit(CounterId, u32, Vec<(ValueID, Expr, DataMode)>),
    // guard on counter < size, push next outer variables up, pop next inner variables down
    ForBack(CounterId, u32, Vec<(ValueID, Expr, DataMode)>),
    // guard on counter == size, Up-evaluate outer variables
    ForExit(CounterId, u32, Vec<(ValueID, Expr, DataMode)>),
}

pub fn from_step_tree(s: &Step) -> Nfa {
    let mut nfa = Nfa::new();
    let initial = nfa.initial;
    let success = nfa.success;
    from_step_tree_inner(s, &mut nfa, initial, success);

    fn from_step_tree_inner(s: &Step, nfa: &mut Nfa, from: StateId, to: StateId) {
        match *s {
            Step::Nop => nfa.add_transition(from, to, Action::Epsilon),
            Step::Token(ref message) => {
                let m: Message = message.clone();
                nfa.add_transition(from, to, Action::Lower(m))
            }
            Step::TokenTop(ref message, box ref body) => {
                let is = nfa.add_state();
                let ie = nfa.add_state();
                nfa.add_transition(from, is, Action::UpperBegin(message.clone()));
                from_step_tree_inner(body, nfa, is, ie);
                nfa.add_transition(ie, to, Action::UpperEnd(message.clone()));
            }
            Step::Seq(ref steps) => {
                if let Some((last, rest)) = steps.split_last() {
                    let mut conn = from;
                    for c in rest.iter() {
                        let next = nfa.add_state();
                        from_step_tree_inner(c, nfa, conn, next);
                        conn = next;
                    }
                    from_step_tree_inner(last, nfa, conn, to);
                } else {
                    nfa.add_transition(from, to, Action::Epsilon);
                }
            }
            Step::Repeat(ref count, box ref inner, up) => {
                let start = nfa.add_state();
                let end = nfa.add_state();
                let counter = start;

                if up {
                    nfa.add_transition(from, start, Action::RepeatUpInit(counter));
                    nfa.add_transition(end, start, Action::RepeatUpBack(counter));
                    nfa.add_transition(end, to, Action::RepeatUpExit(counter, count.clone()));
                } else {
                    nfa.add_transition(from, start, Action::RepeatDnInit(counter, count.clone()));
                    nfa.add_transition(end, start, Action::RepeatDnBack(counter));
                    nfa.add_transition(end, to, Action::RepeatDnExit(counter));
                }
                from_step_tree_inner(inner, nfa, start, end);
            }
            Step::Foreach(width, ref vars, box ref inner) => {
                let start = nfa.add_state();
                let end = nfa.add_state();
                let counter = start;

                nfa.add_transition(from, start, Action::ForInit(counter, width, vars.clone()));
                nfa.add_transition(end, start, Action::ForBack(counter, width, vars.clone()));
                nfa.add_transition(end, to, Action::ForExit(counter, width, vars.clone()));

                from_step_tree_inner(inner, nfa, start, end);
            }
        }
    }

    nfa
}
<commit_msg>nfa: Fix loop exit transition<commit_after>use std::io::{ Write, Result as IoResult };

use exec::{Step, Message};
use eval::Expr;
use data::DataMode;
use session::ValueID;

pub type StateId = usize;
pub type CounterId = usize;

#[derive(Clone, Debug)]
pub struct Nfa {
    states: Vec<State>,
    initial: StateId,
    success: StateId,
}

impl Nfa {
    pub fn new() -> Nfa {
        let mut nfa = Nfa { states: vec![], initial: 0, success: 0 };
        nfa.initial = nfa.add_state();
        nfa.success = nfa.add_state();
        nfa
    }

    pub fn add_state(&mut self) -> StateId {
        let id = self.states.len() as StateId;
        self.states.push(State::new());
        id
    }

    pub fn add_transition(&mut self, from: StateId, to: StateId, action: Action) {
        self.states[from as usize].transitions.push(Transition { target: to, action: action});
    }

    pub fn to_graphviz(&self, f: &mut Write) -> IoResult<()> {
        try!(writeln!(f, "digraph G {{"));
        for (id, state) in self.states.iter().enumerate() {
            for transition in &state.transitions {
                let colorstr = match &transition.action {
                    &Action::Epsilon => r#"fontcolor="gray""#,
                    &Action::Lower(..) => r#"fontcolor="blue""#,
                    &Action::UpperBegin(..) | &Action::UpperEnd(..) => r#"fontcolor="green""#,
                    _ => ""
                };
                try!(writeln!(f, "{} -> {} [ label=\"{:?}\" {}];", id, transition.target, transition.action, colorstr));
            }
        }
        try!(writeln!(f, "}}"));
        Ok(())
    }
}

#[derive(Clone, Debug)]
pub struct State {
    transitions: Vec<Transition>,
}

impl State {
    fn new() -> State {
        State { transitions: vec![] }
    }
}

#[derive(Clone, Debug)]
pub struct Transition {
    target: StateId,
    action: Action,
}

#[derive(Clone, Debug)]
pub enum Action {
    Epsilon,
    Lower(Message),
    UpperBegin(Message),
    UpperEnd(Message),

    RepeatDnInit(CounterId, Expr), // Down-evaluate count, initialize counter to zero
    RepeatDnBack(CounterId), // Increment counter, guard on counter < count
    RepeatDnExit(CounterId), // Guard on counter == count

    RepeatUpInit(CounterId), // initialize counter to zero
    RepeatUpBack(CounterId), // increment counter
    RepeatUpExit(CounterId, Expr), // Guard on up-evaluate count

    // Down-evaluate outer variables, set counter to zero
    ForInit(CounterId, u32, Vec<(ValueID, Expr, DataMode)>),
    // guard on counter < size, pop next inner variables down,
    ForEntry(CounterId, u32, Vec<(ValueID, Expr, DataMode)>),
    // push next outer variables up, increment
    ForBack(CounterId, u32, Vec<(ValueID, Expr, DataMode)>),
    // guard on counter == size, Up-evaluate outer variables
    ForExit(CounterId, u32, Vec<(ValueID, Expr, DataMode)>),
}

pub fn from_step_tree(s: &Step) -> Nfa {
    let mut nfa = Nfa::new();
    let initial = nfa.initial;
    let success = nfa.success;
    from_step_tree_inner(s, &mut nfa, initial, success);

    fn from_step_tree_inner(s: &Step, nfa: &mut Nfa, from: StateId, to: StateId) {
        match *s {
            Step::Nop => nfa.add_transition(from, to, Action::Epsilon),
            Step::Token(ref message) => {
                let m: Message = message.clone();
                nfa.add_transition(from, to, Action::Lower(m))
            }
            Step::TokenTop(ref message, box ref body) => {
                let is = nfa.add_state();
                let ie = nfa.add_state();
                nfa.add_transition(from, is, Action::UpperBegin(message.clone()));
                from_step_tree_inner(body, nfa, is, ie);
                nfa.add_transition(ie, to, Action::UpperEnd(message.clone()));
            }
            Step::Seq(ref steps) => {
                if let Some((last, rest)) = steps.split_last() {
                    let mut conn = from;
                    for c in rest.iter() {
                        let next = nfa.add_state();
                        from_step_tree_inner(c, nfa, conn, next);
                        conn = next;
                    }
                    from_step_tree_inner(last, nfa, conn, to);
                } else {
                    nfa.add_transition(from, to, Action::Epsilon);
                }
            }
            Step::Repeat(ref count, box ref inner, up) => {
                let start = nfa.add_state();
                let end = nfa.add_state();
                let counter = start;

                if up {
                    nfa.add_transition(from, start, Action::RepeatUpInit(counter));
                    nfa.add_transition(start, end, Action::RepeatUpBack(counter));
                    nfa.add_transition(start, to, Action::RepeatUpExit(counter, count.clone()));
                } else {
                    nfa.add_transition(from, start, Action::RepeatDnInit(counter, count.clone()));
                    nfa.add_transition(start, end, Action::RepeatDnBack(counter));
                    nfa.add_transition(start, to, Action::RepeatDnExit(counter));
                }
                from_step_tree_inner(inner, nfa, end, start);
            }
            Step::Foreach(width, ref vars, box ref inner) => {
                let entry = nfa.add_state();
                let start = nfa.add_state();
                let end   = nfa.add_state();
                let counter = entry;

                nfa.add_transition(from,  entry, Action::ForInit(counter, width, vars.clone()));
                nfa.add_transition(entry, start, Action::ForEntry(counter, width, vars.clone()));
                nfa.add_transition(end,   entry, Action::ForBack(counter, width, vars.clone()));
                nfa.add_transition(entry, to,    Action::ForExit(counter, width, vars.clone()));

                from_step_tree_inner(inner, nfa, start, end);
            }
        }
    }

    nfa
}
<|endoftext|>


<commit_before>//! # mdBook
//!
//! **mdBook** is similar to GitBook but implemented in Rust.
//! It offers a command line interface, but can also be used as a regular crate.
//!
//! This is the API doc, the [user guide] is also available if you want
//! information about the command line tool, format, structure etc. It is also
//! rendered with mdBook to showcase the features and default theme.
//!
//! Some reasons why you would want to use the crate (over the cli):
//!
//! - Integrate mdbook in a current project
//! - Extend the capabilities of mdBook
//! - Do some processing or test before building your book
//! - Accessing the public API to help create a new Renderer
//! - ...
//!
//! # Examples
//!
//! If creating a new book from scratch, you'll want to get a `BookBuilder` via
//! the `MDBook::init()` method.
//!
//! ```rust,no_run
//! use mdbook::MDBook;
//! use mdbook::config::Config;
//!
//! let root_dir = "/path/to/book/root";
//!
//! // create a default config and change a couple things
//! let mut cfg = Config::default();
//! cfg.book.title = Some("My Book".to_string());
//! cfg.book.authors.push("Michael-F-Bryan".to_string());
//!
//! MDBook::init(root_dir)
//!     .create_gitignore(true)
//!     .with_config(cfg)
//!     .build()
//!     .expect("Book generation failed");
//! ```
//!
//! You can also load an existing book and build it.
//!
//! ```rust,no_run
//! use mdbook::MDBook;
//!
//! let root_dir = "/path/to/book/root";
//!
//! let mut md = MDBook::load(root_dir)
//!     .expect("Unable to load the book");
//! md.build().expect("Building failed");
//! ```
//!
//! ## Implementing a new Backend
//!
//! `mdbook` has a fairly flexible mechanism for creating additional backends
//! for your book. The general idea is you'll add an extra table in the book's
//! `book.toml` which specifies an executable to be invoked by `mdbook`. This
//! executable will then be called during a build, with an in-memory
//! representation ([`RenderContext`]) of the book being passed to the
//! subprocess via `stdin`.
//!
//! The [`RenderContext`] gives the backend access to the contents of
//! `book.toml` and lets it know which directory all generated artefacts should
//! be placed in. For a much more in-depth explanation, consult the [relevant
//! chapter] in the *For Developers* section of the user guide.
//!
//! To make creating a backend easier, the `mdbook` crate can be imported
//! directly, making deserializing the `RenderContext` easy and giving you
//! access to the various methods for working with the [`Config`].
//!
//! [user guide]: https://rust-lang-nursery.github.io/mdBook/
//! [`RenderContext`]: renderer/struct.RenderContext.html
//! [relevant chapter]: https://rust-lang-nursery.github.io/mdBook/for_developers/backends.html
//! [`Config`]: config/struct.Config.html

#![deny(missing_docs)]

#[macro_use]
extern crate error_chain;
extern crate handlebars;
extern crate itertools;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate memchr;
extern crate pulldown_cmark;
extern crate regex;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate shlex;
extern crate tempdir;
extern crate toml;
extern crate toml_query;

#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;

pub mod preprocess;
pub mod book;
pub mod config;
pub mod renderer;
pub mod theme;
pub mod utils;

pub use book::MDBook;
pub use book::BookItem;
pub use renderer::Renderer;
pub use config::Config;

/// The error types used through out this crate.
pub mod errors {
    use std::path::PathBuf;

    error_chain!{
        foreign_links {
            Io(::std::io::Error) #[doc = "A wrapper around `std::io::Error`"];
            HandlebarsRender(::handlebars::RenderError) #[doc = "Handlebars rendering failed"];
            HandlebarsTemplate(Box<::handlebars::TemplateError>) #[doc = "Unable to parse the template"];
            Utf8(::std::string::FromUtf8Error) #[doc = "Invalid UTF-8"];
            SerdeJson(::serde_json::Error) #[doc = "JSON conversion failed"];
        }

        links {
            TomlQuery(::toml_query::error::Error, ::toml_query::error::ErrorKind) #[doc = "A TomlQuery error"];
        }

        errors {
            /// A subprocess exited with an unsuccessful return code.
            Subprocess(message: String, output: ::std::process::Output) {
                description("A subprocess failed")
                display("{}: {}", message, String::from_utf8_lossy(&output.stdout))
            }

            /// An error was encountered while parsing the `SUMMARY.md` file.
            ParseError(line: usize, col: usize, message: String) {
                description("A SUMMARY.md parsing error")
                display("Error at line {}, column {}: {}", line, col, message)
            }

            /// The user tried to use a reserved filename.
            ReservedFilenameError(filename: PathBuf) {
                description("Reserved Filename")
                display("{} is reserved for internal use", filename.display())
            }
        }
    }

    // Box to halve the size of Error
    impl From<::handlebars::TemplateError> for Error {
        fn from(e: ::handlebars::TemplateError) -> Error {
            From::from(Box::new(e))
        }
    }
}
<commit_msg>Added a stability warning to the API docs<commit_after>//! # mdBook
//!
//! **mdBook** is a tool for rendering a collection of markdown documents into
//! a form more suitable for end users like HTML or EPUB.  It offers a command
//! line interface, but this crate can be used if more control is required.
//!
//! This is the API doc, the [user guide] is also available if you want
//! information about the command line tool, format, structure etc. It is also
//! rendered with mdBook to showcase the features and default theme.
//!
//! Some reasons why you would want to use the crate (over the cli):
//!
//! - Integrate mdbook in a current project
//! - Extend the capabilities of mdBook
//! - Do some processing or test before building your book
//! - Accessing the public API to help create a new Renderer
//! - ...
//!
//! > **Note:** While we try to ensure `mdbook`'s command-line interface and
//! > behaviour are backwards compatible, the tool's internals are still
//! > evolving and being iterated on. If you wish to prevent accidental
//! > breakages it is recommended to pin any tools building on top of the
//! > `mdbook` crate to a specific release.
//!
//! # Examples
//!
//! If creating a new book from scratch, you'll want to get a `BookBuilder` via
//! the `MDBook::init()` method.
//!
//! ```rust,no_run
//! use mdbook::MDBook;
//! use mdbook::config::Config;
//!
//! let root_dir = "/path/to/book/root";
//!
//! // create a default config and change a couple things
//! let mut cfg = Config::default();
//! cfg.book.title = Some("My Book".to_string());
//! cfg.book.authors.push("Michael-F-Bryan".to_string());
//!
//! MDBook::init(root_dir)
//!     .create_gitignore(true)
//!     .with_config(cfg)
//!     .build()
//!     .expect("Book generation failed");
//! ```
//!
//! You can also load an existing book and build it.
//!
//! ```rust,no_run
//! use mdbook::MDBook;
//!
//! let root_dir = "/path/to/book/root";
//!
//! let mut md = MDBook::load(root_dir)
//!     .expect("Unable to load the book");
//! md.build().expect("Building failed");
//! ```
//!
//! ## Implementing a new Backend
//!
//! `mdbook` has a fairly flexible mechanism for creating additional backends
//! for your book. The general idea is you'll add an extra table in the book's
//! `book.toml` which specifies an executable to be invoked by `mdbook`. This
//! executable will then be called during a build, with an in-memory
//! representation ([`RenderContext`]) of the book being passed to the
//! subprocess via `stdin`.
//!
//! The [`RenderContext`] gives the backend access to the contents of
//! `book.toml` and lets it know which directory all generated artefacts should
//! be placed in. For a much more in-depth explanation, consult the [relevant
//! chapter] in the *For Developers* section of the user guide.
//!
//! To make creating a backend easier, the `mdbook` crate can be imported
//! directly, making deserializing the `RenderContext` easy and giving you
//! access to the various methods for working with the [`Config`].
//!
//! [user guide]: https://rust-lang-nursery.github.io/mdBook/
//! [`RenderContext`]: renderer/struct.RenderContext.html
//! [relevant chapter]: https://rust-lang-nursery.github.io/mdBook/for_developers/backends.html
//! [`Config`]: config/struct.Config.html

#![deny(missing_docs)]

#[macro_use]
extern crate error_chain;
extern crate handlebars;
extern crate itertools;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate log;
extern crate memchr;
extern crate pulldown_cmark;
extern crate regex;
extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate shlex;
extern crate tempdir;
extern crate toml;
extern crate toml_query;

#[cfg(test)]
#[macro_use]
extern crate pretty_assertions;

pub mod preprocess;
pub mod book;
pub mod config;
pub mod renderer;
pub mod theme;
pub mod utils;

pub use book::MDBook;
pub use book::BookItem;
pub use renderer::Renderer;
pub use config::Config;

/// The error types used through out this crate.
pub mod errors {
    use std::path::PathBuf;

    error_chain!{
        foreign_links {
            Io(::std::io::Error) #[doc = "A wrapper around `std::io::Error`"];
            HandlebarsRender(::handlebars::RenderError) #[doc = "Handlebars rendering failed"];
            HandlebarsTemplate(Box<::handlebars::TemplateError>) #[doc = "Unable to parse the template"];
            Utf8(::std::string::FromUtf8Error) #[doc = "Invalid UTF-8"];
            SerdeJson(::serde_json::Error) #[doc = "JSON conversion failed"];
        }

        links {
            TomlQuery(::toml_query::error::Error, ::toml_query::error::ErrorKind) #[doc = "A TomlQuery error"];
        }

        errors {
            /// A subprocess exited with an unsuccessful return code.
            Subprocess(message: String, output: ::std::process::Output) {
                description("A subprocess failed")
                display("{}: {}", message, String::from_utf8_lossy(&output.stdout))
            }

            /// An error was encountered while parsing the `SUMMARY.md` file.
            ParseError(line: usize, col: usize, message: String) {
                description("A SUMMARY.md parsing error")
                display("Error at line {}, column {}: {}", line, col, message)
            }

            /// The user tried to use a reserved filename.
            ReservedFilenameError(filename: PathBuf) {
                description("Reserved Filename")
                display("{} is reserved for internal use", filename.display())
            }
        }
    }

    // Box to halve the size of Error
    impl From<::handlebars::TemplateError> for Error {
        fn from(e: ::handlebars::TemplateError) -> Error {
            From::from(Box::new(e))
        }
    }
}
<|endoftext|>


<commit_before>// Copyright 2016 Serde YAML Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Whitelisted clippy_pedantic lints
#![cfg_attr(feature = "cargo-clippy", allow(
// private Deserializer::next
    should_implement_trait,
// things are often more readable this way
    single_match_else,
    stutter,
// not practical
    missing_docs_in_private_items,
// not stable
    empty_enum,
))]

extern crate linked_hash_map;
extern crate num_traits;
#[macro_use]
extern crate serde;
extern crate yaml_rust;

pub use self::de::{from_reader, from_slice, from_str};
pub use self::ser::{to_string, to_vec, to_writer};
pub use self::value::{Sequence, Value, from_value, to_value};
pub use self::error::{Error, Result};
pub use self::mapping::Mapping;

mod de;
mod ser;
mod value;
mod error;
mod path;
mod mapping;
<commit_msg>Add Readme docs as crate docs<commit_after>// Copyright 2016 Serde YAML Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! This crate is a Rust library for using the [Serde] serialization framework
//! with data in [YAML] file format.
//!
//! This library does not reimplement a YAML parser; it uses [yaml-rust] which
//! is a pure Rust YAML 1.2 implementation.
//!
//! [Serde]: https://github.com/serde-rs/serde
//! [YAML]: http://yaml.org
//! [yaml-rust]: https://github.com/chyh1990/yaml-rust
//!
//! # Examples
//!
//! ```rust
//! extern crate serde_yaml;
//!
//! use std::collections::BTreeMap;
//!
//! // You have some type.
//! let mut map = BTreeMap::new();
//! map.insert("x".to_string(), 1.0);
//! map.insert("y".to_string(), 2.0);
//!
//! // Serialize it to a YAML string.
//! let s = serde_yaml::to_string(&map).unwrap();
//! assert_eq!(s, "---\nx: 1\ny: 2");
//!
//! // Deserialize it back to a Rust type.
//! let deserialized_map: BTreeMap<String, f64> = serde_yaml::from_str(&s).unwrap();
//! assert_eq!(map, deserialized_map);
//! ```
//!
//! ## Using serde derive
//!
//! It can also be used with Serde's serialization code generator `serde_derive` to
//! handle structs and enums defined in your own program.
//!
//! ```rust
//! #[macro_use] extern crate serde_derive;
//! extern crate serde_yaml;
//!
//! #[derive(Debug, PartialEq, Serialize, Deserialize)]
//! struct Point { x: f64, y: f64 }
//!
//! # fn main() {
//! let point = Point { x: 1.0, y: 2.0 };
//!
//! let s = serde_yaml::to_string(&point).unwrap();
//! assert_eq!(s, "---\nx: 1\ny: 2");
//!
//! let deserialized_point: Point = serde_yaml::from_str(&s).unwrap();
//! assert_eq!(point, deserialized_point);
//! # }
//! ```

#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Whitelisted clippy_pedantic lints
#![cfg_attr(feature = "cargo-clippy", allow(
// private Deserializer::next
    should_implement_trait,
// things are often more readable this way
    single_match_else,
    stutter,
// not practical
    missing_docs_in_private_items,
// not stable
    empty_enum,
))]

extern crate linked_hash_map;
extern crate num_traits;
#[macro_use]
extern crate serde;
extern crate yaml_rust;

pub use self::de::{from_reader, from_slice, from_str};
pub use self::ser::{to_string, to_vec, to_writer};
pub use self::value::{Sequence, Value, from_value, to_value};
pub use self::error::{Error, Result};
pub use self::mapping::Mapping;

mod de;
mod ser;
mod value;
mod error;
mod path;
mod mapping;
<|endoftext|>