<commit_before>// Copyright 2018 The Grin Developers
//
// 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.

use core::core::{self, amount_to_hr_string};
use libwallet::types::{AcctPathMapping, OutputData, OutputStatus, TxLogEntry, WalletInfo};
use libwallet::Error;
use prettytable;
use std::io::prelude::Write;
use term;
use util;
use util::secp::pedersen;

/// Display outputs in a pretty way
pub fn outputs(
	account: &str,
	cur_height: u64,
	validated: bool,
	outputs: Vec<(OutputData, pedersen::Commitment)>,
) -> Result<(), Error> {
	let title = format!(
		"Wallet Outputs - Account '{}' - Block Height: {}",
		account, cur_height
	);
	println!();
	let mut t = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	t.reset().unwrap();

	let mut table = table!();

	table.set_titles(row![
		bMG->"Output Commitment",
		bMG->"Block Height",
		bMG->"Locked Until",
		bMG->"Status",
		bMG->"Coinbase?",
		bMG->"# Confirms",
		bMG->"Value",
		bMG->"Tx"
	]);

	for (out, commit) in outputs {
		let commit = format!("{}", util::to_hex(commit.as_ref().to_vec()));
		let height = format!("{}", out.height);
		let lock_height = format!("{}", out.lock_height);
		let is_coinbase = format!("{}", out.is_coinbase);

		// Mark unconfirmed coinbase outputs as "Mining" instead of "Unconfirmed"
		let status = match out.status {
			OutputStatus::Unconfirmed if out.is_coinbase => "Mining".to_string(),
			_ => format!("{}", out.status),
		};

		let num_confirmations = format!("{}", out.num_confirmations(cur_height));
		let value = format!("{}", core::amount_to_hr_string(out.value, false));
		let tx = match out.tx_log_entry {
			None => "".to_owned(),
			Some(t) => t.to_string(),
		};
		table.add_row(row![
			bFC->commit,
			bFB->height,
			bFB->lock_height,
			bFR->status,
			bFY->is_coinbase,
			bFB->num_confirmations,
			bFG->value,
			bFC->tx,
		]);
	}

	table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
	table.printstd();
	println!();

	if !validated {
		println!(
			"\nWARNING: Wallet failed to verify data. \
			 The above is from local cache and possibly invalid! \
			 (is your `grin server` offline or broken?)"
		);
	}
	Ok(())
}

/// Display transaction log in a pretty way
pub fn txs(
	account: &str,
	cur_height: u64,
	validated: bool,
	txs: Vec<TxLogEntry>,
	include_status: bool,
) -> Result<(), Error> {
	let title = format!(
		"Transaction Log - Account '{}' - Block Height: {}",
		account, cur_height
	);
	println!();
	let mut t = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	t.reset().unwrap();

	let mut table = table!();

	table.set_titles(row![
		bMG->"Id",
		bMG->"Type",
		bMG->"Shared Transaction Id",
		bMG->"Creation Time",
		bMG->"Confirmed?",
		bMG->"Confirmation Time",
		bMG->"Num. Inputs",
		bMG->"Num. Outputs",
		bMG->"Amount Credited",
		bMG->"Amount Debited",
		bMG->"Fee",
		bMG->"Net Difference",
		bMG->"Tx Data",
	]);

	for t in txs {
		let id = format!("{}", t.id);
		let slate_id = match t.tx_slate_id {
			Some(m) => format!("{}", m),
			None => "None".to_owned(),
		};
		let entry_type = format!("{}", t.tx_type);
		let creation_ts = format!("{}", t.creation_ts.format("%Y-%m-%d %H:%M:%S"));
		let confirmation_ts = match t.confirmation_ts {
			Some(m) => format!("{}", m.format("%Y-%m-%d %H:%M:%S")),
			None => "None".to_owned(),
		};
		let confirmed = format!("{}", t.confirmed);
		let num_inputs = format!("{}", t.num_inputs);
		let num_outputs = format!("{}", t.num_outputs);
		let amount_debited_str = core::amount_to_hr_string(t.amount_debited, true);
		let amount_credited_str = core::amount_to_hr_string(t.amount_credited, true);
		let fee = match t.fee {
			Some(f) => format!("{}", core::amount_to_hr_string(f, true)),
			None => "None".to_owned(),
		};
		let net_diff = if t.amount_credited >= t.amount_debited {
			core::amount_to_hr_string(t.amount_credited - t.amount_debited, true)
		} else {
			format!(
				"-{}",
				core::amount_to_hr_string(t.amount_debited - t.amount_credited, true)
			)
		};
		let tx_data = match t.tx_hex {
			Some(_) => format!("Exists"),
			None => "None".to_owned(),
		};
		table.add_row(row![
			bFC->id,
			bFC->entry_type,
			bFC->slate_id,
			bFB->creation_ts,
			bFC->confirmed,
			bFB->confirmation_ts,
			bFC->num_inputs,
			bFC->num_outputs,
			bFG->amount_credited_str,
			bFR->amount_debited_str,
			bFR->fee,
			bFY->net_diff,
			bFb->tx_data,
		]);
	}

	table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
	table.printstd();
	println!();

	if !validated && include_status {
		println!(
			"\nWARNING: Wallet failed to verify data. \
			 The above is from local cache and possibly invalid! \
			 (is your `grin server` offline or broken?)"
		);
	}
	Ok(())
}
/// Display summary info in a pretty way
pub fn info(account: &str, wallet_info: &WalletInfo, validated: bool) {
	println!(
		"\n____ Wallet Summary Info - Account '{}' as of height {} ____\n",
		account, wallet_info.last_confirmed_height
	);
	let mut table = table!(
		[bFG->"Total", FG->amount_to_hr_string(wallet_info.total, false)],
		[bFY->"Awaiting Confirmation", FY->amount_to_hr_string(wallet_info.amount_awaiting_confirmation, false)],
		[bFY->"Immature Coinbase", FY->amount_to_hr_string(wallet_info.amount_immature, false)],
		[bFG->"Currently Spendable", FG->amount_to_hr_string(wallet_info.amount_currently_spendable, false)],
		[Fw->"---------", Fw->"---------"],
		[Fr->"(Locked by previous transaction)", Fr->amount_to_hr_string(wallet_info.amount_locked, false)]
	);
	table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
	table.printstd();
	println!();
	if !validated {
		println!(
			"\nWARNING: Wallet failed to verify data against a live chain. \
			 The above is from local cache and only valid up to the given height! \
			 (is your `grin server` offline or broken?)"
		);
	}
}
/// Display list of wallet accounts in a pretty way
pub fn accounts(acct_mappings: Vec<AcctPathMapping>) {
	println!("\n____ Wallet Accounts ____\n",);
	let mut table = table!();

	table.set_titles(row![
		mMG->"Name",
		bMG->"Parent BIP-32 Derivation Path",
	]);
	for m in acct_mappings {
		table.add_row(row![
			bFC->m.label,
			bGC->m.path.to_bip_32_string(),
		]);
	}
	table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
	table.printstd();
	println!();
}
<commit_msg>Adjust the title format of wallet txs output (#1882)<commit_after>// Copyright 2018 The Grin Developers
//
// 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.

use core::core::{self, amount_to_hr_string};
use libwallet::types::{AcctPathMapping, OutputData, OutputStatus, TxLogEntry, WalletInfo};
use libwallet::Error;
use prettytable;
use std::io::prelude::Write;
use term;
use util;
use util::secp::pedersen;

/// Display outputs in a pretty way
pub fn outputs(
	account: &str,
	cur_height: u64,
	validated: bool,
	outputs: Vec<(OutputData, pedersen::Commitment)>,
) -> Result<(), Error> {
	let title = format!(
		"Wallet Outputs - Account '{}' - Block Height: {}",
		account, cur_height
	);
	println!();
	let mut t = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	t.reset().unwrap();

	let mut table = table!();

	table.set_titles(row![
		bMG->"Output Commitment",
		bMG->"Block Height",
		bMG->"Locked Until",
		bMG->"Status",
		bMG->"Coinbase?",
		bMG->"# Confirms",
		bMG->"Value",
		bMG->"Tx"
	]);

	for (out, commit) in outputs {
		let commit = format!("{}", util::to_hex(commit.as_ref().to_vec()));
		let height = format!("{}", out.height);
		let lock_height = format!("{}", out.lock_height);
		let is_coinbase = format!("{}", out.is_coinbase);

		// Mark unconfirmed coinbase outputs as "Mining" instead of "Unconfirmed"
		let status = match out.status {
			OutputStatus::Unconfirmed if out.is_coinbase => "Mining".to_string(),
			_ => format!("{}", out.status),
		};

		let num_confirmations = format!("{}", out.num_confirmations(cur_height));
		let value = format!("{}", core::amount_to_hr_string(out.value, false));
		let tx = match out.tx_log_entry {
			None => "".to_owned(),
			Some(t) => t.to_string(),
		};
		table.add_row(row![
			bFC->commit,
			bFB->height,
			bFB->lock_height,
			bFR->status,
			bFY->is_coinbase,
			bFB->num_confirmations,
			bFG->value,
			bFC->tx,
		]);
	}

	table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
	table.printstd();
	println!();

	if !validated {
		println!(
			"\nWARNING: Wallet failed to verify data. \
			 The above is from local cache and possibly invalid! \
			 (is your `grin server` offline or broken?)"
		);
	}
	Ok(())
}

/// Display transaction log in a pretty way
pub fn txs(
	account: &str,
	cur_height: u64,
	validated: bool,
	txs: Vec<TxLogEntry>,
	include_status: bool,
) -> Result<(), Error> {
	let title = format!(
		"Transaction Log - Account '{}' - Block Height: {}",
		account, cur_height
	);
	println!();
	let mut t = term::stdout().unwrap();
	t.fg(term::color::MAGENTA).unwrap();
	writeln!(t, "{}", title).unwrap();
	t.reset().unwrap();

	let mut table = table!();

	table.set_titles(row![
		bMG->"Id",
		bMG->"Type",
		bMG->"Shared Transaction Id",
		bMG->"Creation Time",
		bMG->"Confirmed?",
		bMG->"Confirmation Time",
		bMG->"Num. \nInputs",
		bMG->"Num. \nOutputs",
		bMG->"Amount \nCredited",
		bMG->"Amount \nDebited",
		bMG->"Fee",
		bMG->"Net \nDifference",
		bMG->"Tx \nData",
	]);

	for t in txs {
		let id = format!("{}", t.id);
		let slate_id = match t.tx_slate_id {
			Some(m) => format!("{}", m),
			None => "None".to_owned(),
		};
		let entry_type = format!("{}", t.tx_type);
		let creation_ts = format!("{}", t.creation_ts.format("%Y-%m-%d %H:%M:%S"));
		let confirmation_ts = match t.confirmation_ts {
			Some(m) => format!("{}", m.format("%Y-%m-%d %H:%M:%S")),
			None => "None".to_owned(),
		};
		let confirmed = format!("{}", t.confirmed);
		let num_inputs = format!("{}", t.num_inputs);
		let num_outputs = format!("{}", t.num_outputs);
		let amount_debited_str = core::amount_to_hr_string(t.amount_debited, true);
		let amount_credited_str = core::amount_to_hr_string(t.amount_credited, true);
		let fee = match t.fee {
			Some(f) => format!("{}", core::amount_to_hr_string(f, true)),
			None => "None".to_owned(),
		};
		let net_diff = if t.amount_credited >= t.amount_debited {
			core::amount_to_hr_string(t.amount_credited - t.amount_debited, true)
		} else {
			format!(
				"-{}",
				core::amount_to_hr_string(t.amount_debited - t.amount_credited, true)
			)
		};
		let tx_data = match t.tx_hex {
			Some(_) => format!("Exists"),
			None => "None".to_owned(),
		};
		table.add_row(row![
			bFC->id,
			bFC->entry_type,
			bFC->slate_id,
			bFB->creation_ts,
			bFC->confirmed,
			bFB->confirmation_ts,
			bFC->num_inputs,
			bFC->num_outputs,
			bFG->amount_credited_str,
			bFR->amount_debited_str,
			bFR->fee,
			bFY->net_diff,
			bFb->tx_data,
		]);
	}

	table.set_format(*prettytable::format::consts::FORMAT_NO_COLSEP);
	table.printstd();
	println!();

	if !validated && include_status {
		println!(
			"\nWARNING: Wallet failed to verify data. \
			 The above is from local cache and possibly invalid! \
			 (is your `grin server` offline or broken?)"
		);
	}
	Ok(())
}
/// Display summary info in a pretty way
pub fn info(account: &str, wallet_info: &WalletInfo, validated: bool) {
	println!(
		"\n____ Wallet Summary Info - Account '{}' as of height {} ____\n",
		account, wallet_info.last_confirmed_height
	);
	let mut table = table!(
		[bFG->"Total", FG->amount_to_hr_string(wallet_info.total, false)],
		[bFY->"Awaiting Confirmation", FY->amount_to_hr_string(wallet_info.amount_awaiting_confirmation, false)],
		[bFY->"Immature Coinbase", FY->amount_to_hr_string(wallet_info.amount_immature, false)],
		[bFG->"Currently Spendable", FG->amount_to_hr_string(wallet_info.amount_currently_spendable, false)],
		[Fw->"---------", Fw->"---------"],
		[Fr->"(Locked by previous transaction)", Fr->amount_to_hr_string(wallet_info.amount_locked, false)]
	);
	table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
	table.printstd();
	println!();
	if !validated {
		println!(
			"\nWARNING: Wallet failed to verify data against a live chain. \
			 The above is from local cache and only valid up to the given height! \
			 (is your `grin server` offline or broken?)"
		);
	}
}
/// Display list of wallet accounts in a pretty way
pub fn accounts(acct_mappings: Vec<AcctPathMapping>) {
	println!("\n____ Wallet Accounts ____\n",);
	let mut table = table!();

	table.set_titles(row![
		mMG->"Name",
		bMG->"Parent BIP-32 Derivation Path",
	]);
	for m in acct_mappings {
		table.add_row(row![
			bFC->m.label,
			bGC->m.path.to_bip_32_string(),
		]);
	}
	table.set_format(*prettytable::format::consts::FORMAT_NO_BORDER_LINE_SEPARATOR);
	table.printstd();
	println!();
}
<|endoftext|>


<commit_before>//! Using `select!` to send and receive on the same channel at the same time.
//!
//! # Copyright
//!
//! This example is based on the following program in Go.
//!
//! Author: Stefan Nilsson
//! License: Creative Commons Attribution 3.0 Unported License.
//! Sources:
//!   - https://web.archive.org/web/20171209034309/https://www.nada.kth.se/~snilsson/concurrency
//!   - http://www.nada.kth.se/~snilsson/concurrency/src/matching.go
//!
//! ```go
//! func main() {
//!     people := []string{"Anna", "Bob", "Cody", "Dave", "Eva"}
//!     match := make(chan string, 1) // Make room for one unmatched send.
//!     wg := new(sync.WaitGroup)
//!     for _, name := range people {
//!         wg.Add(1)
//!         go Seek(name, match, wg)
//!     }
//!     wg.Wait()
//!     select {
//!     case name := <-match:
//!         fmt.Printf("No one received %s’s message.\n", name)
//!     default:
//!         // There was no pending send operation.
//!     }
//! }
//!
//! // Seek either sends or receives, whichever possible, a name on the match
//! // channel and notifies the wait group when done.
//! func Seek(name string, match chan string, wg *sync.WaitGroup) {
//!     select {
//!     case peer := <-match:
//!         fmt.Printf("%s received a message from %s.\n", name, peer)
//!     case match <- name:
//!         // Wait for someone to receive my message.
//!     }
//!     wg.Done()
//! }
//! ```

extern crate crossbeam;
#[macro_use]
extern crate crossbeam_channel;

use crossbeam_channel::bounded;

fn main() {
    let people = vec!["Anna", "Bob", "Cody", "Dave", "Eva"];
    let (s, r) = bounded(1); // Make room for one unmatched send.

    // Either send my name into the channel or receive someone else's, whatever happens first.
    let seek = |name, s, r| {
        select! {
            recv(r) -> peer => println!("{} received a message from {}.", name, peer.unwrap()),
            send(s, name) -> _ => {}, // Wait for someone to receive my message.
        }
    };

    crossbeam::scope(|scope| {
        for name in people {
            let (s, r) = (s.clone(), r.clone());
            scope.spawn(move || seek(name, s, r));
        }
    });

    // Check if there is a pending send operation.
    if let Ok(name) = r.try_recv() {
        println!("No one received {}’s message.", name);
    }
}
<commit_msg>Add a license for matching.go<commit_after>//! Using `select!` to send and receive on the same channel at the same time.
//!
//! # Copyright
//!
//! This example is based on the following program in Go.
//!
//! Author: Stefan Nilsson
//! License: Creative Commons Attribution 3.0 Unported License.
//! Sources:
//!   - http://www.nada.kth.se/~snilsson/concurrency/src/matching.go
//!   - https://web.archive.org/web/20171209034309/https://www.nada.kth.se/~snilsson/concurrency
//!
//! ```text
//! Creative Commons Legal Code
//!
//! Attribution 3.0 Unported
//!
//!     CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
//!     LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
//!     ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
//!     INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
//!     REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
//!     DAMAGES RESULTING FROM ITS USE.
//!
//! License
//!
//! THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
//! COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
//! COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
//! AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
//!
//! BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
//! TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
//! BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
//! CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
//! CONDITIONS.
//!
//! 1. Definitions
//!
//!  a. "Adaptation" means a work based upon the Work, or upon the Work and
//!     other pre-existing works, such as a translation, adaptation,
//!     derivative work, arrangement of music or other alterations of a
//!     literary or artistic work, or phonogram or performance and includes
//!     cinematographic adaptations or any other form in which the Work may be
//!     recast, transformed, or adapted including in any form recognizably
//!     derived from the original, except that a work that constitutes a
//!     Collection will not be considered an Adaptation for the purpose of
//!     this License. For the avoidance of doubt, where the Work is a musical
//!     work, performance or phonogram, the synchronization of the Work in
//!     timed-relation with a moving image ("synching") will be considered an
//!     Adaptation for the purpose of this License.
//!  b. "Collection" means a collection of literary or artistic works, such as
//!     encyclopedias and anthologies, or performances, phonograms or
//!     broadcasts, or other works or subject matter other than works listed
//!     in Section 1(f) below, which, by reason of the selection and
//!     arrangement of their contents, constitute intellectual creations, in
//!     which the Work is included in its entirety in unmodified form along
//!     with one or more other contributions, each constituting separate and
//!     independent works in themselves, which together are assembled into a
//!     collective whole. A work that constitutes a Collection will not be
//!     considered an Adaptation (as defined above) for the purposes of this
//!     License.
//!  c. "Distribute" means to make available to the public the original and
//!     copies of the Work or Adaptation, as appropriate, through sale or
//!     other transfer of ownership.
//!  d. "Licensor" means the individual, individuals, entity or entities that
//!     offer(s) the Work under the terms of this License.
//!  e. "Original Author" means, in the case of a literary or artistic work,
//!     the individual, individuals, entity or entities who created the Work
//!     or if no individual or entity can be identified, the publisher; and in
//!     addition (i) in the case of a performance the actors, singers,
//!     musicians, dancers, and other persons who act, sing, deliver, declaim,
//!     play in, interpret or otherwise perform literary or artistic works or
//!     expressions of folklore; (ii) in the case of a phonogram the producer
//!     being the person or legal entity who first fixes the sounds of a
//!     performance or other sounds; and, (iii) in the case of broadcasts, the
//!     organization that transmits the broadcast.
//!  f. "Work" means the literary and/or artistic work offered under the terms
//!     of this License including without limitation any production in the
//!     literary, scientific and artistic domain, whatever may be the mode or
//!     form of its expression including digital form, such as a book,
//!     pamphlet and other writing; a lecture, address, sermon or other work
//!     of the same nature; a dramatic or dramatico-musical work; a
//!     choreographic work or entertainment in dumb show; a musical
//!     composition with or without words; a cinematographic work to which are
//!     assimilated works expressed by a process analogous to cinematography;
//!     a work of drawing, painting, architecture, sculpture, engraving or
//!     lithography; a photographic work to which are assimilated works
//!     expressed by a process analogous to photography; a work of applied
//!     art; an illustration, map, plan, sketch or three-dimensional work
//!     relative to geography, topography, architecture or science; a
//!     performance; a broadcast; a phonogram; a compilation of data to the
//!     extent it is protected as a copyrightable work; or a work performed by
//!     a variety or circus performer to the extent it is not otherwise
//!     considered a literary or artistic work.
//!  g. "You" means an individual or entity exercising rights under this
//!     License who has not previously violated the terms of this License with
//!     respect to the Work, or who has received express permission from the
//!     Licensor to exercise rights under this License despite a previous
//!     violation.
//!  h. "Publicly Perform" means to perform public recitations of the Work and
//!     to communicate to the public those public recitations, by any means or
//!     process, including by wire or wireless means or public digital
//!     performances; to make available to the public Works in such a way that
//!     members of the public may access these Works from a place and at a
//!     place individually chosen by them; to perform the Work to the public
//!     by any means or process and the communication to the public of the
//!     performances of the Work, including by public digital performance; to
//!     broadcast and rebroadcast the Work by any means including signs,
//!     sounds or images.
//!  i. "Reproduce" means to make copies of the Work by any means including
//!     without limitation by sound or visual recordings and the right of
//!     fixation and reproducing fixations of the Work, including storage of a
//!     protected performance or phonogram in digital form or other electronic
//!     medium.
//!
//! 2. Fair Dealing Rights. Nothing in this License is intended to reduce,
//! limit, or restrict any uses free from copyright or rights arising from
//! limitations or exceptions that are provided for in connection with the
//! copyright protection under copyright law or other applicable laws.
//!
//! 3. License Grant. Subject to the terms and conditions of this License,
//! Licensor hereby grants You a worldwide, royalty-free, non-exclusive,
//! perpetual (for the duration of the applicable copyright) license to
//! exercise the rights in the Work as stated below:
//!
//!  a. to Reproduce the Work, to incorporate the Work into one or more
//!     Collections, and to Reproduce the Work as incorporated in the
//!     Collections;
//!  b. to create and Reproduce Adaptations provided that any such Adaptation,
//!     including any translation in any medium, takes reasonable steps to
//!     clearly label, demarcate or otherwise identify that changes were made
//!     to the original Work. For example, a translation could be marked "The
//!     original work was translated from English to Spanish," or a
//!     modification could indicate "The original work has been modified.";
//!  c. to Distribute and Publicly Perform the Work including as incorporated
//!     in Collections; and,
//!  d. to Distribute and Publicly Perform Adaptations.
//!  e. For the avoidance of doubt:
//!
//!      i. Non-waivable Compulsory License Schemes. In those jurisdictions in
//!         which the right to collect royalties through any statutory or
//!         compulsory licensing scheme cannot be waived, the Licensor
//!         reserves the exclusive right to collect such royalties for any
//!         exercise by You of the rights granted under this License;
//!     ii. Waivable Compulsory License Schemes. In those jurisdictions in
//!         which the right to collect royalties through any statutory or
//!         compulsory licensing scheme can be waived, the Licensor waives the
//!         exclusive right to collect such royalties for any exercise by You
//!         of the rights granted under this License; and,
//!    iii. Voluntary License Schemes. The Licensor waives the right to
//!         collect royalties, whether individually or, in the event that the
//!         Licensor is a member of a collecting society that administers
//!         voluntary licensing schemes, via that society, from any exercise
//!         by You of the rights granted under this License.
//!
//! The above rights may be exercised in all media and formats whether now
//! known or hereafter devised. The above rights include the right to make
//! such modifications as are technically necessary to exercise the rights in
//! other media and formats. Subject to Section 8(f), all rights not expressly
//! granted by Licensor are hereby reserved.
//!
//! 4. Restrictions. The license granted in Section 3 above is expressly made
//! subject to and limited by the following restrictions:
//!
//!  a. You may Distribute or Publicly Perform the Work only under the terms
//!     of this License. You must include a copy of, or the Uniform Resource
//!     Identifier (URI) for, this License with every copy of the Work You
//!     Distribute or Publicly Perform. You may not offer or impose any terms
//!     on the Work that restrict the terms of this License or the ability of
//!     the recipient of the Work to exercise the rights granted to that
//!     recipient under the terms of the License. You may not sublicense the
//!     Work. You must keep intact all notices that refer to this License and
//!     to the disclaimer of warranties with every copy of the Work You
//!     Distribute or Publicly Perform. When You Distribute or Publicly
//!     Perform the Work, You may not impose any effective technological
//!     measures on the Work that restrict the ability of a recipient of the
//!     Work from You to exercise the rights granted to that recipient under
//!     the terms of the License. This Section 4(a) applies to the Work as
//!     incorporated in a Collection, but this does not require the Collection
//!     apart from the Work itself to be made subject to the terms of this
//!     License. If You create a Collection, upon notice from any Licensor You
//!     must, to the extent practicable, remove from the Collection any credit
//!     as required by Section 4(b), as requested. If You create an
//!     Adaptation, upon notice from any Licensor You must, to the extent
//!     practicable, remove from the Adaptation any credit as required by
//!     Section 4(b), as requested.
//!  b. If You Distribute, or Publicly Perform the Work or any Adaptations or
//!     Collections, You must, unless a request has been made pursuant to
//!     Section 4(a), keep intact all copyright notices for the Work and
//!     provide, reasonable to the medium or means You are utilizing: (i) the
//!     name of the Original Author (or pseudonym, if applicable) if supplied,
//!     and/or if the Original Author and/or Licensor designate another party
//!     or parties (e.g., a sponsor institute, publishing entity, journal) for
//!     attribution ("Attribution Parties") in Licensor's copyright notice,
//!     terms of service or by other reasonable means, the name of such party
//!     or parties; (ii) the title of the Work if supplied; (iii) to the
//!     extent reasonably practicable, the URI, if any, that Licensor
//!     specifies to be associated with the Work, unless such URI does not
//!     refer to the copyright notice or licensing information for the Work;
//!     and (iv) , consistent with Section 3(b), in the case of an Adaptation,
//!     a credit identifying the use of the Work in the Adaptation (e.g.,
//!     "French translation of the Work by Original Author," or "Screenplay
//!     based on original Work by Original Author"). The credit required by
//!     this Section 4 (b) may be implemented in any reasonable manner;
//!     provided, however, that in the case of a Adaptation or Collection, at
//!     a minimum such credit will appear, if a credit for all contributing
//!     authors of the Adaptation or Collection appears, then as part of these
//!     credits and in a manner at least as prominent as the credits for the
//!     other contributing authors. For the avoidance of doubt, You may only
//!     use the credit required by this Section for the purpose of attribution
//!     in the manner set out above and, by exercising Your rights under this
//!     License, You may not implicitly or explicitly assert or imply any
//!     connection with, sponsorship or endorsement by the Original Author,
//!     Licensor and/or Attribution Parties, as appropriate, of You or Your
//!     use of the Work, without the separate, express prior written
//!     permission of the Original Author, Licensor and/or Attribution
//!     Parties.
//!  c. Except as otherwise agreed in writing by the Licensor or as may be
//!     otherwise permitted by applicable law, if You Reproduce, Distribute or
//!     Publicly Perform the Work either by itself or as part of any
//!     Adaptations or Collections, You must not distort, mutilate, modify or
//!     take other derogatory action in relation to the Work which would be
//!     prejudicial to the Original Author's honor or reputation. Licensor
//!     agrees that in those jurisdictions (e.g. Japan), in which any exercise
//!     of the right granted in Section 3(b) of this License (the right to
//!     make Adaptations) would be deemed to be a distortion, mutilation,
//!     modification or other derogatory action prejudicial to the Original
//!     Author's honor and reputation, the Licensor will waive or not assert,
//!     as appropriate, this Section, to the fullest extent permitted by the
//!     applicable national law, to enable You to reasonably exercise Your
//!     right under Section 3(b) of this License (right to make Adaptations)
//!     but not otherwise.
//!
//! 5. Representations, Warranties and Disclaimer
//!
//! UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR
//! OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
//! KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE,
//! INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
//! FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF
//! LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS,
//! WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION
//! OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
//!
//! 6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE
//! LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR
//! ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES
//! ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS
//! BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
//!
//! 7. Termination
//!
//!  a. This License and the rights granted hereunder will terminate
//!     automatically upon any breach by You of the terms of this License.
//!     Individuals or entities who have received Adaptations or Collections
//!     from You under this License, however, will not have their licenses
//!     terminated provided such individuals or entities remain in full
//!     compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will
//!     survive any termination of this License.
//!  b. Subject to the above terms and conditions, the license granted here is
//!     perpetual (for the duration of the applicable copyright in the Work).
//!     Notwithstanding the above, Licensor reserves the right to release the
//!     Work under different license terms or to stop distributing the Work at
//!     any time; provided, however that any such election will not serve to
//!     withdraw this License (or any other license that has been, or is
//!     required to be, granted under the terms of this License), and this
//!     License will continue in full force and effect unless terminated as
//!     stated above.
//!
//! 8. Miscellaneous
//!
//!  a. Each time You Distribute or Publicly Perform the Work or a Collection,
//!     the Licensor offers to the recipient a license to the Work on the same
//!     terms and conditions as the license granted to You under this License.
//!  b. Each time You Distribute or Publicly Perform an Adaptation, Licensor
//!     offers to the recipient a license to the original Work on the same
//!     terms and conditions as the license granted to You under this License.
//!  c. If any provision of this License is invalid or unenforceable under
//!     applicable law, it shall not affect the validity or enforceability of
//!     the remainder of the terms of this License, and without further action
//!     by the parties to this agreement, such provision shall be reformed to
//!     the minimum extent necessary to make such provision valid and
//!     enforceable.
//!  d. No term or provision of this License shall be deemed waived and no
//!     breach consented to unless such waiver or consent shall be in writing
//!     and signed by the party to be charged with such waiver or consent.
//!  e. This License constitutes the entire agreement between the parties with
//!     respect to the Work licensed here. There are no understandings,
//!     agreements or representations with respect to the Work not specified
//!     here. Licensor shall not be bound by any additional provisions that
//!     may appear in any communication from You. This License may not be
//!     modified without the mutual written agreement of the Licensor and You.
//!  f. The rights granted under, and the subject matter referenced, in this
//!     License were drafted utilizing the terminology of the Berne Convention
//!     for the Protection of Literary and Artistic Works (as amended on
//!     September 28, 1979), the Rome Convention of 1961, the WIPO Copyright
//!     Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996
//!     and the Universal Copyright Convention (as revised on July 24, 1971).
//!     These rights and subject matter take effect in the relevant
//!     jurisdiction in which the License terms are sought to be enforced
//!     according to the corresponding provisions of the implementation of
//!     those treaty provisions in the applicable national law. If the
//!     standard suite of rights granted under applicable copyright law
//!     includes additional rights not granted under this License, such
//!     additional rights are deemed to be included in the License; this
//!     License is not intended to restrict the license of any rights under
//!     applicable law.
//!
//!
//! Creative Commons Notice
//!
//!     Creative Commons is not a party to this License, and makes no warranty
//!     whatsoever in connection with the Work. Creative Commons will not be
//!     liable to You or any party on any legal theory for any damages
//!     whatsoever, including without limitation any general, special,
//!     incidental or consequential damages arising in connection to this
//!     license. Notwithstanding the foregoing two (2) sentences, if Creative
//!     Commons has expressly identified itself as the Licensor hereunder, it
//!     shall have all rights and obligations of Licensor.
//!
//!     Except for the limited purpose of indicating to the public that the
//!     Work is licensed under the CCPL, Creative Commons does not authorize
//!     the use by either party of the trademark "Creative Commons" or any
//!     related trademark or logo of Creative Commons without the prior
//!     written consent of Creative Commons. Any permitted use will be in
//!     compliance with Creative Commons' then-current trademark usage
//!     guidelines, as may be published on its website or otherwise made
//!     available upon request from time to time. For the avoidance of doubt,
//!     this trademark restriction does not form part of this License.
//!
//!     Creative Commons may be contacted at https://creativecommons.org/.
//! ```

extern crate crossbeam;
#[macro_use]
extern crate crossbeam_channel;

use crossbeam_channel::bounded;

fn main() {
    let people = vec!["Anna", "Bob", "Cody", "Dave", "Eva"];
    let (s, r) = bounded(1); // Make room for one unmatched send.

    // Either send my name into the channel or receive someone else's, whatever happens first.
    let seek = |name, s, r| {
        select! {
            recv(r) -> peer => println!("{} received a message from {}.", name, peer.unwrap()),
            send(s, name) -> _ => {}, // Wait for someone to receive my message.
        }
    };

    crossbeam::scope(|scope| {
        for name in people {
            let (s, r) = (s.clone(), r.clone());
            scope.spawn(move || seek(name, s, r));
        }
    });

    // Check if there is a pending send operation.
    if let Ok(name) = r.try_recv() {
        println!("No one received {}’s message.", name);
    }
}
<|endoftext|>


<commit_before>//! Using `select!` to send and receive on the same channel at the same time.
//!
//! This example is based on the following program in Go.
//!
//! Source:
//!   - https://web.archive.org/web/20171209034309/https://www.nada.kth.se/~snilsson/concurrency
//!   - http://www.nada.kth.se/~snilsson/concurrency/src/matching.go
//!
//! Copyright & License:
//!   - Stefan Nilsson
//!   - Creative Commons Attribution 3.0 Unported License
//!   - https://creativecommons.org/licenses/by/3.0/
//!
//! ```go
//! func main() {
//!     people := []string{"Anna", "Bob", "Cody", "Dave", "Eva"}
//!     match := make(chan string, 1) // Make room for one unmatched send.
//!     wg := new(sync.WaitGroup)
//!     for _, name := range people {
//!         wg.Add(1)
//!         go Seek(name, match, wg)
//!     }
//!     wg.Wait()
//!     select {
//!     case name := <-match:
//!         fmt.Printf("No one received %s’s message.\n", name)
//!     default:
//!         // There was no pending send operation.
//!     }
//! }
//!
//! // Seek either sends or receives, whichever possible, a name on the match
//! // channel and notifies the wait group when done.
//! func Seek(name string, match chan string, wg *sync.WaitGroup) {
//!     select {
//!     case peer := <-match:
//!         fmt.Printf("%s received a message from %s.\n", name, peer)
//!     case match <- name:
//!         // Wait for someone to receive my message.
//!     }
//!     wg.Done()
//! }
//! ```

#[macro_use]
extern crate crossbeam_channel;
extern crate crossbeam_utils;

use crossbeam_channel::bounded;
use crossbeam_utils::thread;

fn main() {
    let people = vec!["Anna", "Bob", "Cody", "Dave", "Eva"];
    let (s, r) = bounded(1); // Make room for one unmatched send.

    // Either send my name into the channel or receive someone else's, whatever happens first.
    let seek = |name, s, r| {
        select! {
            recv(r) -> peer => println!("{} received a message from {}.", name, peer.unwrap()),
            send(s, name) -> _ => {}, // Wait for someone to receive my message.
        }
    };

    thread::scope(|scope| {
        for name in people {
            let (s, r) = (s.clone(), r.clone());
            scope.spawn(move || seek(name, s, r));
        }
    });

    // Check if there is a pending send operation.
    if let Ok(name) = r.try_recv() {
        println!("No one received {}’s message.", name);
    }
}
<commit_msg>Fix an error in matching.rs<commit_after>//! Using `select!` to send and receive on the same channel at the same time.
//!
//! This example is based on the following program in Go.
//!
//! Source:
//!   - https://web.archive.org/web/20171209034309/https://www.nada.kth.se/~snilsson/concurrency
//!   - http://www.nada.kth.se/~snilsson/concurrency/src/matching.go
//!
//! Copyright & License:
//!   - Stefan Nilsson
//!   - Creative Commons Attribution 3.0 Unported License
//!   - https://creativecommons.org/licenses/by/3.0/
//!
//! ```go
//! func main() {
//!     people := []string{"Anna", "Bob", "Cody", "Dave", "Eva"}
//!     match := make(chan string, 1) // Make room for one unmatched send.
//!     wg := new(sync.WaitGroup)
//!     for _, name := range people {
//!         wg.Add(1)
//!         go Seek(name, match, wg)
//!     }
//!     wg.Wait()
//!     select {
//!     case name := <-match:
//!         fmt.Printf("No one received %s’s message.\n", name)
//!     default:
//!         // There was no pending send operation.
//!     }
//! }
//!
//! // Seek either sends or receives, whichever possible, a name on the match
//! // channel and notifies the wait group when done.
//! func Seek(name string, match chan string, wg *sync.WaitGroup) {
//!     select {
//!     case peer := <-match:
//!         fmt.Printf("%s received a message from %s.\n", name, peer)
//!     case match <- name:
//!         // Wait for someone to receive my message.
//!     }
//!     wg.Done()
//! }
//! ```

#[macro_use]
extern crate crossbeam_channel;
extern crate crossbeam_utils;

use crossbeam_channel::bounded;
use crossbeam_utils::thread;

fn main() {
    let people = vec!["Anna", "Bob", "Cody", "Dave", "Eva"];
    let (s, r) = bounded(1); // Make room for one unmatched send.

    // Either send my name into the channel or receive someone else's, whatever happens first.
    let seek = |name, s, r| {
        select! {
            recv(r) -> peer => println!("{} received a message from {}.", name, peer.unwrap()),
            send(s, name) -> _ => {}, // Wait for someone to receive my message.
        }
    };

    thread::scope(|scope| {
        for name in people {
            let (s, r) = (s.clone(), r.clone());
            scope.spawn(move |_| seek(name, s, r));
        }
    });

    // Check if there is a pending send operation.
    if let Ok(name) = r.try_recv() {
        println!("No one received {}’s message.", name);
    }
}
<|endoftext|>


<commit_before>//! Module for handling content types

use std::collections::BTreeMap;
use mime::Mime;
use log::*;
use maplit::btreemap;
use itertools::Itertools;
use lazy_static::*;
use std::str::FromStr;

#[cfg(test)]
use expectest::prelude::*;

/// Content type of a body
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContentType {
  /// Main content type
  pub main_type: String,
  /// Sub content type
  pub sub_type: String,
  /// Content type attributes
  pub attributes: BTreeMap<String, String>,
  /// Suffix
  pub suffix: Option<String>
}

lazy_static! {
  /// XML Content Type
  pub static ref XML: ContentType = ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    .. ContentType::default()
  };

  /// HTML Content Type
  pub static ref HTML: ContentType = ContentType {
    main_type: "text".into(),
    sub_type: "html".into(),
    .. ContentType::default()
  };

  /// JSON Content Type
  pub static ref JSON: ContentType = ContentType {
    main_type: "application".into(),
    sub_type: "json".into(),
    .. ContentType::default()
  };

  /// TEXT Content Type
  pub static ref TEXT: ContentType = ContentType {
    main_type: "text".into(),
    sub_type: "plain".into(),
    .. ContentType::default()
  };
}

impl ContentType {
  /// Parses a string into a ContentType
  pub fn parse<'a, S: Into<&'a str>>(content_type: S) -> Result<ContentType, String> {
    let content_type = content_type.into();
    match Mime::from_str(content_type) {
      Ok(mime) => {
        Ok(ContentType {
          main_type: mime.type_().to_string(),
          sub_type: mime.subtype().to_string(),
          attributes: mime.params().map(|(key, value)| (key.to_string(), value.to_string())).collect(),
          suffix: mime.suffix().map(|name| name.to_string())
        })
      },
      Err(err) => {
        let message = format!("Failed to parse '{}' as a content type: {}",
                              content_type, err);
        warn!("{}", message);
        Err(message)
      }
    }
  }

  /// If it is a JSON type
  pub fn is_json(&self) -> bool {
    self.main_type == "application" && (self.sub_type.starts_with("json") ||
      self.suffix.as_ref().unwrap_or(&String::default()) == "json")
  }

  /// If it is a XML type
  pub fn is_xml(&self) -> bool {
    self.main_type == "application" && (self.sub_type == "xml" ||
      self.suffix.as_ref().unwrap_or(&String::default()) == "xml")
  }

  /// If it is a text type
  pub fn is_text(&self) -> bool {
    self.main_type == "text" || self.is_xml() || self.is_json()
  }

  /// If it is a known binary type
  pub fn is_binary(&self) -> bool {
    match self.main_type.as_str() {
      "audio" | "font" | "image" | "video" => true,
      "text" => false,
      _ => false
    }
  }

  /// Returns the base type with no attributes or suffix
  pub fn base_type(&self) -> ContentType {
    match self.suffix.as_ref() {
      Some(suffix) => ContentType {
        main_type: self.main_type.clone(),
        sub_type: suffix.clone(),
        .. ContentType::default()
      },
      None => ContentType {
        main_type: self.main_type.clone(),
        sub_type: self.sub_type.clone(),
        .. ContentType::default()
      }
    }
  }

  /// If the content type is the default type
  pub fn is_unknown(&self) -> bool {
    self.main_type == "*" || self.sub_type == "*"
  }

  /// Equals, ignoring attributes if not present on self
  pub fn is_equivalent_to(&self, other: &ContentType) -> bool {
    if self.attributes.is_empty() {
      self.main_type == other.main_type && self.sub_type == other.sub_type
    } else {
      self == other
    }
  }
}

impl Default for ContentType {
  fn default() -> Self {
    ContentType {
      main_type: "*".into(),
      sub_type: "*".into(),
      attributes: btreemap!{},
      suffix: None
    }
  }
}

impl std::fmt::Display for ContentType {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    if self.attributes.is_empty() {
      write!(f, "{}/{}", self.main_type, self.sub_type)
    } else {
      write!(f, "{}/{};{}", self.main_type, self.sub_type, self.attributes.iter()
        .map(|(key, value)| format!("{}={}", key, value)).join(";"))
    }
  }
}

impl From<String> for ContentType {
  fn from(s: String) -> Self {
    ContentType::parse(s.as_str()).unwrap_or_default()
  }
}

impl From<&String> for ContentType {
  fn from(s: &String) -> Self {
    ContentType::parse(s.as_str()).unwrap_or_default()
  }
}

impl From<&str> for ContentType {
  fn from(s: &str) -> Self {
    ContentType::parse(s).unwrap_or_default()
  }
}

impl PartialEq<str> for ContentType {
  fn eq(&self, other: &str) -> bool {
    match ContentType::parse(other) {
      Ok(other) => *self == other,
      Err(_) => false
    }
  }
}

impl PartialEq<&str> for ContentType {
  fn eq(&self, other: &&str) -> bool {
    match ContentType::parse(*other) {
      Ok(other) => *self == other,
      Err(_) => false
    }
  }
}

#[test]
fn parse_test() {
  let content_type = &ContentType::parse("application/json").unwrap();
  expect!(&content_type.main_type).to(be_equal_to(&"application".to_string()));
  expect!(&content_type.sub_type).to(be_equal_to(&"json".to_string()));
  expect!(content_type.attributes.iter()).to(be_empty());
  expect!(content_type.clone().suffix).to(be_none());

  let content_type = &ContentType::parse("application/json;charset=UTF-16").unwrap();
  expect!(&content_type.main_type).to(be_equal_to(&"application".to_string()));
  expect!(&content_type.sub_type).to(be_equal_to(&"json".to_string()));
  expect!(content_type.clone().attributes).to(be_equal_to(btreemap!{
    "charset".to_string() => "utf-16".to_string()
  }));
  expect!(content_type.clone().suffix).to(be_none());

  let content_type = &ContentType::parse("application/hal+json; charset=UTF-8").unwrap();
  expect!(&content_type.main_type).to(be_equal_to(&"application".to_string()));
  expect!(&content_type.sub_type).to(be_equal_to(&"hal".to_string()));
  expect!(content_type.clone().attributes).to(be_equal_to(btreemap!{
    "charset".to_string() => "utf-8".to_string()
  }));
  expect!(content_type.clone().suffix).to(be_some().value("json".to_string()));
}

#[test]
fn to_string_test() {
  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "hal+json".into(),
    .. ContentType::default()
  };
  expect!(content_type.to_string()).to(be_equal_to("application/hal+json".to_string()));

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "hal+json".into(),
    attributes: btreemap! {
      "charset".to_string() => "UTF-32".to_string(),
      "b".to_string() => "c".to_string()
    },
    suffix: None
  };
  expect!(content_type.to_string()).to(be_equal_to("application/hal+json;b=c;charset=UTF-32".to_string()));
}

#[test]
fn is_json_test() {
  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "hal".into(),
    suffix: Some("json".to_string()),
    .. ContentType::default()
  };
  expect!(content_type.is_json()).to(be_true());

  let content_type = ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_json()).to(be_false());

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "json".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_json()).to(be_true());

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "json-rpc".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_json()).to(be_true());
}

#[test]
fn is_xml_test() {
  let content_type = ContentType::parse("application/atom+xml").unwrap();
  expect!(content_type.is_xml()).to(be_true());

  let content_type = ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_xml()).to(be_false());

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_xml()).to(be_true());
}

#[test]
fn base_type_test() {
  let content_type = ContentType::parse("application/atom+xml").unwrap();
  expect!(content_type.base_type()).to(be_equal_to(ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    .. ContentType::default()
  }));

  let content_type = ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  };
  expect!(content_type.base_type()).to(be_equal_to(ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  }));

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    attributes: btreemap! { "charset".to_string() => "UTF-8".to_string() },
    .. ContentType::default()
  };
  expect!(content_type.base_type()).to(be_equal_to(ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    .. ContentType::default()
  }));
}

#[test]
fn is_binary_test() {
  let content_type = ContentType::parse("application/atom+xml").unwrap();
  expect!(content_type.is_binary()).to(be_false());

  let content_type = ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_binary()).to(be_false());

  let content_type = ContentType {
    main_type: "image".into(),
    sub_type: "jpeg".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_binary()).to(be_true());
}
<commit_msg>feat: support text/xml as an XML content type<commit_after>//! Module for handling content types

use std::collections::BTreeMap;
use mime::Mime;
use log::*;
use maplit::btreemap;
use itertools::Itertools;
use lazy_static::*;
use std::str::FromStr;

#[cfg(test)]
use expectest::prelude::*;

/// Content type of a body
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContentType {
  /// Main content type
  pub main_type: String,
  /// Sub content type
  pub sub_type: String,
  /// Content type attributes
  pub attributes: BTreeMap<String, String>,
  /// Suffix
  pub suffix: Option<String>
}

lazy_static! {
  /// XML Content Type
  pub static ref XML: ContentType = ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    .. ContentType::default()
  };

  /// HTML Content Type
  pub static ref HTML: ContentType = ContentType {
    main_type: "text".into(),
    sub_type: "html".into(),
    .. ContentType::default()
  };

  /// JSON Content Type
  pub static ref JSON: ContentType = ContentType {
    main_type: "application".into(),
    sub_type: "json".into(),
    .. ContentType::default()
  };

  /// TEXT Content Type
  pub static ref TEXT: ContentType = ContentType {
    main_type: "text".into(),
    sub_type: "plain".into(),
    .. ContentType::default()
  };
}

impl ContentType {
  /// Parses a string into a ContentType
  pub fn parse<'a, S: Into<&'a str>>(content_type: S) -> Result<ContentType, String> {
    let content_type = content_type.into();
    match Mime::from_str(content_type) {
      Ok(mime) => {
        Ok(ContentType {
          main_type: mime.type_().to_string(),
          sub_type: mime.subtype().to_string(),
          attributes: mime.params().map(|(key, value)| (key.to_string(), value.to_string())).collect(),
          suffix: mime.suffix().map(|name| name.to_string())
        })
      },
      Err(err) => {
        let message = format!("Failed to parse '{}' as a content type: {}",
                              content_type, err);
        warn!("{}", message);
        Err(message)
      }
    }
  }

  /// If it is a JSON type
  pub fn is_json(&self) -> bool {
    self.main_type == "application" && (self.sub_type.starts_with("json") ||
      self.suffix.as_ref().unwrap_or(&String::default()) == "json")
  }

  /// If it is a XML type
  pub fn is_xml(&self) -> bool {
    (self.main_type == "application" || self.main_type == "text") && (self.sub_type == "xml" ||
      self.suffix.as_ref().unwrap_or(&String::default()) == "xml")
  }

  /// If it is a text type
  pub fn is_text(&self) -> bool {
    self.main_type == "text" || self.is_xml() || self.is_json()
  }

  /// If it is a known binary type
  pub fn is_binary(&self) -> bool {
    match self.main_type.as_str() {
      "audio" | "font" | "image" | "video" => true,
      "text" => false,
      _ => false
    }
  }

  /// Returns the base type with no attributes or suffix
  pub fn base_type(&self) -> ContentType {
    match self.suffix.as_ref() {
      Some(suffix) => ContentType {
        main_type: self.main_type.clone(),
        sub_type: suffix.clone(),
        .. ContentType::default()
      },
      None => ContentType {
        main_type: self.main_type.clone(),
        sub_type: self.sub_type.clone(),
        .. ContentType::default()
      }
    }
  }

  /// If the content type is the default type
  pub fn is_unknown(&self) -> bool {
    self.main_type == "*" || self.sub_type == "*"
  }

  /// Equals, ignoring attributes if not present on self
  pub fn is_equivalent_to(&self, other: &ContentType) -> bool {
    if self.attributes.is_empty() {
      self.main_type == other.main_type && self.sub_type == other.sub_type
    } else {
      self == other
    }
  }
}

impl Default for ContentType {
  fn default() -> Self {
    ContentType {
      main_type: "*".into(),
      sub_type: "*".into(),
      attributes: btreemap!{},
      suffix: None
    }
  }
}

impl std::fmt::Display for ContentType {
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    if self.attributes.is_empty() {
      write!(f, "{}/{}", self.main_type, self.sub_type)
    } else {
      write!(f, "{}/{};{}", self.main_type, self.sub_type, self.attributes.iter()
        .map(|(key, value)| format!("{}={}", key, value)).join(";"))
    }
  }
}

impl From<String> for ContentType {
  fn from(s: String) -> Self {
    ContentType::parse(s.as_str()).unwrap_or_default()
  }
}

impl From<&String> for ContentType {
  fn from(s: &String) -> Self {
    ContentType::parse(s.as_str()).unwrap_or_default()
  }
}

impl From<&str> for ContentType {
  fn from(s: &str) -> Self {
    ContentType::parse(s).unwrap_or_default()
  }
}

impl PartialEq<str> for ContentType {
  fn eq(&self, other: &str) -> bool {
    match ContentType::parse(other) {
      Ok(other) => *self == other,
      Err(_) => false
    }
  }
}

impl PartialEq<&str> for ContentType {
  fn eq(&self, other: &&str) -> bool {
    match ContentType::parse(*other) {
      Ok(other) => *self == other,
      Err(_) => false
    }
  }
}

#[test]
fn parse_test() {
  let content_type = &ContentType::parse("application/json").unwrap();
  expect!(&content_type.main_type).to(be_equal_to(&"application".to_string()));
  expect!(&content_type.sub_type).to(be_equal_to(&"json".to_string()));
  expect!(content_type.attributes.iter()).to(be_empty());
  expect!(content_type.clone().suffix).to(be_none());

  let content_type = &ContentType::parse("application/json;charset=UTF-16").unwrap();
  expect!(&content_type.main_type).to(be_equal_to(&"application".to_string()));
  expect!(&content_type.sub_type).to(be_equal_to(&"json".to_string()));
  expect!(content_type.clone().attributes).to(be_equal_to(btreemap!{
    "charset".to_string() => "utf-16".to_string()
  }));
  expect!(content_type.clone().suffix).to(be_none());

  let content_type = &ContentType::parse("application/hal+json; charset=UTF-8").unwrap();
  expect!(&content_type.main_type).to(be_equal_to(&"application".to_string()));
  expect!(&content_type.sub_type).to(be_equal_to(&"hal".to_string()));
  expect!(content_type.clone().attributes).to(be_equal_to(btreemap!{
    "charset".to_string() => "utf-8".to_string()
  }));
  expect!(content_type.clone().suffix).to(be_some().value("json".to_string()));
}

#[test]
fn to_string_test() {
  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "hal+json".into(),
    .. ContentType::default()
  };
  expect!(content_type.to_string()).to(be_equal_to("application/hal+json".to_string()));

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "hal+json".into(),
    attributes: btreemap! {
      "charset".to_string() => "UTF-32".to_string(),
      "b".to_string() => "c".to_string()
    },
    suffix: None
  };
  expect!(content_type.to_string()).to(be_equal_to("application/hal+json;b=c;charset=UTF-32".to_string()));
}

#[test]
fn is_json_test() {
  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "hal".into(),
    suffix: Some("json".to_string()),
    .. ContentType::default()
  };
  expect!(content_type.is_json()).to(be_true());

  let content_type = ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_json()).to(be_false());

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "json".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_json()).to(be_true());

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "json-rpc".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_json()).to(be_true());
}

#[test]
fn is_xml_test() {
  let content_type = ContentType::parse("application/atom+xml").unwrap();
  expect!(content_type.is_xml()).to(be_true());

  let content_type = ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_xml()).to(be_false());

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_xml()).to(be_true());

  let content_type = ContentType {
    main_type: "text".into(),
    sub_type: "xml".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_xml()).to(be_true());
}

#[test]
fn base_type_test() {
  let content_type = ContentType::parse("application/atom+xml").unwrap();
  expect!(content_type.base_type()).to(be_equal_to(ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    .. ContentType::default()
  }));

  let content_type = ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  };
  expect!(content_type.base_type()).to(be_equal_to(ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  }));

  let content_type = ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    attributes: btreemap! { "charset".to_string() => "UTF-8".to_string() },
    .. ContentType::default()
  };
  expect!(content_type.base_type()).to(be_equal_to(ContentType {
    main_type: "application".into(),
    sub_type: "xml".into(),
    .. ContentType::default()
  }));
}

#[test]
fn is_binary_test() {
  let content_type = ContentType::parse("application/atom+xml").unwrap();
  expect!(content_type.is_binary()).to(be_false());

  let content_type = ContentType {
    main_type: "text".into(),
    sub_type: "javascript".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_binary()).to(be_false());

  let content_type = ContentType {
    main_type: "image".into(),
    sub_type: "jpeg".into(),
    .. ContentType::default()
  };
  expect!(content_type.is_binary()).to(be_true());
}
<|endoftext|>


<commit_before>///
/// Represents the state of a job queue
///
#[derive(PartialEq, Debug, Clone, Copy)]
pub (super) enum QueueState {
    /// Queue is currently not running and not ready to run
    Idle,

    /// Queue has been queued up to run but isn't running yet
    Pending,

    /// Queue has been assigned to a thread and is currently running
    Running,

    /// A job on the queue has indicated that it's waiting to be re-awakened (by the scheduler)
    WaitingForWake,

    /// The queue is running synchronously on a thread and is waiting to be unparked
    WaitingForUnpark,

    /// We've returned from a polling operation and are waiting to be resumed
    WaitingForPoll,

    /// A wake-up call was made while the queue was in the running state
    AwokenWhileRunning,

    /// Queue received a panic and is no longer able to be scheduled
    Panicked
}

impl QueueState {
    ///
    /// Indicates if this queue is in the running state
    ///
    pub (crate) fn is_running(&self) -> bool {
        match self {
            QueueState::Running             |
            QueueState::AwokenWhileRunning  |
            QueueState::WaitingForUnpark    => true,
            _other                          => false
        }
    }
}
<commit_msg>`WaitingForPoll` is like `WaitingForUnpark` in that it indicates that while the queue is idle it's still 'running' as it can only be awoken safely by calling 'poll' on it again.<commit_after>///
/// Represents the state of a job queue
///
#[derive(PartialEq, Debug, Clone, Copy)]
pub (super) enum QueueState {
    /// Queue is currently not running and not ready to run
    ///
    /// The queue has this state when it has no jobs in it.
    Idle,

    /// Queue has been queued up to run but isn't running yet
    ///
    /// In general, this means the queue is waiting for a background thread to run on.
    Pending,

    /// Queue has been assigned to a thread and is currently running
    ///
    /// The queue must not be scheduled anywhere else while this is going on.
    Running,

    /// A job on the queue has indicated that it's waiting to be re-awakened (by the scheduler)
    ///
    /// This state is reached when a future for this queue generates a result and the queue moves
    /// into the pending state. It generally means it's scheduled to resume on a background
    /// thread (similar to Pending)
    WaitingForWake,

    /// The queue is running synchronously on a thread and is waiting to be unparked
    ///
    /// This occurs when the queue has been running on a thread and needs to wait for an event to
    /// occur on some other thread, usually the completion of a future. Queues in this state must
    /// only be resumed by the appropriate unparking method (the queue is effectively 'running'
    /// on the thread where it is parked)
    WaitingForUnpark,

    /// We've returned from a polling operation and are waiting to be resumed
    ///
    /// This happens when a queue has scheduled itself onto a thread as a result of calling the
    /// poll() method on a future and has returned pending. This queue should be resumed when the
    /// future is next awoken by calling 'poll' (it's effectively 'running' in the context of the
    /// call too poll)
    WaitingForPoll,

    /// A wake-up call was made while the queue was in the running state
    ///
    /// When attempting to suspend a queue as the result of a future indicating that it is pending,
    /// it is possible for the future to wake the queue before it has reached the suspended state.
    /// We capture this event by setting the queue's status to 'AwokenWhileRunning'.
    AwokenWhileRunning,

    /// Queue received a panic and is no longer able to be scheduled
    Panicked
}

impl QueueState {
    ///
    /// Indicates if this queue is in the running state
    ///
    pub (crate) fn is_running(&self) -> bool {
        match self {
            QueueState::Running             |
            QueueState::AwokenWhileRunning  |
            QueueState::WaitingForPoll      |
            QueueState::WaitingForUnpark    => true,
            _other                          => false
        }
    }
}
<|endoftext|>


<commit_before>// compile-flags:-g

// min-gdb-version: 8.1

// === GDB TESTS ==================================================================================

// gdb-command:run

// gdb-command:print r
// gdb-check:[...]$1 = Rc(strong=2, weak=1) = {value = 42, strong = 2, weak = 1}
// gdb-command:print a
// gdb-check:[...]$2 = Arc(strong=2, weak=1) = {value = 42, strong = 2, weak = 1}

// === LLDB TESTS ==================================================================================

// lldb-command:run

// lldb-command:print r
// lldb-check:[...]$0 = strong=2, weak=1 { value = 42 }
// lldb-command:print a
// lldb-check:[...]$1 = strong=2, weak=1 { data = 42 }

use std::rc::Rc;
use std::sync::Arc;

fn main() {
    let r = Rc::new(42);
    let r1 = Rc::clone(&r);
    let w1 = Rc::downgrade(&r);

    let a = Arc::new(42);
    let a1 = Arc::clone(&a);
    let w2 = Arc::downgrade(&a);

    print!(""); // #break
}
<commit_msg>tests: Ignore src/test/debuginfo/rc_arc.rs on Windows<commit_after>// ignore-windows pretty-printers are not loaded
// compile-flags:-g

// min-gdb-version: 8.1

// === GDB TESTS ==================================================================================

// gdb-command:run

// gdb-command:print r
// gdb-check:[...]$1 = Rc(strong=2, weak=1) = {value = 42, strong = 2, weak = 1}
// gdb-command:print a
// gdb-check:[...]$2 = Arc(strong=2, weak=1) = {value = 42, strong = 2, weak = 1}

// === LLDB TESTS ==================================================================================

// lldb-command:run

// lldb-command:print r
// lldb-check:[...]$0 = strong=2, weak=1 { value = 42 }
// lldb-command:print a
// lldb-check:[...]$1 = strong=2, weak=1 { data = 42 }

use std::rc::Rc;
use std::sync::Arc;

fn main() {
    let r = Rc::new(42);
    let r1 = Rc::clone(&r);
    let w1 = Rc::downgrade(&r);

    let a = Arc::new(42);
    let a1 = Arc::clone(&a);
    let w2 = Arc::downgrade(&a);

    print!(""); // #break
}
<|endoftext|>


<commit_before>use std::rc::Rc;

/// upside-down tree with a designated position (the *stack pointer*) and *nodes* of type `a`.
#[derive(Debug, Clone)]
pub struct TreeStack<A> {
    parent: Option<(usize, Rc<TreeStack<A>>)>,
    value: A,
    children: Vec<Option<Rc<TreeStack<A>>>>,
}

impl<A> TreeStack<A> {
    /// Creates a new `TreeStack<A>` with root label `a`.
    pub fn new(a: A) -> Self {
        TreeStack { value: a, children: Vec::new(), parent: None }
    }

    /// Applies a function `Fn(&A) -> B`to every node in a `TreeStack<A>`.
    pub fn map<B>(&self, f: &Fn(&A) -> B) -> TreeStack<B> {
        let new_parent = match self.parent {
            Some((i, ref p)) => Some((i, Rc::new(p.map(f)))),
            None => None,
        };
        let new_children = self.children.iter().map(|o| o.clone().map(|v| Rc::new(v.map(f)))).collect();
        TreeStack { parent: new_parent, value: f(&self.value), children: new_children }
    }

    /// Applies a function `FnMut(&A) -> B`to every node in a `TreeStack<A>`.
    pub fn map_mut<B: Clone>(&self, f: &mut FnMut(&A) -> B) -> TreeStack<B> {
        let new_parent = match self.parent {
            Some((i, ref p)) => Some((i, Rc::new(p.map_mut(f)))),
            None => None,
        };
        let new_children = self.children.iter().map(|o| o.clone().map(|v| Rc::new(v.map_mut(f)))).collect();
        TreeStack { parent: new_parent, value: f(&self.value), children: new_children }
    }

    /// Returns `True` if the stack pointer points to the bottom node.
    pub fn is_at_bottom(&self) -> bool {
        self.parent.is_none()
    }

    /// Returns a reference to label of the current node.
    pub fn current_symbol(&self) -> &A {
        &self.value
    }

    /// Replaces the current value by the given value.
    pub fn set(mut self, a: A) -> Self {
        self.value = a;
        self
    }

    /// Writes a value to the specified child position (if the child position is vacant) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn push(mut self, n: usize, a: A) -> Result<Self, Self> {
        if {
            let filler = &mut vec![None; n - self.children.len() + 1];
            self.children.append(filler);

            self.children[n].is_none()
        } {
            Ok(TreeStack { value: a,
                           children: Vec::new(),
                           parent: Some((n, Rc::new(self))) })
        } else {
            Err(self)
        }
    }
}

impl<A: Clone> TreeStack<A> {
    /// Goes up to a specific child position (if this position is occupied) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn up(mut self, n: usize) -> Result<Self, Self> {
        match {
            if self.children.len() > n {
                self.children.push(None);
                self.children.swap_remove(n)
            } else {
                None
            }
        } {
            Some(ref tn) => Ok(TreeStack { value: tn.value.clone(),
                                           children: tn.children.clone(),
                                           parent: Some((n, Rc::new(self))) }),
            _ => Err(self),
        }
    }

    /// Goes down to the parent position (if there is one) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn down(mut self) -> Result<Self, Self> {
        match self.parent.take() {
            Some((n, pn)) => {
                let mut new_pch = pn.children.clone();
                new_pch[n] = Some(Rc::new(self));
                Ok(TreeStack { value: pn.value.clone(),
                               children: new_pch,
                               parent: pn.parent.clone() })
            },
            None => Err(self),
        }
    }
}

impl<A: PartialEq> PartialEq for TreeStack<A> {
    fn eq(&self, other: &Self) -> bool {
        let comp = |p1, p2| Rc::ptr_eq(p1, p2) || p1 == p2;
        self.value == other.value
            && match (&self.parent, &other.parent) {
                (&Some((i1, ref p1)), &Some((i2, ref p2))) => i1 == i2 && comp(p1, p2),
                (&None, &None)                             => true,
                _                                          => false,
            }
            && self.children == other.children
    }
}

impl<A: Eq> Eq for TreeStack<A> {}

#[test]
fn test_tree_stack() {
    let mut ts: TreeStack<u8> = TreeStack::new(0);
    assert_eq!(&0, ts.current_symbol());

    ts = ts.push(1, 1).unwrap().clone();
    assert_eq!(&1, ts.current_symbol());

    ts = ts.down().unwrap().clone();
    assert_eq!(&0, ts.current_symbol());

    ts = ts.push(2, 2).unwrap().clone();
    assert_eq!(&2, ts.current_symbol());

    ts = ts.down().unwrap().clone();
    ts = ts.up(1).unwrap().clone();
    assert_eq!(&1, ts.current_symbol());

    let ts1 = ts.clone().push(1, 11);
    let ts2 = ts.push(1, 11);
    assert_eq!(ts1, ts2);
}
<commit_msg>src/tree_stack/tree_stack.rs::push: fixed capacity overflow caused by integer underflow<commit_after>use std::rc::Rc;

/// upside-down tree with a designated position (the *stack pointer*) and *nodes* of type `a`.
#[derive(Debug, Clone)]
pub struct TreeStack<A> {
    parent: Option<(usize, Rc<TreeStack<A>>)>,
    value: A,
    children: Vec<Option<Rc<TreeStack<A>>>>,
}

impl<A> TreeStack<A> {
    /// Creates a new `TreeStack<A>` with root label `a`.
    pub fn new(a: A) -> Self {
        TreeStack { value: a, children: Vec::new(), parent: None }
    }

    /// Applies a function `Fn(&A) -> B`to every node in a `TreeStack<A>`.
    pub fn map<B>(&self, f: &Fn(&A) -> B) -> TreeStack<B> {
        let new_parent = match self.parent {
            Some((i, ref p)) => Some((i, Rc::new(p.map(f)))),
            None => None,
        };
        let new_children = self.children.iter().map(|o| o.clone().map(|v| Rc::new(v.map(f)))).collect();
        TreeStack { parent: new_parent, value: f(&self.value), children: new_children }
    }

    /// Applies a function `FnMut(&A) -> B`to every node in a `TreeStack<A>`.
    pub fn map_mut<B: Clone>(&self, f: &mut FnMut(&A) -> B) -> TreeStack<B> {
        let new_parent = match self.parent {
            Some((i, ref p)) => Some((i, Rc::new(p.map_mut(f)))),
            None => None,
        };
        let new_children = self.children.iter().map(|o| o.clone().map(|v| Rc::new(v.map_mut(f)))).collect();
        TreeStack { parent: new_parent, value: f(&self.value), children: new_children }
    }

    /// Returns `True` if the stack pointer points to the bottom node.
    pub fn is_at_bottom(&self) -> bool {
        self.parent.is_none()
    }

    /// Returns a reference to label of the current node.
    pub fn current_symbol(&self) -> &A {
        &self.value
    }

    /// Replaces the current value by the given value.
    pub fn set(mut self, a: A) -> Self {
        self.value = a;
        self
    }

    /// Writes a value to the specified child position (if the child position is vacant) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn push(mut self, n: usize, a: A) -> Result<Self, Self> {
        if {
            if n >= self.children.len() {
                let len = n - self.children.len() + 1;
                let filler = &mut vec![None; len];
                self.children.append(filler);
            }

            self.children[n].is_none()
        } {
            Ok(TreeStack { value: a,
                           children: Vec::new(),
                           parent: Some((n, Rc::new(self))) })
        } else {
            Err(self)
        }
    }
}

impl<A: Clone> TreeStack<A> {
    /// Goes up to a specific child position (if this position is occupied) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn up(mut self, n: usize) -> Result<Self, Self> {
        match {
            if self.children.len() > n {
                self.children.push(None);
                self.children.swap_remove(n)
            } else {
                None
            }
        } {
            Some(ref tn) => Ok(TreeStack { value: tn.value.clone(),
                                           children: tn.children.clone(),
                                           parent: Some((n, Rc::new(self))) }),
            _ => Err(self),
        }
    }

    /// Goes down to the parent position (if there is one) and returns the resulting `TreeStack` in an `Ok`.
    /// Returns the unmodified `TreeStack` in an `Err` otherwise.
    pub fn down(mut self) -> Result<Self, Self> {
        match self.parent.take() {
            Some((n, pn)) => {
                let mut new_pch = pn.children.clone();
                new_pch[n] = Some(Rc::new(self));
                Ok(TreeStack { value: pn.value.clone(),
                               children: new_pch,
                               parent: pn.parent.clone() })
            },
            None => Err(self),
        }
    }
}

impl<A: PartialEq> PartialEq for TreeStack<A> {
    fn eq(&self, other: &Self) -> bool {
        let comp = |p1, p2| Rc::ptr_eq(p1, p2) || p1 == p2;
        self.value == other.value
            && match (&self.parent, &other.parent) {
                (&Some((i1, ref p1)), &Some((i2, ref p2))) => i1 == i2 && comp(p1, p2),
                (&None, &None)                             => true,
                _                                          => false,
            }
            && self.children == other.children
    }
}

impl<A: Eq> Eq for TreeStack<A> {}

#[test]
fn test_tree_stack() {
    let mut ts: TreeStack<u8> = TreeStack::new(0);
    assert_eq!(&0, ts.current_symbol());

    ts = ts.push(1, 1).unwrap().clone();
    assert_eq!(&1, ts.current_symbol());

    ts = ts.down().unwrap().clone();
    assert_eq!(&0, ts.current_symbol());

    ts = ts.push(2, 2).unwrap().clone();
    assert_eq!(&2, ts.current_symbol());

    ts = ts.down().unwrap().clone();
    ts = ts.up(1).unwrap().clone();
    assert_eq!(&1, ts.current_symbol());

    let ts1 = ts.clone().push(1, 11);
    let ts2 = ts.push(1, 11);
    assert_eq!(ts1, ts2);
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "cargo-clippy", allow(unused_parens))]

extern crate serde;
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde-json")]
extern crate serde_json;
extern crate toml;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};

mod error;
mod escaping;

pub use error::{Error, Result};
pub use escaping::MarkupDisplay;
pub mod filters;

pub struct Config {
    pub dirs: Vec<PathBuf>,
}

impl Config {
    pub fn new() -> Config {
        let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let filename = root.join(CONFIG_FILE_NAME);

        let default = vec![root.join("templates")];
        let dirs = if filename.exists() {
            let config_str = fs::read_to_string(&filename)
                .expect(&format!("unable to read {}", filename.to_str().unwrap()));
            let raw: RawConfig = toml::from_str(&config_str)
                .expect(&format!("invalid TOML in {}", filename.to_str().unwrap()));
            raw.dirs
                .map(|dirs| dirs.into_iter().map(|dir| root.join(dir)).collect())
                .unwrap_or_else(|| default)
        } else {
            default
        };

        Config { dirs }
    }

    pub fn find_template(&self, path: &str, start_at: Option<&Path>) -> PathBuf {
        if let Some(root) = start_at {
            let relative = root.with_file_name(path);
            if relative.exists() {
                return relative.to_owned();
            }
        }

        for dir in &self.dirs {
            let rooted = dir.join(path);
            if rooted.exists() {
                return rooted;
            }
        }

        panic!(
            "template {:?} not found in directories {:?}",
            path, self.dirs
        )
    }
}

#[derive(Deserialize)]
struct RawConfig {
    dirs: Option<Vec<String>>,
}

static CONFIG_FILE_NAME: &str = "askama.toml";

#[cfg(test)]
mod tests {
    use super::Config;
    use std::env;
    use std::path::{Path, PathBuf};

    fn assert_eq_rooted(actual: &Path, expected: &str) {
        let mut root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        root.push("templates");
        let mut inner = PathBuf::new();
        inner.push(expected);
        assert_eq!(actual.strip_prefix(root).unwrap(), inner);
    }

    #[test]
    fn find_absolute() {
        let config = Config::new();
        let root = config.find_template("a.html", None);
        let path = config.find_template("sub/b.html", Some(&root));
        assert_eq_rooted(&path, "sub/b.html");
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        let config = Config::new();
        let root = config.find_template("a.html", None);
        config.find_template("b.html", Some(&root));
    }

    #[test]
    fn find_relative() {
        let config = Config::new();
        let root = config.find_template("sub/b.html", None);
        let path = config.find_template("c.html", Some(&root));
        assert_eq_rooted(&path, "sub/c.html");
    }

    #[test]
    fn find_relative_sub() {
        let config = Config::new();
        let root = config.find_template("sub/b.html", None);
        let path = config.find_template("sub1/d.html", Some(&root));
        assert_eq_rooted(&path, "sub/sub1/d.html");
    }
}
<commit_msg>Enable instantiation of Config from str configuration<commit_after>#![cfg_attr(feature = "cargo-clippy", allow(unused_parens))]

extern crate serde;
#[macro_use]
extern crate serde_derive;
#[cfg(feature = "serde-json")]
extern crate serde_json;
extern crate toml;

use std::env;
use std::fs;
use std::path::{Path, PathBuf};

mod error;
mod escaping;

pub use error::{Error, Result};
pub use escaping::MarkupDisplay;
pub mod filters;

pub struct Config {
    pub dirs: Vec<PathBuf>,
}

impl Config {
    pub fn new() -> Config {
        let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let filename = root.join(CONFIG_FILE_NAME);
        if filename.exists() {
            Self::from_str(&fs::read_to_string(&filename)
                .expect(&format!("unable to read {}", filename.to_str().unwrap())))
        } else {
            Self::from_str("")
        }
    }

    pub fn from_str(s: &str) -> Self {
        let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let default = vec![root.join("templates")];
        let raw: RawConfig =
            toml::from_str(&s).expect(&format!("invalid TOML in {}", CONFIG_FILE_NAME));
        let dirs = raw.dirs
            .map(|dirs| dirs.into_iter().map(|dir| root.join(dir)).collect())
            .unwrap_or_else(|| default);
        Self { dirs }
    }

    pub fn find_template(&self, path: &str, start_at: Option<&Path>) -> PathBuf {
        if let Some(root) = start_at {
            let relative = root.with_file_name(path);
            if relative.exists() {
                return relative.to_owned();
            }
        }

        for dir in &self.dirs {
            let rooted = dir.join(path);
            if rooted.exists() {
                return rooted;
            }
        }

        panic!(
            "template {:?} not found in directories {:?}",
            path, self.dirs
        )
    }
}

#[derive(Deserialize)]
struct RawConfig {
    dirs: Option<Vec<String>>,
}

static CONFIG_FILE_NAME: &str = "askama.toml";

#[cfg(test)]
mod tests {
    use super::Config;
    use std::env;
    use std::path::{Path, PathBuf};

    fn assert_eq_rooted(actual: &Path, expected: &str) {
        let mut root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        root.push("templates");
        let mut inner = PathBuf::new();
        inner.push(expected);
        assert_eq!(actual.strip_prefix(root).unwrap(), inner);
    }

    #[test]
    fn find_absolute() {
        let config = Config::new();
        let root = config.find_template("a.html", None);
        let path = config.find_template("sub/b.html", Some(&root));
        assert_eq_rooted(&path, "sub/b.html");
    }

    #[test]
    #[should_panic]
    fn find_relative_nonexistent() {
        let config = Config::new();
        let root = config.find_template("a.html", None);
        config.find_template("b.html", Some(&root));
    }

    #[test]
    fn find_relative() {
        let config = Config::new();
        let root = config.find_template("sub/b.html", None);
        let path = config.find_template("c.html", Some(&root));
        assert_eq_rooted(&path, "sub/c.html");
    }

    #[test]
    fn find_relative_sub() {
        let config = Config::new();
        let root = config.find_template("sub/b.html", None);
        let path = config.find_template("sub1/d.html", Some(&root));
        assert_eq_rooted(&path, "sub/sub1/d.html");
    }
}
<|endoftext|>