<commit_before>extern crate coinched;
extern crate libcoinche;
extern crate hyper;
extern crate rustc_serialize;
extern crate url;

use std::io;
use std::io::{BufRead, Write};
use std::str::FromStr;
use rustc_serialize::Decodable;
use rustc_serialize::json;
use hyper::client::IntoUrl;
use std::convert::From;
use std::io::Read;
use libcoinche::pos::PlayerPos;
use hyper::header::{Header, ContentType};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

// It used to include a re-usable hyper::Client,
// but it would lead to failed request if too
// long happened between two queries.
struct Client {
    player_id: u32,
    pos: PlayerPos,

    event_id: usize,

    host: String,
}

fn from_reader<R: Read, T: Decodable>(r: &mut R) -> Result<T, Error> {
    let json = try!(json::Json::from_reader(r));
    println!("Json: {:?}", json);
    let mut decoder = json::Decoder::new(json);
    let result = try!(Decodable::decode(&mut decoder));
    Ok(result)
}

impl Drop for Client {
    fn drop(&mut self) {
        println!("LEAVING");
        let leave_url = format!("http://{}/leave/{}", self.host, self.player_id);
        hyper::Client::new().post(&leave_url).send().unwrap();
    }
}

impl Client {
    fn new(host: &str, player_id: u32, pos: PlayerPos) -> Self {
        // Build the URL here

        Client {
            player_id: player_id,
            pos: pos,
            event_id: 0,
            host: host.to_string(),
        }
    }

    fn join(host: &str) -> Result<Self, Error> {
        let client = hyper::Client::new();

        let join_url = try!(format!("http://{}/join", host).into_url());
        let mut response = try!(client.post(join_url).send());
        let party: coinched::NewPartyInfo = try!(from_reader(&mut response));

        Ok(Client::new(host, party.player_id, party.player_pos))
    }

    fn read_event<R: io::Read>(&mut self, r: &mut R) -> Result<coinched::EventType, Error> {
        let event: coinched::Event = try!(from_reader(r));

        self.event_id = event.id + 1;

        Ok(event.event)
    }

    fn wait(&mut self) -> Result<coinched::EventType, Error> {
        let wait_url = format!("http://{}/wait/{}/{}",
                               &self.host,
                               self.player_id,
                               self.event_id);
        let mut response = try!(hyper::Client::new().get(&wait_url).send());
        self.read_event(&mut response)
    }

    fn bid(&mut self, contract: coinched::ContractBody) -> Result<coinched::EventType, Error> {
        let bid_url = format!("http://{}/bid/{}", self.host, self.player_id);
        let body = json::encode(&contract).unwrap();
        let mut response = try!(hyper::Client::new()
                                    .post(&bid_url)
                                    .header(ContentType(Mime(TopLevel::Application,
                                                             SubLevel::Json,
                                                             vec![(Attr::Charset, Value::Utf8)])))
                                    .body(&body)
                                    .send());
        self.read_event(&mut response)
    }

    fn pass(&mut self) -> Result<coinched::EventType, Error> {
        let pass_url = format!("http://{}/pass/{}", self.host, self.player_id);
        let mut response = try!(hyper::Client::new().post(&pass_url).send());
        self.read_event(&mut response)
    }

    fn coinche(&mut self) -> Result<coinched::EventType, Error> {
        let coinche_url = format!("http://{}/coinche/{}", self.host, self.player_id);
        let mut response = try!(hyper::Client::new().post(&coinche_url).send());
        self.read_event(&mut response)
    }
}

#[derive(Debug)]
enum Error {
    Url(url::ParseError),
    Hyper(hyper::Error),
    Json(json::DecoderError),
}

impl From<url::ParseError> for Error {
    fn from(err: url::ParseError) -> Self {
        Error::Url(err)
    }
}

impl From<hyper::Error> for Error {
    fn from(err: hyper::Error) -> Self {
        Error::Hyper(err)
    }
}

impl From<json::ParserError> for Error {
    fn from(err: json::ParserError) -> Self {
        Error::Json(json::DecoderError::ParseError(err))
    }
}

impl From<json::DecoderError> for Error {
    fn from(err: json::DecoderError) -> Self {
        Error::Json(err)
    }
}

fn play_game<F: FnMut() -> String>(client: &mut Client,
                                   first: PlayerPos,
                                   hand: libcoinche::cards::Hand,
                                   mut input: F)
                                   -> [i32; 2] {
    print!("Cards:\n[");
    for card in hand.list() {
        print!(" {}", card.to_string());
    }
    println!(" ]");

    let mut next = first;

    loop {
        let event = if next == client.pos {
            // Our turn
            print!("Bid? Write `pass` or `[80, 90, ... , Capot] [H,C,D,S]` or `coinche`.\n> ");
            io::stdout().flush().unwrap();
            let line = input();
            if &line == "pass" {
                client.pass()
            } else if &line == "coinche" {
                client.coinche()
            } else {
                let tokens: Vec<&str> = line.trim().split(" ").collect();
                if tokens.len() != 2 {
                    println!("Invalid number of tokens");
                    continue;
                }
                let target = match libcoinche::bid::Target::from_str(tokens[0]) {
                    Ok(target) => target,
                    _ => {
                        println!("Invalid target");
                        continue;
                    }
                };
                let suit = match libcoinche::cards::Suit::from_str(tokens[1]) {
                    Ok(suit) => suit,
                    Err(err) => {
                        println!("Invalid suit: {}", err);
                        continue;
                    }
                };

                let contract = coinched::ContractBody {
                    target: target,
                    suit: suit,
                };
                client.bid(contract)
            }
        } else {
            client.wait()
        };
        match event {
            Ok(coinched::EventType::FromPlayer(pos, e)) => {
                match e {
                    coinched::PlayerEvent::Bidded(trump, target) => {
                        println!("Player {:?} bidded {:?} on {:?}", pos, target, trump);
                        next = next.next();
                    }
                    coinched::PlayerEvent::Passed | coinched::PlayerEvent::Coinched => {
                        next = next.next()
                    }
                    _ => (),
                }
            }
            Ok(coinched::EventType::PartyCancelled(msg)) => panic!("party cancelled: {}", msg),
            Ok(_) => (),
            Err(err) => println!("Error: {:?}", err),
        }
    }
}

fn main() {
    // TODO: read this from arguments
    let host = "localhost:3000";

    // TODO: allow reconnecting to an existing game

    let mut client = Client::join(host).unwrap();
    let mut score: [i32; 2] = [0, 0];
    let mut stdin_reader = io::BufReader::new(io::stdin());
    let mut read_line = || {
        let mut buffer = String::new();
        stdin_reader.read_line(&mut buffer).unwrap();
        buffer.pop().unwrap();
        buffer
    };

    loop {
        let event = client.wait().unwrap();
        match event {
            coinched::EventType::NewGameRelative{first, hand} => {
                let new_scores = play_game(&mut client, first, hand, &mut read_line);
                score[0] += new_scores[0];
                score[1] += new_scores[1];
            }
            _ => panic!("Unexpected event: {:?}", event),
        }
    }

    // Then: card play
}
<commit_msg>Remove unused import<commit_after>extern crate coinched;
extern crate libcoinche;
extern crate hyper;
extern crate rustc_serialize;
extern crate url;

use std::io;
use std::io::{BufRead, Write};
use std::str::FromStr;
use rustc_serialize::Decodable;
use rustc_serialize::json;
use hyper::client::IntoUrl;
use std::convert::From;
use std::io::Read;
use libcoinche::pos::PlayerPos;
use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};

// It used to include a re-usable hyper::Client,
// but it would lead to failed request if too
// long happened between two queries.
struct Client {
    player_id: u32,
    pos: PlayerPos,

    event_id: usize,

    host: String,
}

fn from_reader<R: Read, T: Decodable>(r: &mut R) -> Result<T, Error> {
    let json = try!(json::Json::from_reader(r));
    println!("Json: {:?}", json);
    let mut decoder = json::Decoder::new(json);
    let result = try!(Decodable::decode(&mut decoder));
    Ok(result)
}

impl Drop for Client {
    fn drop(&mut self) {
        println!("LEAVING");
        let leave_url = format!("http://{}/leave/{}", self.host, self.player_id);
        hyper::Client::new().post(&leave_url).send().unwrap();
    }
}

impl Client {
    fn new(host: &str, player_id: u32, pos: PlayerPos) -> Self {
        // Build the URL here

        Client {
            player_id: player_id,
            pos: pos,
            event_id: 0,
            host: host.to_string(),
        }
    }

    fn join(host: &str) -> Result<Self, Error> {
        let client = hyper::Client::new();

        let join_url = try!(format!("http://{}/join", host).into_url());
        let mut response = try!(client.post(join_url).send());
        let party: coinched::NewPartyInfo = try!(from_reader(&mut response));

        Ok(Client::new(host, party.player_id, party.player_pos))
    }

    fn read_event<R: io::Read>(&mut self, r: &mut R) -> Result<coinched::EventType, Error> {
        let event: coinched::Event = try!(from_reader(r));

        self.event_id = event.id + 1;

        Ok(event.event)
    }

    fn wait(&mut self) -> Result<coinched::EventType, Error> {
        let wait_url = format!("http://{}/wait/{}/{}",
                               &self.host,
                               self.player_id,
                               self.event_id);
        let mut response = try!(hyper::Client::new().get(&wait_url).send());
        self.read_event(&mut response)
    }

    fn bid(&mut self, contract: coinched::ContractBody) -> Result<coinched::EventType, Error> {
        let bid_url = format!("http://{}/bid/{}", self.host, self.player_id);
        let body = json::encode(&contract).unwrap();
        let mut response = try!(hyper::Client::new()
                                    .post(&bid_url)
                                    .header(ContentType(Mime(TopLevel::Application,
                                                             SubLevel::Json,
                                                             vec![(Attr::Charset, Value::Utf8)])))
                                    .body(&body)
                                    .send());
        self.read_event(&mut response)
    }

    fn pass(&mut self) -> Result<coinched::EventType, Error> {
        let pass_url = format!("http://{}/pass/{}", self.host, self.player_id);
        let mut response = try!(hyper::Client::new().post(&pass_url).send());
        self.read_event(&mut response)
    }

    fn coinche(&mut self) -> Result<coinched::EventType, Error> {
        let coinche_url = format!("http://{}/coinche/{}", self.host, self.player_id);
        let mut response = try!(hyper::Client::new().post(&coinche_url).send());
        self.read_event(&mut response)
    }
}

#[derive(Debug)]
enum Error {
    Url(url::ParseError),
    Hyper(hyper::Error),
    Json(json::DecoderError),
}

impl From<url::ParseError> for Error {
    fn from(err: url::ParseError) -> Self {
        Error::Url(err)
    }
}

impl From<hyper::Error> for Error {
    fn from(err: hyper::Error) -> Self {
        Error::Hyper(err)
    }
}

impl From<json::ParserError> for Error {
    fn from(err: json::ParserError) -> Self {
        Error::Json(json::DecoderError::ParseError(err))
    }
}

impl From<json::DecoderError> for Error {
    fn from(err: json::DecoderError) -> Self {
        Error::Json(err)
    }
}

fn play_game<F: FnMut() -> String>(client: &mut Client,
                                   first: PlayerPos,
                                   hand: libcoinche::cards::Hand,
                                   mut input: F)
                                   -> [i32; 2] {
    print!("Cards:\n[");
    for card in hand.list() {
        print!(" {}", card.to_string());
    }
    println!(" ]");

    let mut next = first;

    loop {
        let event = if next == client.pos {
            // Our turn
            print!("Bid? Write `pass` or `[80, 90, ... , Capot] [H,C,D,S]` or `coinche`.\n> ");
            io::stdout().flush().unwrap();
            let line = input();
            if &line == "pass" {
                client.pass()
            } else if &line == "coinche" {
                client.coinche()
            } else {
                let tokens: Vec<&str> = line.trim().split(" ").collect();
                if tokens.len() != 2 {
                    println!("Invalid number of tokens");
                    continue;
                }
                let target = match libcoinche::bid::Target::from_str(tokens[0]) {
                    Ok(target) => target,
                    _ => {
                        println!("Invalid target");
                        continue;
                    }
                };
                let suit = match libcoinche::cards::Suit::from_str(tokens[1]) {
                    Ok(suit) => suit,
                    Err(err) => {
                        println!("Invalid suit: {}", err);
                        continue;
                    }
                };

                let contract = coinched::ContractBody {
                    target: target,
                    suit: suit,
                };
                client.bid(contract)
            }
        } else {
            client.wait()
        };
        match event {
            Ok(coinched::EventType::FromPlayer(pos, e)) => {
                match e {
                    coinched::PlayerEvent::Bidded(trump, target) => {
                        println!("Player {:?} bidded {:?} on {:?}", pos, target, trump);
                        next = next.next();
                    }
                    coinched::PlayerEvent::Passed | coinched::PlayerEvent::Coinched => {
                        next = next.next()
                    }
                    _ => (),
                }
            }
            Ok(coinched::EventType::PartyCancelled(msg)) => panic!("party cancelled: {}", msg),
            Ok(_) => (),
            Err(err) => println!("Error: {:?}", err),
        }
    }
}

fn main() {
    // TODO: read this from arguments
    let host = "localhost:3000";

    // TODO: allow reconnecting to an existing game

    let mut client = Client::join(host).unwrap();
    let mut score: [i32; 2] = [0, 0];
    let mut stdin_reader = io::BufReader::new(io::stdin());
    let mut read_line = || {
        let mut buffer = String::new();
        stdin_reader.read_line(&mut buffer).unwrap();
        buffer.pop().unwrap();
        buffer
    };

    loop {
        let event = client.wait().unwrap();
        match event {
            coinched::EventType::NewGameRelative{first, hand} => {
                let new_scores = play_game(&mut client, first, hand, &mut read_line);
                score[0] += new_scores[0];
                score[1] += new_scores[1];
            }
            _ => panic!("Unexpected event: {:?}", event),
        }
    }

    // Then: card play
}
<|endoftext|>


<commit_before>use std::collections::VecDeque;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::thread;

use parking_lot::Mutex;

use select::CaseId;
use select::handle::{self, Handle};

/// A selection case, identified by a `Handle` and a `CaseId`.
///
/// Note that multiple threads could be operating on a single channel end, as well as a single
/// thread on multiple different channel ends.
struct Case {
    /// A handle associated with the thread owning this case.
    handle: Handle,

    /// The case ID.
    case_id: CaseId,
}

/// A simple wait queue for list-based and array-based channels.
///
/// This data structure is used sfor registeroing selection cases before blocking and waking them
/// up when the channel receives a message, sends one, or becomes disconnected.
pub struct Monitor {
    /// The list of registered selection cases.
    cases: Mutex<VecDeque<Case>>,

    /// Number of cases in the list.
    len: AtomicUsize,
}

impl Monitor {
    /// Creates a new `Monitor`.
    #[inline]
    pub fn new() -> Self {
        Monitor {
            cases: Mutex::new(VecDeque::new()),
            len: AtomicUsize::new(0),
        }
    }

    /// Registers the current thread with `case_id`.
    pub fn register(&self, case_id: CaseId) {
        let mut cases = self.cases.lock();
        cases.push_back(Case {
            handle: handle::current(),
            case_id,
        });
        self.len.store(cases.len(), SeqCst);
    }

    /// Unregisters the current thread with `case_id`.
    pub fn unregister(&self, case_id: CaseId) {
        let thread_id = thread::current().id();
        let mut cases = self.cases.lock();

        if let Some((i, _)) = cases.iter().enumerate().find(|&(_, case)| {
            case.case_id == case_id && case.handle.thread_id() == thread_id
        }) {
            cases.remove(i);
            self.len.store(cases.len(), SeqCst);
            Self::maybe_shrink(&mut cases);
        }
    }

    /// Attempts to fire one case which is owned by another thread.
    pub fn notify_one(&self) {
        if self.len.load(SeqCst) > 0 {
            let thread_id = thread::current().id();
            let mut cases = self.cases.lock();

            for i in 0..cases.len() {
                if cases[i].handle.thread_id() != thread_id {
                    if cases[i].handle.try_select(cases[i].case_id) {
                        let case = cases.remove(i).unwrap();
                        self.len.store(cases.len(), SeqCst);
                        Self::maybe_shrink(&mut cases);

                        case.handle.unpark();
                        break;
                    }
                }
            }
        }
    }

    /// Aborts all currently registered selection cases.
    pub fn abort_all(&self) {
        if self.len.load(SeqCst) > 0 {
            let mut cases = self.cases.lock();

            self.len.store(0, SeqCst);
            for case in cases.drain(..) {
                if case.handle.try_select(CaseId::abort()) {
                    case.handle.unpark();
                }
            }

            Self::maybe_shrink(&mut cases);
        }
    }

    /// Shrinks the internal deque if it's capacity is much larger than length.
    fn maybe_shrink(cases: &mut VecDeque<Case>) {
        if cases.capacity() > 32 && cases.len() < cases.capacity() / 4 {
            let mut v = VecDeque::with_capacity(cases.capacity() / 2);
            v.extend(cases.drain(..));
            *cases = v;
        }
    }
}

impl Drop for Monitor {
    fn drop(&mut self) {
        debug_assert!(self.cases.lock().is_empty());
        debug_assert_eq!(self.len.load(SeqCst), 0);
    }
}
<commit_msg>Fix a typo<commit_after>use std::collections::VecDeque;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering::SeqCst;
use std::thread;

use parking_lot::Mutex;

use select::CaseId;
use select::handle::{self, Handle};

/// A selection case, identified by a `Handle` and a `CaseId`.
///
/// Note that multiple threads could be operating on a single channel end, as well as a single
/// thread on multiple different channel ends.
struct Case {
    /// A handle associated with the thread owning this case.
    handle: Handle,

    /// The case ID.
    case_id: CaseId,
}

/// A simple wait queue for list-based and array-based channels.
///
/// This data structure is used for registering selection cases before blocking and waking them
/// up when the channel receives a message, sends one, or gets closed.
pub struct Monitor {
    /// The list of registered selection cases.
    cases: Mutex<VecDeque<Case>>,

    /// Number of cases in the list.
    len: AtomicUsize,
}

impl Monitor {
    /// Creates a new `Monitor`.
    #[inline]
    pub fn new() -> Self {
        Monitor {
            cases: Mutex::new(VecDeque::new()),
            len: AtomicUsize::new(0),
        }
    }

    /// Registers the current thread with `case_id`.
    pub fn register(&self, case_id: CaseId) {
        let mut cases = self.cases.lock();
        cases.push_back(Case {
            handle: handle::current(),
            case_id,
        });
        self.len.store(cases.len(), SeqCst);
    }

    /// Unregisters the current thread with `case_id`.
    pub fn unregister(&self, case_id: CaseId) {
        let thread_id = thread::current().id();
        let mut cases = self.cases.lock();

        if let Some((i, _)) = cases.iter().enumerate().find(|&(_, case)| {
            case.case_id == case_id && case.handle.thread_id() == thread_id
        }) {
            cases.remove(i);
            self.len.store(cases.len(), SeqCst);
            Self::maybe_shrink(&mut cases);
        }
    }

    /// Attempts to fire one case which is owned by another thread.
    pub fn notify_one(&self) {
        if self.len.load(SeqCst) > 0 {
            let thread_id = thread::current().id();
            let mut cases = self.cases.lock();

            for i in 0..cases.len() {
                if cases[i].handle.thread_id() != thread_id {
                    if cases[i].handle.try_select(cases[i].case_id) {
                        let case = cases.remove(i).unwrap();
                        self.len.store(cases.len(), SeqCst);
                        Self::maybe_shrink(&mut cases);

                        case.handle.unpark();
                        break;
                    }
                }
            }
        }
    }

    /// Aborts all currently registered selection cases.
    pub fn abort_all(&self) {
        if self.len.load(SeqCst) > 0 {
            let mut cases = self.cases.lock();

            self.len.store(0, SeqCst);
            for case in cases.drain(..) {
                if case.handle.try_select(CaseId::abort()) {
                    case.handle.unpark();
                }
            }

            Self::maybe_shrink(&mut cases);
        }
    }

    /// Shrinks the internal deque if it's capacity is much larger than length.
    fn maybe_shrink(cases: &mut VecDeque<Case>) {
        if cases.capacity() > 32 && cases.len() < cases.capacity() / 4 {
            let mut v = VecDeque::with_capacity(cases.capacity() / 2);
            v.extend(cases.drain(..));
            *cases = v;
        }
    }
}

impl Drop for Monitor {
    fn drop(&mut self) {
        debug_assert!(self.cases.lock().is_empty());
        debug_assert_eq!(self.len.load(SeqCst), 0);
    }
}
<|endoftext|>


<commit_before>use libzakopane::config::Config;
use libzakopane::snapshot::Snapshot;
use libzakopane::structs::CliOptions;
use libzakopane::structs::ZakopaneError;

use clap::{App, Arg, ArgMatches};

const DEFAULT_POLICY_ARG_NAME: &'static str = "default-policy";
const CONFIG_FILE_ARG_NAME: &'static str = "config";
const OLD_SNAPSHOT_PATH_ARG_NAME: &'static str = "old-snapshot-path";
const NEW_SNAPSHOT_PATH_ARG_NAME: &'static str = "new-snapshot-path";

// Holds one instance of each struct necessary to operate.
struct OperationalData {
    config: Config,
    old_snapshot: Snapshot,
    new_snapshot: Snapshot,
}

// Reads parsed command-line arguments and returns the appropriate
// operational data. Can abort the program on error.
fn complete_initialization(matches: &ArgMatches) -> Result<OperationalData, ZakopaneError> {
    // The two snapshot paths are required, so these are safe to unwrap.
    let old_snapshot_path = matches.value_of(OLD_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let new_snapshot_path = matches.value_of(NEW_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let old_contents = libzakopane::helpers::ingest_file(old_snapshot_path)?;
    let new_contents = libzakopane::helpers::ingest_file(new_snapshot_path)?;

    let options = CliOptions {
        config_path: matches.value_of(CONFIG_FILE_ARG_NAME),
        default_policy: matches.value_of(DEFAULT_POLICY_ARG_NAME),
    };

    Ok(OperationalData {
        config: Config::new(&options)?,
        old_snapshot: Snapshot::new(&old_contents)?,
        new_snapshot: Snapshot::new(&new_contents)?,
    })
}

// Begins parsing command-line arguments. Can abort the program on
// error.
fn initialize() -> Result<OperationalData, ZakopaneError> {
    let matches = App::new("zakopane")
        .version("0.2.0")
        .author("j39m")
        .about("compares zakopane snapshots")
        .arg(
            Arg::with_name(CONFIG_FILE_ARG_NAME)
                .short("c")
                .long("config")
                .value_name("FILE")
                .help("specifies a zakopane config")
                .takes_value(true),
        )
        .arg(
            Arg::with_name(DEFAULT_POLICY_ARG_NAME)
                .short("d")
                .long("default-policy")
                .value_name("POLICY_TOKENS")
                .help("specifies an explicit default policy")
                .takes_value(true),
        )
        .arg(
            Arg::with_name(OLD_SNAPSHOT_PATH_ARG_NAME)
                .help("path to older snapshot")
                .index(1)
                .required(true),
        )
        .arg(
            Arg::with_name(NEW_SNAPSHOT_PATH_ARG_NAME)
                .help("path to newer snapshot")
                .index(2)
                .required(true),
        )
        .get_matches();
    return complete_initialization(&matches);
}

fn main() {
    let operational_data = match initialize() {
        Ok(data) => data,
        Err(error) => {
            eprintln!("{}", error.to_string());
            std::process::exit(1);
        }
    };

    let OperationalData {
        config,
        new_snapshot,
        old_snapshot,
    } = operational_data;
    assert!(config.rules() > 0);
    let violations = libzakopane::enter(&config, &old_snapshot, &new_snapshot);
    println!("{}", violations);
}
<commit_msg>Introduce zakopane subcommands<commit_after>use libzakopane::config::Config;
use libzakopane::snapshot::Snapshot;
use libzakopane::structs::CliOptions;
use libzakopane::structs::ZakopaneError;

use clap::{App, Arg, ArgMatches, SubCommand};

const DEFAULT_POLICY_ARG_NAME: &'static str = "default-policy";
const CONFIG_FILE_ARG_NAME: &'static str = "config";
const OLD_SNAPSHOT_PATH_ARG_NAME: &'static str = "old-snapshot-path";
const NEW_SNAPSHOT_PATH_ARG_NAME: &'static str = "new-snapshot-path";

// Holds one instance of each struct necessary to operate.
struct OperationalData {
    config: Config,
    old_snapshot: Snapshot,
    new_snapshot: Snapshot,
}

// Reads parsed command-line arguments and returns the appropriate
// operational data. Can abort the program on error.
fn complete_initialization(matches: &ArgMatches) -> Result<OperationalData, ZakopaneError> {
    // The two snapshot paths are required, so these are safe to unwrap.
    let old_snapshot_path = matches.value_of(OLD_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let new_snapshot_path = matches.value_of(NEW_SNAPSHOT_PATH_ARG_NAME).unwrap();
    let old_contents = libzakopane::helpers::ingest_file(old_snapshot_path)?;
    let new_contents = libzakopane::helpers::ingest_file(new_snapshot_path)?;

    let options = CliOptions {
        config_path: matches.value_of(CONFIG_FILE_ARG_NAME),
        default_policy: matches.value_of(DEFAULT_POLICY_ARG_NAME),
    };

    Ok(OperationalData {
        config: Config::new(&options)?,
        old_snapshot: Snapshot::new(&old_contents)?,
        new_snapshot: Snapshot::new(&new_contents)?,
    })
}

// Begins parsing command-line arguments. Can abort the program on
// error.
fn initialize() -> Result<OperationalData, ZakopaneError> {
    let matches = App::new("zakopane")
        .version("0.2.0")
        .author("j39m")
        .about("checksums directories")
        .subcommand(
            SubCommand::with_name("compare")
                .about("compares two zakopane snapshots")
                .arg(
                    Arg::with_name(CONFIG_FILE_ARG_NAME)
                        .short("c")
                        .long("config")
                        .value_name("FILE")
                        .help("specifies a zakopane config")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(DEFAULT_POLICY_ARG_NAME)
                        .short("d")
                        .long("default-policy")
                        .value_name("POLICY_TOKENS")
                        .help("specifies an explicit default policy")
                        .takes_value(true),
                )
                .arg(
                    Arg::with_name(OLD_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to older snapshot")
                        .index(1)
                        .required(true),
                )
                .arg(
                    Arg::with_name(NEW_SNAPSHOT_PATH_ARG_NAME)
                        .help("path to newer snapshot")
                        .index(2)
                        .required(true),
                ),
        )
        .get_matches();
    return complete_initialization(&matches);
}

fn main() {
    let operational_data = match initialize() {
        Ok(data) => data,
        Err(error) => {
            eprintln!("{}", error.to_string());
            std::process::exit(1);
        }
    };

    let OperationalData {
        config,
        new_snapshot,
        old_snapshot,
    } = operational_data;
    assert!(config.rules() > 0);
    let violations = libzakopane::enter(&config, &old_snapshot, &new_snapshot);
    println!("{}", violations);
}
<|endoftext|>


<commit_before>use sync::{Arc, Weak};

use vec::Vec;
use vec_deque::VecDeque;

use sync::spin::InterruptSpinLock;

use thread::WaitQueue;

use net::Interface;

use hal::net::discover;

use net::Packet;
use net::defs::Device;

const MAX_QUEUE_SIZE: usize = 512;

#[derive(Clone)]
/// A network stack
///
/// This object represent a shareable network stack. This object stack
/// internally uses an Arc so it can be safely shared.
pub struct Instance(Arc<InstanceRaw>);

/// A weak reference over a network stack
pub struct InstanceWeak(Weak<InstanceRaw>);

struct InstanceRaw {
    /// Interfaces registered
    interfaces: Vec<Interface>,
    /// Contains packets to be processed
    rx_queue: InterruptSpinLock<VecDeque<Packet>>,
    /// Used to wait for packet to arrive in the rx_queue
    rx_wait: WaitQueue,
}

// rx_queue is protected by a spin lock
// rx_wait is Sync
unsafe impl Sync for InstanceRaw {}

impl Instance {
    /// Network thread linked to an instance
    ///
    /// This function is used to process packet that are received within a
    /// network stack instance
    pub fn network_thread(instance: Instance) {
        loop {
            let pkt_opt = instance.0.rx_queue.lock().pop_front();

            match pkt_opt {
                None => {
                    instance.refresh_interfaces();
                    // No packet to process => wait for one to come
                    wait_event!(instance.0.rx_wait,
                                !instance.0.rx_queue.lock().is_empty());
                    instance.refresh_interfaces();
                }
                Some(pkt) => {
                    // Treat the packet
                    println!("New incoming packet!");
                }
            }
        }
    }

    /// Create a new network stack
    ///
    /// TODO: This cannot really be used more than once for now.
    pub fn new() -> Self {
        let intfs = discover();

        if intfs.is_empty() {
            println!("Warning: Uni.rs is built with network capabilities but no interface found");
        } else {
            println!("{} interface(s) discovered:", intfs.len());

            for i in &intfs {
                println!("  - {} ({})", i.read().name_ref(),
                         i.read().hw_addr_ref());
            }
        }

        let inner = Arc::new(InstanceRaw {
            interfaces: intfs,
            rx_queue: InterruptSpinLock::new(VecDeque::with_capacity(MAX_QUEUE_SIZE)),
            rx_wait: WaitQueue::new(),
        });

        Instance(inner)
    }

    /// Get a weak reference over the network stack
    pub fn downgrade(&self) -> InstanceWeak {
        InstanceWeak(Arc::downgrade(&self.0))
    }

    /// Get the list of registered interfaces within network stack
    pub fn interfaces(&self) -> &[Interface] {
        &self.0.interfaces[..]
    }

    /// Call refresh on every registered interface
    fn refresh_interfaces(&self) {
        for intf in self.interfaces() {
            intf.write().refresh();
        }
    }

    /// Enqueue a packet to be received on the network stack
    ///
    /// Returns false if the receive queue is full and therefore the packet was
    /// not enqueued.
    ///
    /// Note: This is safe to be called from interrupt context. Indeed the
    /// receive queue is not resizable so no allocation will be performed by
    /// this function
    pub fn enqueue_rx_packet(&self, packet: Packet) -> bool {
        let mut locked_rx_queue = self.0.rx_queue.lock();

        if locked_rx_queue.len() == MAX_QUEUE_SIZE {
            // Queue is full, we don't want to cause a reallocation in
            // interruption context. So we don't enqueue the packet
            return false;
        }

        // Enqueue the packet
        locked_rx_queue.push_back(packet);

        // Wake up network thread
        self.0.rx_wait.unblock();

        true
    }
}

impl InstanceWeak {
    /// Upgrade the weak reference to a strong one
    pub fn upgrade(&self) -> Option<Instance> {
        self.0.upgrade().map(|strong| Instance(strong))
    }
}
<commit_msg>net: interface list in Instance is now locked<commit_after>use sync::{Arc, Weak};

use vec::Vec;
use vec_deque::VecDeque;

use sync::spin::{InterruptSpinLock, RwLock, RwLockReadGuard};

use thread::WaitQueue;

use net::Interface;

use hal::net::discover;

use net::Packet;
use net::defs::Device;

const MAX_QUEUE_SIZE: usize = 512;

#[derive(Clone)]
/// A network stack
///
/// This object represent a shareable network stack. This object stack
/// internally uses an Arc so it can be safely shared.
pub struct Instance(Arc<InstanceRaw>);

/// A weak reference over a network stack
pub struct InstanceWeak(Weak<InstanceRaw>);

struct InstanceRaw {
    /// Interfaces registered
    interfaces: RwLock<Vec<Interface>>,
    /// Contains packets to be processed
    rx_queue: InterruptSpinLock<VecDeque<Packet>>,
    /// Used to wait for packet to arrive in the rx_queue
    rx_wait: WaitQueue,
}

// rx_queue is protected by a spin lock
// rx_wait is Sync
unsafe impl Sync for InstanceRaw {}

impl Instance {
    /// Network thread linked to an instance
    ///
    /// This function is used to process packet that are received within a
    /// network stack instance
    pub fn network_thread(instance: Instance) {
        loop {
            let pkt_opt = instance.0.rx_queue.lock().pop_front();

            match pkt_opt {
                None => {
                    instance.refresh_interfaces();
                    // No packet to process => wait for one to come
                    wait_event!(instance.0.rx_wait,
                                !instance.0.rx_queue.lock().is_empty());
                    instance.refresh_interfaces();
                }
                Some(pkt) => {
                    // Treat the packet
                    println!("New incoming packet!");
                }
            }
        }
    }

    /// Create a new network stack
    ///
    /// TODO: This cannot really be used more than once for now.
    pub fn new() -> Self {
        let inner = Arc::new(InstanceRaw {
            interfaces: RwLock::new(Vec::new()),
            rx_queue: InterruptSpinLock::new(VecDeque::with_capacity(MAX_QUEUE_SIZE)),
            rx_wait: WaitQueue::new(),
        });

        let instance = Instance(inner);

        let intfs = discover();

        if intfs.is_empty() {
            println!("Warning: Uni.rs is built with network capabilities but no interface found");
        } else {
            println!("{} interface(s) discovered:", intfs.len());

            for i in &intfs {
                println!("  - {} ({})", i.read().name_ref(),
                         i.read().hw_addr_ref());
            }
        }

        *(instance.0.interfaces.write()) = intfs;

        instance
    }

    /// Get a weak reference over the network stack
    pub fn downgrade(&self) -> InstanceWeak {
        InstanceWeak(Arc::downgrade(&self.0))
    }

    /// Get the list of registered interfaces within network stack
    pub fn interfaces<'a>(&'a self) -> RwLockReadGuard<'a, Vec<Interface>> {
        self.0.interfaces.read()
    }

    /// Call refresh on every registered interface
    fn refresh_interfaces(&self) {
        for intf in self.interfaces().iter() {
            intf.write().refresh();
        }
    }

    /// Enqueue a packet to be received on the network stack
    ///
    /// Returns false if the receive queue is full and therefore the packet was
    /// not enqueued.
    ///
    /// Note: This is safe to be called from interrupt context. Indeed the
    /// receive queue is not resizable so no allocation will be performed by
    /// this function
    pub fn enqueue_rx_packet(&self, packet: Packet) -> bool {
        let mut locked_rx_queue = self.0.rx_queue.lock();

        if locked_rx_queue.len() == MAX_QUEUE_SIZE {
            // Queue is full, we don't want to cause a reallocation in
            // interruption context. So we don't enqueue the packet
            return false;
        }

        // Enqueue the packet
        locked_rx_queue.push_back(packet);

        // Wake up network thread
        self.0.rx_wait.unblock();

        true
    }
}

impl InstanceWeak {
    /// Upgrade the weak reference to a strong one
    pub fn upgrade(&self) -> Option<Instance> {
        self.0.upgrade().map(|strong| Instance(strong))
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::path::PathBuf;

use quire::validate as V;
use quire::ast::Ast as A;
use quire::ast::Ast;
use quire::ast::Tag::{NonSpecific};
use quire::ast::ScalarKind::{Plain};

use config::Range;
use super::volumes::{Volume, volume_validator};
use launcher::system::SystemInfo;

type PortNumValidator = V::Numeric;

#[derive(RustcDecodable, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum Pid1Mode {
    exec = 0,
    wait = 1,
    wait_all_children = 2,
}

#[derive(RustcDecodable, Debug, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum SuperviseMode {
    wait_all_successful,
    stop_on_failure,
}

#[derive(RustcDecodable, Debug, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum WriteMode {
    read_only,
    //transient_reflink_copy, // TODO(tailhook)
    transient_hard_link_copy,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct Network {
    pub ip: String,
    pub hostname: Option<String>,
    pub ports: BTreeMap<u16, u16>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct CommandInfo {
    // Common for toplevel commands
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,
    pub pass_tcp_socket: Option<String>,
    pub prerequisites: Vec<String>,
    pub options: Option<String>,  // Only for toplevel
    pub expect_inotify_limit: Option<usize>,
    pub link_as: Option<String>,
    pub linked_path_translation: bool,

    // Command
    pub tags: Vec<String>,  // Only for supervise chidlren
    pub network: Option<Network>, // Only for supervise children
    pub pid1mode: Pid1Mode,
    pub work_dir: Option<String>,
    pub container: String,
    pub accepts_arguments: Option<bool>,
    pub environ: BTreeMap<String, String>,
    pub volumes: BTreeMap<PathBuf, Volume>,
    pub write_mode: WriteMode,
    pub run: Vec<String>,
    pub user_id: u32,
    pub external_user_id: Option<u32>,
    pub group_id: u32,
    pub supplementary_gids: Vec<u32>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct CapsuleInfo {
    // Common for toplevel commands
    // TODO(tailhook) remove unuseful fields here
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,
    pub pass_tcp_socket: Option<String>,
    pub prerequisites: Vec<String>,
    pub options: Option<String>,  // Only for toplevel
    pub expect_inotify_limit: Option<usize>,
    pub link_as: Option<String>,
    pub linked_path_translation: bool,

    // CapsuleCommand
    pub uids: Vec<Range>,
    pub gids: Vec<Range>,
    pub work_dir: Option<String>,
    pub accepts_arguments: Option<bool>,
    pub environ: BTreeMap<String, String>,
    pub run: Vec<String>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct SuperviseInfo {
    // Common
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,
    pub prerequisites: Vec<String>,
    pub options: Option<String>,  // Only for toplevel
    pub expect_inotify_limit: Option<usize>,
    pub link_as: Option<String>,
    pub linked_path_translation: bool,

    // Supervise
    pub mode: SuperviseMode,
    pub isolate_network: bool,
    pub kill_unresponsive_after: u32,
    pub children: BTreeMap<String, ChildCommand>,
}

#[derive(RustcDecodable, PartialEq, Eq, Clone)]
pub enum MainCommand {
    Command(CommandInfo),
    CapsuleCommand(CapsuleInfo),
    Supervise(SuperviseInfo),
}

pub struct LinkInfo<'a> {
    pub name: &'a str,
    pub path_translation: bool,
}

impl MainCommand {
    pub fn description<'x>(&'x self) -> Option<&'x String> {
        match *self {
            MainCommand::Command(ref cmd) => cmd.description.as_ref(),
            MainCommand::Supervise(ref cmd) => cmd.description.as_ref(),
            MainCommand::CapsuleCommand(ref cmd) => cmd.description.as_ref(),
        }
    }
    pub fn system<'x>(&'x self) -> SystemInfo {
        match *self {
            MainCommand::Command(ref cmd) => SystemInfo {
                expect_inotify_limit: cmd.expect_inotify_limit,
            },
            MainCommand::Supervise(ref cmd) => SystemInfo {
                expect_inotify_limit: cmd.expect_inotify_limit,
            },
            MainCommand::CapsuleCommand(ref cmd) => SystemInfo {
                expect_inotify_limit: cmd.expect_inotify_limit,
            },
        }
    }
    pub fn link(&self) -> Option<LinkInfo> {
        match *self {
            MainCommand::Command(ref cmd) => {
                cmd.link_as.as_ref().map(|name| {
                    LinkInfo {
                        name: name,
                        path_translation: cmd.linked_path_translation,
                    }
                })
            },
            MainCommand::CapsuleCommand(ref cmd) => {
                cmd.link_as.as_ref().map(|name| {
                    LinkInfo {
                        name: name,
                        path_translation: cmd.linked_path_translation,
                    }
                })
            },
            MainCommand::Supervise(ref cmd) => {
                cmd.link_as.as_ref().map(|name| {
                    LinkInfo {
                        name: name,
                        path_translation: cmd.linked_path_translation,
                    }
                })
            },
        }
    }
}

#[derive(RustcDecodable, PartialEq, Eq, Clone)]
pub enum ChildCommand {
    Command(CommandInfo),
    BridgeCommand(CommandInfo),
}

impl ChildCommand {
    pub fn get_container<'x>(&'x self) -> &String {
        match *self {
            ChildCommand::Command(ref info) => &info.container,
            ChildCommand::BridgeCommand(ref info) => &info.container,
        }
    }
    pub fn get_tags<'x>(&'x self) -> &Vec<String> {
        match *self {
            ChildCommand::Command(ref info) => &info.tags,
            ChildCommand::BridgeCommand(ref info) => &info.tags,
        }
    }
    pub fn get_volumes(&self) -> &BTreeMap<PathBuf, Volume> {
        match *self {
            ChildCommand::Command(ref info) => &info.volumes,
            ChildCommand::BridgeCommand(ref info) => &info.volumes,
        }
    }
    pub fn prerequisites<'x>(&'x self) -> &Vec<String> {
        match *self {
            ChildCommand::Command(ref cmd) => cmd.prerequisites.as_ref(),
            ChildCommand::BridgeCommand(ref cmd) => cmd.prerequisites.as_ref(),
        }
    }
    pub fn pass_socket(&self) -> Option<&String> {
        match *self {
            ChildCommand::Command(ref c) => c.pass_tcp_socket.as_ref(),
            ChildCommand::BridgeCommand(ref c) => c.pass_tcp_socket.as_ref(),
        }
    }
}

fn shell_command(ast: Ast) -> Vec<Ast> {
    match ast {
        A::Scalar(pos, _, style, value) => {
            return vec!(
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "/bin/sh".to_string()),
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "-c".to_string()),
                A::Scalar(pos.clone(), NonSpecific, style,
                          value),
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "--".to_string()),
                );
        }
        _ => unreachable!(),
    }
}

fn run_fields<'a>(cmd: V::Structure, network: bool) -> V::Structure {
    let mut cmd = cmd
        .member("pid1mode", V::Scalar::new().default("wait"))
        .member("work_dir", V::Scalar::new().optional())
        .member("container", V::Scalar::new())
        .member("accepts_arguments", V::Scalar::new().optional())
        .member("environ", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("volumes", V::Mapping::new(
            V::Directory::new().absolute(true),
            volume_validator()))
        .member("write_mode", V::Scalar::new().default("read-only"))
        .member("run", V::Sequence::new(V::Scalar::new())
            .parser(shell_command as fn(Ast) -> Vec<Ast>))
        .member("user_id", V::Numeric::new().min(0).max(1 << 30).default(0))
        .member("external_user_id",
            V::Numeric::new().min(0).max(1 << 30).optional())
        .member("group_id", V::Numeric::new()
            .min(0).max(1 << 30).default(0))
        .member("supplementary_gids",
            V::Sequence::new(V::Numeric::new().min(0).max(1 << 30)));
    if network {
        cmd = cmd
            .member("network", V::Structure::new().optional()
                .member("ip", V::Scalar::new().optional())
                .member("hostname", V::Scalar::new().optional())
                .member("ports", V::Mapping::new(
                    V::Numeric::new(),
                    V::Numeric::new())))
    }
    return cmd;
}

fn command_fields<'a>(mut cmd: V::Structure, toplevel: bool) -> V::Structure
{
    cmd = cmd
        .member("description", V::Scalar::new().optional())
        .member("banner", V::Scalar::new().optional())
        .member("banner_delay", V::Numeric::new().optional().min(0))
        .member("epilog", V::Scalar::new().optional())
        .member("tags", V::Sequence::new(V::Scalar::new()))
        .member("pass_tcp_socket", V::Scalar::new().optional())
        .member("expect_inotify_limit", V::Scalar::new().optional())
        .member("link_as", V::Scalar::new().optional())
        .member("linked_path_translation", V::Scalar::new().default(false))
        .member("prerequisites", V::Sequence::new(V::Scalar::new()));
    if toplevel {
        cmd = cmd.member("options", V::Scalar::new().optional());
    }
    return cmd;
}

fn subcommand_validator<'a>() -> V::Enum<'a> {
    V::Enum::new()
    .option("Command",
        run_fields(command_fields(V::Structure::new(), false), true))
    .option("BridgeCommand",
        run_fields(command_fields(V::Structure::new(), false), false))
}

pub fn command_validator<'a>() -> V::Enum<'a> {
    let cmd = run_fields(command_fields(V::Structure::new(), true), false);
    let sup = command_fields(V::Structure::new(), true);
    let caps = command_fields(V::Structure::new(), true);

    let sup = sup
        .member("mode", V::Scalar::new().default("stop-on-failure"))
        .member("isolate_network", V::Scalar::new().default(false))
        .member("children", V::Mapping::new(
            V::Scalar::new(),
            subcommand_validator()))
        .member("kill_unresponsive_after",
            V::Numeric::new().default(2).min(1).max(86400));

    let caps = caps
        .member("uids", V::Sequence::new(V::Scalar::new()))
        .member("gids", V::Sequence::new(V::Scalar::new()))
        .member("work_dir", V::Scalar::new().optional())
        .member("run", V::Sequence::new(V::Scalar::new())
            .parser(shell_command as fn(Ast) -> Vec<Ast>))
        .member("environ", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("accepts_arguments", V::Scalar::new().optional());

    return V::Enum::new()
        .option("Command", cmd)
        .option("CapsuleCommand", caps)
        .option("Supervise", sup);
}

pub trait Networking {
    fn network<'x>(&'x self) -> Option<&'x Network>;
}

impl Networking for ChildCommand {
    fn network<'x>(&'x self) -> Option<&'x Network> {
        match self {
            &ChildCommand::Command(ref cmd) => cmd.network.as_ref(),
            &ChildCommand::BridgeCommand(_) => None,
        }
    }
}
<commit_msg>Implement options method for all types of commands<commit_after>use std::collections::BTreeMap;
use std::path::PathBuf;

use quire::validate as V;
use quire::ast::Ast as A;
use quire::ast::Ast;
use quire::ast::Tag::{NonSpecific};
use quire::ast::ScalarKind::{Plain};

use config::Range;
use super::volumes::{Volume, volume_validator};
use launcher::system::SystemInfo;

type PortNumValidator = V::Numeric;

#[derive(RustcDecodable, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum Pid1Mode {
    exec = 0,
    wait = 1,
    wait_all_children = 2,
}

#[derive(RustcDecodable, Debug, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum SuperviseMode {
    wait_all_successful,
    stop_on_failure,
}

#[derive(RustcDecodable, Debug, Clone, PartialEq, Eq, Copy)]
#[allow(non_camel_case_types)]
pub enum WriteMode {
    read_only,
    //transient_reflink_copy, // TODO(tailhook)
    transient_hard_link_copy,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct Network {
    pub ip: String,
    pub hostname: Option<String>,
    pub ports: BTreeMap<u16, u16>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct CommandInfo {
    // Common for toplevel commands
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,
    pub pass_tcp_socket: Option<String>,
    pub prerequisites: Vec<String>,
    pub options: Option<String>,  // Only for toplevel
    pub expect_inotify_limit: Option<usize>,
    pub link_as: Option<String>,
    pub linked_path_translation: bool,

    // Command
    pub tags: Vec<String>,  // Only for supervise chidlren
    pub network: Option<Network>, // Only for supervise children
    pub pid1mode: Pid1Mode,
    pub work_dir: Option<String>,
    pub container: String,
    pub accepts_arguments: Option<bool>,
    pub environ: BTreeMap<String, String>,
    pub volumes: BTreeMap<PathBuf, Volume>,
    pub write_mode: WriteMode,
    pub run: Vec<String>,
    pub user_id: u32,
    pub external_user_id: Option<u32>,
    pub group_id: u32,
    pub supplementary_gids: Vec<u32>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct CapsuleInfo {
    // Common for toplevel commands
    // TODO(tailhook) remove unuseful fields here
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,
    pub pass_tcp_socket: Option<String>,
    pub prerequisites: Vec<String>,
    pub options: Option<String>,  // Only for toplevel
    pub expect_inotify_limit: Option<usize>,
    pub link_as: Option<String>,
    pub linked_path_translation: bool,

    // CapsuleCommand
    pub uids: Vec<Range>,
    pub gids: Vec<Range>,
    pub work_dir: Option<String>,
    pub accepts_arguments: Option<bool>,
    pub environ: BTreeMap<String, String>,
    pub run: Vec<String>,
}

#[derive(RustcDecodable, Clone, PartialEq, Eq)]
pub struct SuperviseInfo {
    // Common
    pub description: Option<String>,
    pub banner: Option<String>,
    pub banner_delay: Option<u32>,
    pub epilog: Option<String>,
    pub prerequisites: Vec<String>,
    pub options: Option<String>,  // Only for toplevel
    pub expect_inotify_limit: Option<usize>,
    pub link_as: Option<String>,
    pub linked_path_translation: bool,

    // Supervise
    pub mode: SuperviseMode,
    pub isolate_network: bool,
    pub kill_unresponsive_after: u32,
    pub children: BTreeMap<String, ChildCommand>,
}

#[derive(RustcDecodable, PartialEq, Eq, Clone)]
pub enum MainCommand {
    Command(CommandInfo),
    CapsuleCommand(CapsuleInfo),
    Supervise(SuperviseInfo),
}

pub struct LinkInfo<'a> {
    pub name: &'a str,
    pub path_translation: bool,
}

impl MainCommand {
    pub fn description<'x>(&'x self) -> Option<&'x String> {
        match *self {
            MainCommand::Command(ref cmd) => cmd.description.as_ref(),
            MainCommand::Supervise(ref cmd) => cmd.description.as_ref(),
            MainCommand::CapsuleCommand(ref cmd) => cmd.description.as_ref(),
        }
    }
    pub fn options<'x>(&'x self) -> Option<&'x String> {
        match *self {
            MainCommand::Command(ref cmd) => cmd.options.as_ref(),
            MainCommand::Supervise(ref cmd) => cmd.options.as_ref(),
            MainCommand::CapsuleCommand(ref cmd) => cmd.options.as_ref(),
        }
    }
    pub fn system<'x>(&'x self) -> SystemInfo {
        match *self {
            MainCommand::Command(ref cmd) => SystemInfo {
                expect_inotify_limit: cmd.expect_inotify_limit,
            },
            MainCommand::Supervise(ref cmd) => SystemInfo {
                expect_inotify_limit: cmd.expect_inotify_limit,
            },
            MainCommand::CapsuleCommand(ref cmd) => SystemInfo {
                expect_inotify_limit: cmd.expect_inotify_limit,
            },
        }
    }
    pub fn link(&self) -> Option<LinkInfo> {
        match *self {
            MainCommand::Command(ref cmd) => {
                cmd.link_as.as_ref().map(|name| {
                    LinkInfo {
                        name: name,
                        path_translation: cmd.linked_path_translation,
                    }
                })
            },
            MainCommand::CapsuleCommand(ref cmd) => {
                cmd.link_as.as_ref().map(|name| {
                    LinkInfo {
                        name: name,
                        path_translation: cmd.linked_path_translation,
                    }
                })
            },
            MainCommand::Supervise(ref cmd) => {
                cmd.link_as.as_ref().map(|name| {
                    LinkInfo {
                        name: name,
                        path_translation: cmd.linked_path_translation,
                    }
                })
            },
        }
    }
}

#[derive(RustcDecodable, PartialEq, Eq, Clone)]
pub enum ChildCommand {
    Command(CommandInfo),
    BridgeCommand(CommandInfo),
}

impl ChildCommand {
    pub fn get_container<'x>(&'x self) -> &String {
        match *self {
            ChildCommand::Command(ref info) => &info.container,
            ChildCommand::BridgeCommand(ref info) => &info.container,
        }
    }
    pub fn get_tags<'x>(&'x self) -> &Vec<String> {
        match *self {
            ChildCommand::Command(ref info) => &info.tags,
            ChildCommand::BridgeCommand(ref info) => &info.tags,
        }
    }
    pub fn get_volumes(&self) -> &BTreeMap<PathBuf, Volume> {
        match *self {
            ChildCommand::Command(ref info) => &info.volumes,
            ChildCommand::BridgeCommand(ref info) => &info.volumes,
        }
    }
    pub fn prerequisites<'x>(&'x self) -> &Vec<String> {
        match *self {
            ChildCommand::Command(ref cmd) => cmd.prerequisites.as_ref(),
            ChildCommand::BridgeCommand(ref cmd) => cmd.prerequisites.as_ref(),
        }
    }
    pub fn pass_socket(&self) -> Option<&String> {
        match *self {
            ChildCommand::Command(ref c) => c.pass_tcp_socket.as_ref(),
            ChildCommand::BridgeCommand(ref c) => c.pass_tcp_socket.as_ref(),
        }
    }
}

fn shell_command(ast: Ast) -> Vec<Ast> {
    match ast {
        A::Scalar(pos, _, style, value) => {
            return vec!(
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "/bin/sh".to_string()),
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "-c".to_string()),
                A::Scalar(pos.clone(), NonSpecific, style,
                          value),
                A::Scalar(pos.clone(), NonSpecific, Plain,
                          "--".to_string()),
                );
        }
        _ => unreachable!(),
    }
}

fn run_fields<'a>(cmd: V::Structure, network: bool) -> V::Structure {
    let mut cmd = cmd
        .member("pid1mode", V::Scalar::new().default("wait"))
        .member("work_dir", V::Scalar::new().optional())
        .member("container", V::Scalar::new())
        .member("accepts_arguments", V::Scalar::new().optional())
        .member("environ", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("volumes", V::Mapping::new(
            V::Directory::new().absolute(true),
            volume_validator()))
        .member("write_mode", V::Scalar::new().default("read-only"))
        .member("run", V::Sequence::new(V::Scalar::new())
            .parser(shell_command as fn(Ast) -> Vec<Ast>))
        .member("user_id", V::Numeric::new().min(0).max(1 << 30).default(0))
        .member("external_user_id",
            V::Numeric::new().min(0).max(1 << 30).optional())
        .member("group_id", V::Numeric::new()
            .min(0).max(1 << 30).default(0))
        .member("supplementary_gids",
            V::Sequence::new(V::Numeric::new().min(0).max(1 << 30)));
    if network {
        cmd = cmd
            .member("network", V::Structure::new().optional()
                .member("ip", V::Scalar::new().optional())
                .member("hostname", V::Scalar::new().optional())
                .member("ports", V::Mapping::new(
                    V::Numeric::new(),
                    V::Numeric::new())))
    }
    return cmd;
}

fn command_fields<'a>(mut cmd: V::Structure, toplevel: bool) -> V::Structure
{
    cmd = cmd
        .member("description", V::Scalar::new().optional())
        .member("banner", V::Scalar::new().optional())
        .member("banner_delay", V::Numeric::new().optional().min(0))
        .member("epilog", V::Scalar::new().optional())
        .member("tags", V::Sequence::new(V::Scalar::new()))
        .member("pass_tcp_socket", V::Scalar::new().optional())
        .member("expect_inotify_limit", V::Scalar::new().optional())
        .member("link_as", V::Scalar::new().optional())
        .member("linked_path_translation", V::Scalar::new().default(false))
        .member("prerequisites", V::Sequence::new(V::Scalar::new()));
    if toplevel {
        cmd = cmd.member("options", V::Scalar::new().optional());
    }
    return cmd;
}

fn subcommand_validator<'a>() -> V::Enum<'a> {
    V::Enum::new()
    .option("Command",
        run_fields(command_fields(V::Structure::new(), false), true))
    .option("BridgeCommand",
        run_fields(command_fields(V::Structure::new(), false), false))
}

pub fn command_validator<'a>() -> V::Enum<'a> {
    let cmd = run_fields(command_fields(V::Structure::new(), true), false);
    let sup = command_fields(V::Structure::new(), true);
    let caps = command_fields(V::Structure::new(), true);

    let sup = sup
        .member("mode", V::Scalar::new().default("stop-on-failure"))
        .member("isolate_network", V::Scalar::new().default(false))
        .member("children", V::Mapping::new(
            V::Scalar::new(),
            subcommand_validator()))
        .member("kill_unresponsive_after",
            V::Numeric::new().default(2).min(1).max(86400));

    let caps = caps
        .member("uids", V::Sequence::new(V::Scalar::new()))
        .member("gids", V::Sequence::new(V::Scalar::new()))
        .member("work_dir", V::Scalar::new().optional())
        .member("run", V::Sequence::new(V::Scalar::new())
            .parser(shell_command as fn(Ast) -> Vec<Ast>))
        .member("environ", V::Mapping::new(V::Scalar::new(), V::Scalar::new()))
        .member("accepts_arguments", V::Scalar::new().optional());

    return V::Enum::new()
        .option("Command", cmd)
        .option("CapsuleCommand", caps)
        .option("Supervise", sup);
}

pub trait Networking {
    fn network<'x>(&'x self) -> Option<&'x Network>;
}

impl Networking for ChildCommand {
    fn network<'x>(&'x self) -> Option<&'x Network> {
        match self {
            &ChildCommand::Command(ref cmd) => cmd.network.as_ref(),
            &ChildCommand::BridgeCommand(_) => None,
        }
    }
}
<|endoftext|>


<commit_before>//! Option parsing and management.
//!
//! Use the `Options::parse()` function to get the program's configuration,
//! as parsed from the commandline.
//!
//! # Examples
//!
//! ```no_run
//! # use cargo_update::Options;
//! let opts = Options::parse();
//! println!("{:#?}", opts);
//! ```

use clap::{self, AppSettings, SubCommand, App, Arg};
use self::super::ops::ConfigOperation;
use std::env::{self, home_dir};
use array_tool::vec::Uniq;
use std::path::PathBuf;
use std::fs;

/// Representation of the application's all configurable values.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Options {
    /// Packages to update. Default: `None`
    ///
    /// If empty - update all.
    pub to_update: Vec<String>,
    /// Whether to update packages or just list them. Default: `true`
    pub update: bool,
    /// Whether to allow for just installing packages. Default: `false`
    pub install: bool,
    /// Update all packages. Default: `false`
    pub force: bool,
    /// The `cargo` home directory. Default: in `"$CARGO_INSTALL_ROOT"`, then `"$CARGO_HOME"`, then `"$HOME/.cargo"`
    pub crates_file: (String, PathBuf),
    /// The `cargo` home directory. Default: `"$CARGO_HOME"`, then `"$HOME/.cargo"`
    pub cargo_dir: (String, PathBuf),
}

/// Representation of the config application's all configurable values.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct ConfigOptions {
    /// The `config` file in the `cargo` home directory.
    /// Default: in `"$CARGO_INSTALL_ROOT"`, then `"$CARGO_HOME"`, then `"$HOME/.cargo"`
    pub crates_file: (String, PathBuf),
    /// Crate to modify config for
    pub package: String,
    /// What to do to the config, or display with empty
    pub ops: Vec<ConfigOperation>,
}

impl Options {
    /// Parse `env`-wide command-line arguments into an `Options` instance
    pub fn parse() -> Options {
        let matches = App::new("cargo-install-update")
            .bin_name("cargo")
            .settings(&[AppSettings::ColoredHelp, AppSettings::ArgRequiredElseHelp, AppSettings::GlobalVersion, AppSettings::SubcommandRequired])
            .subcommand(SubCommand::with_name("install-update")
                .version(crate_version!())
                .author(crate_authors!("\n"))
                .about("A cargo subcommand for checking and applying updates to installed executables")
                .args(&[Arg::from_usage("-c --cargo-dir=[CARGO_DIR] 'The cargo home directory. Default: $CARGO_HOME or $HOME/.cargo'")
                            .validator(cargo_dir_validator),
                        Arg::from_usage("-a --all 'Update all packages'").conflicts_with("PACKAGE"),
                        Arg::from_usage("-l --list 'Don't update packages, only list and check if they need an update'"),
                        Arg::from_usage("-f --force 'Update all packages regardless if they need updating'"),
                        Arg::from_usage("--allow-no-update 'Allow for fresh-installing packages'"),
                        Arg::from_usage("<PACKAGE>... 'Packages to update'").conflicts_with("all").empty_values(false).min_values(1)]))
            .get_matches();
        let matches = matches.subcommand_matches("install-update").unwrap();

        let cdir = cargo_dir();
        Options {
            to_update: if matches.is_present("all") {
                vec![]
            } else {
                let packages: Vec<_> = matches.values_of("PACKAGE").unwrap().map(String::from).collect();
                packages.unique()
            },
            update: !matches.is_present("list"),
            install: matches.is_present("allow-no-update"),
            force: matches.is_present("force"),
            crates_file: match matches.value_of("cargo-dir") {
                Some(dirs) => (dirs.to_string(), fs::canonicalize(dirs).unwrap()),
                None => {
                    match env::var("CARGO_INSTALL_ROOT").map_err(|_| ()).and_then(|ch| fs::canonicalize(ch).map_err(|_| ())) {
                        Ok(ch) => ("$CARGO_INSTALL_ROOT/.crates.toml".to_string(), ch.join(".crates.toml")),
                        Err(()) => (format!("{}/.crates.toml", cdir.0), cdir.1.join(".crates.toml")),
                    }
                }
            },
            cargo_dir: cdir,
        }
    }
}

impl ConfigOptions {
    /// Parse `env`-wide command-line arguments into an `ConfigOptions` instance
    pub fn parse() -> ConfigOptions {
        let matches = App::new("cargo-install-update-config")
            .bin_name("cargo")
            .settings(&[AppSettings::ColoredHelp, AppSettings::ArgRequiredElseHelp, AppSettings::GlobalVersion, AppSettings::SubcommandRequired])
            .subcommand(SubCommand::with_name("install-update-config")
                .version(crate_version!())
                .author(crate_authors!("\n"))
                .about("A cargo subcommand for checking and applying updates to installed executables -- configuration")
                .args(&[Arg::from_usage("-c --cargo-dir=[CARGO_DIR] 'The cargo home directory. Default: $CARGO_HOME or $HOME/.cargo'")
                            .validator(cargo_dir_validator),
                        Arg::from_usage("-t --toolchain=[TOOLCHAIN] 'Toolchain to use or empty for default'"),
                        Arg::from_usage("-f --feature=[FEATURE]... 'Feature to enable'"),
                        Arg::from_usage("-n --no-feature=[DISABLED_FEATURE]... 'Feature to disable'"),
                        Arg::from_usage("-d --default-features=[DEFAULT_FEATURES] 'Whether to allow default features'")
                            .possible_values(&["1", "yes", "true", "0", "no", "false"])
                            .hide_possible_values(true),
                        Arg::from_usage("<PACKAGE> 'Packages to update'").empty_values(false)]))
            .get_matches();
        let matches = matches.subcommand_matches("install-update-config").unwrap();

        let cdir = cargo_dir();
        ConfigOptions {
            crates_file: match matches.value_of("cargo-dir") {
                Some(dirs) => (dirs.to_string(), fs::canonicalize(dirs).unwrap()),
                None => {
                    match env::var("CARGO_INSTALL_ROOT").map_err(|_| ()).and_then(|ch| fs::canonicalize(ch).map_err(|_| ())) {
                        Ok(ch) => ("$CARGO_INSTALL_ROOT/.crates.toml".to_string(), ch.join(".crates.toml")),
                        Err(()) => (format!("{}/.crates.toml", cdir.0), cdir.1.join(".crates.toml")),
                    }
                }
            },
            package: matches.value_of("PACKAGE").unwrap().to_string(),
            ops: matches.value_of("toolchain")
                .map(|t| if t.is_empty() {
                    ConfigOperation::RemoveToolchain
                } else {
                    ConfigOperation::SetToolchain(t.to_string())
                })
                .into_iter()
                .chain(matches.values_of("feature").into_iter().flat_map(|f| f).map(str::to_string).map(ConfigOperation::AddFeature))
                .chain(matches.values_of("no-feature").into_iter().flat_map(|f| f).map(str::to_string).map(ConfigOperation::RemoveFeature))
                .chain(matches.value_of("default-features").map(|d| ["1", "yes", "true"].contains(&d)).map(ConfigOperation::DefaultFeatures).into_iter())
                .collect(),
        }
    }
}

fn cargo_dir() -> (String, PathBuf) {
    match env::var("CARGO_HOME").map_err(|_| ()).and_then(|ch| fs::canonicalize(ch).map_err(|_| ())) {
        Ok(ch) => ("$CARGO_HOME".to_string(), ch),
        Err(()) =>
                match home_dir().and_then(|hd| hd.canonicalize().ok()) {
                    Some(mut hd) => {
                        hd.push(".cargo");

                        fs::create_dir_all(&hd).unwrap();
                        ("$HOME/.cargo".to_string(), hd)
                    }
                    None => {
                        clap::Error {
                                message: "$CARGO_HOME and home directory invalid, please specify the cargo home directory with the -c option".to_string(),
                                kind: clap::ErrorKind::MissingRequiredArgument,
                                info: None,
                            }
                            .exit()
                    }
                },
    }
}

fn cargo_dir_validator(s: String) -> Result<(), String> {
    fs::canonicalize(&s).map(|_| ()).map_err(|_| format!("Cargo directory \"{}\" not found", s))
}
<commit_msg>Add -i as short for --allow-no-update<commit_after>//! Option parsing and management.
//!
//! Use the `Options::parse()` function to get the program's configuration,
//! as parsed from the commandline.
//!
//! # Examples
//!
//! ```no_run
//! # use cargo_update::Options;
//! let opts = Options::parse();
//! println!("{:#?}", opts);
//! ```

use clap::{self, AppSettings, SubCommand, App, Arg};
use self::super::ops::ConfigOperation;
use std::env::{self, home_dir};
use array_tool::vec::Uniq;
use std::path::PathBuf;
use std::fs;

/// Representation of the application's all configurable values.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct Options {
    /// Packages to update. Default: `None`
    ///
    /// If empty - update all.
    pub to_update: Vec<String>,
    /// Whether to update packages or just list them. Default: `true`
    pub update: bool,
    /// Whether to allow for just installing packages. Default: `false`
    pub install: bool,
    /// Update all packages. Default: `false`
    pub force: bool,
    /// The `cargo` home directory. Default: in `"$CARGO_INSTALL_ROOT"`, then `"$CARGO_HOME"`, then `"$HOME/.cargo"`
    pub crates_file: (String, PathBuf),
    /// The `cargo` home directory. Default: `"$CARGO_HOME"`, then `"$HOME/.cargo"`
    pub cargo_dir: (String, PathBuf),
}

/// Representation of the config application's all configurable values.
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct ConfigOptions {
    /// The `config` file in the `cargo` home directory.
    /// Default: in `"$CARGO_INSTALL_ROOT"`, then `"$CARGO_HOME"`, then `"$HOME/.cargo"`
    pub crates_file: (String, PathBuf),
    /// Crate to modify config for
    pub package: String,
    /// What to do to the config, or display with empty
    pub ops: Vec<ConfigOperation>,
}

impl Options {
    /// Parse `env`-wide command-line arguments into an `Options` instance
    pub fn parse() -> Options {
        let matches = App::new("cargo-install-update")
            .bin_name("cargo")
            .settings(&[AppSettings::ColoredHelp, AppSettings::ArgRequiredElseHelp, AppSettings::GlobalVersion, AppSettings::SubcommandRequired])
            .subcommand(SubCommand::with_name("install-update")
                .version(crate_version!())
                .author(crate_authors!("\n"))
                .about("A cargo subcommand for checking and applying updates to installed executables")
                .args(&[Arg::from_usage("-c --cargo-dir=[CARGO_DIR] 'The cargo home directory. Default: $CARGO_HOME or $HOME/.cargo'")
                            .validator(cargo_dir_validator),
                        Arg::from_usage("-a --all 'Update all packages'").conflicts_with("PACKAGE"),
                        Arg::from_usage("-l --list 'Don't update packages, only list and check if they need an update'"),
                        Arg::from_usage("-f --force 'Update all packages regardless if they need updating'"),
                        Arg::from_usage("-i --allow-no-update 'Allow for fresh-installing packages'"),
                        Arg::from_usage("<PACKAGE>... 'Packages to update'").conflicts_with("all").empty_values(false).min_values(1)]))
            .get_matches();
        let matches = matches.subcommand_matches("install-update").unwrap();

        let cdir = cargo_dir();
        Options {
            to_update: if matches.is_present("all") {
                vec![]
            } else {
                let packages: Vec<_> = matches.values_of("PACKAGE").unwrap().map(String::from).collect();
                packages.unique()
            },
            update: !matches.is_present("list"),
            install: matches.is_present("allow-no-update"),
            force: matches.is_present("force"),
            crates_file: match matches.value_of("cargo-dir") {
                Some(dirs) => (dirs.to_string(), fs::canonicalize(dirs).unwrap()),
                None => {
                    match env::var("CARGO_INSTALL_ROOT").map_err(|_| ()).and_then(|ch| fs::canonicalize(ch).map_err(|_| ())) {
                        Ok(ch) => ("$CARGO_INSTALL_ROOT/.crates.toml".to_string(), ch.join(".crates.toml")),
                        Err(()) => (format!("{}/.crates.toml", cdir.0), cdir.1.join(".crates.toml")),
                    }
                }
            },
            cargo_dir: cdir,
        }
    }
}

impl ConfigOptions {
    /// Parse `env`-wide command-line arguments into an `ConfigOptions` instance
    pub fn parse() -> ConfigOptions {
        let matches = App::new("cargo-install-update-config")
            .bin_name("cargo")
            .settings(&[AppSettings::ColoredHelp, AppSettings::ArgRequiredElseHelp, AppSettings::GlobalVersion, AppSettings::SubcommandRequired])
            .subcommand(SubCommand::with_name("install-update-config")
                .version(crate_version!())
                .author(crate_authors!("\n"))
                .about("A cargo subcommand for checking and applying updates to installed executables -- configuration")
                .args(&[Arg::from_usage("-c --cargo-dir=[CARGO_DIR] 'The cargo home directory. Default: $CARGO_HOME or $HOME/.cargo'")
                            .validator(cargo_dir_validator),
                        Arg::from_usage("-t --toolchain=[TOOLCHAIN] 'Toolchain to use or empty for default'"),
                        Arg::from_usage("-f --feature=[FEATURE]... 'Feature to enable'"),
                        Arg::from_usage("-n --no-feature=[DISABLED_FEATURE]... 'Feature to disable'"),
                        Arg::from_usage("-d --default-features=[DEFAULT_FEATURES] 'Whether to allow default features'")
                            .possible_values(&["1", "yes", "true", "0", "no", "false"])
                            .hide_possible_values(true),
                        Arg::from_usage("<PACKAGE> 'Packages to update'").empty_values(false)]))
            .get_matches();
        let matches = matches.subcommand_matches("install-update-config").unwrap();

        let cdir = cargo_dir();
        ConfigOptions {
            crates_file: match matches.value_of("cargo-dir") {
                Some(dirs) => (dirs.to_string(), fs::canonicalize(dirs).unwrap()),
                None => {
                    match env::var("CARGO_INSTALL_ROOT").map_err(|_| ()).and_then(|ch| fs::canonicalize(ch).map_err(|_| ())) {
                        Ok(ch) => ("$CARGO_INSTALL_ROOT/.crates.toml".to_string(), ch.join(".crates.toml")),
                        Err(()) => (format!("{}/.crates.toml", cdir.0), cdir.1.join(".crates.toml")),
                    }
                }
            },
            package: matches.value_of("PACKAGE").unwrap().to_string(),
            ops: matches.value_of("toolchain")
                .map(|t| if t.is_empty() {
                    ConfigOperation::RemoveToolchain
                } else {
                    ConfigOperation::SetToolchain(t.to_string())
                })
                .into_iter()
                .chain(matches.values_of("feature").into_iter().flat_map(|f| f).map(str::to_string).map(ConfigOperation::AddFeature))
                .chain(matches.values_of("no-feature").into_iter().flat_map(|f| f).map(str::to_string).map(ConfigOperation::RemoveFeature))
                .chain(matches.value_of("default-features").map(|d| ["1", "yes", "true"].contains(&d)).map(ConfigOperation::DefaultFeatures).into_iter())
                .collect(),
        }
    }
}

fn cargo_dir() -> (String, PathBuf) {
    match env::var("CARGO_HOME").map_err(|_| ()).and_then(|ch| fs::canonicalize(ch).map_err(|_| ())) {
        Ok(ch) => ("$CARGO_HOME".to_string(), ch),
        Err(()) =>
                match home_dir().and_then(|hd| hd.canonicalize().ok()) {
                    Some(mut hd) => {
                        hd.push(".cargo");

                        fs::create_dir_all(&hd).unwrap();
                        ("$HOME/.cargo".to_string(), hd)
                    }
                    None => {
                        clap::Error {
                                message: "$CARGO_HOME and home directory invalid, please specify the cargo home directory with the -c option".to_string(),
                                kind: clap::ErrorKind::MissingRequiredArgument,
                                info: None,
                            }
                            .exit()
                    }
                },
    }
}

fn cargo_dir_validator(s: String) -> Result<(), String> {
    fs::canonicalize(&s).map(|_| ()).map_err(|_| format!("Cargo directory \"{}\" not found", s))
}
<|endoftext|>


<commit_before>use std::io;
use std::fmt;
use std::fs::File;
use std::io::Read;

use regex;
use regex::Regex;

#[derive(Debug)]
pub enum LicenseError {
    IO(io::Error),
    Regex(regex::Error),
    Parse(String),
}

impl fmt::Display for LicenseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            LicenseError::IO(ref err) => err.fmt(f),
            LicenseError::Regex(ref err) => err.fmt(f),
            LicenseError::Parse(ref err) => write!(f, "parsing failed, {}", err),
        }
    }
}

impl From<io::Error> for LicenseError {
    fn from(err: io::Error) -> LicenseError {
        LicenseError::IO(err)
    }
}

impl From<regex::Error> for LicenseError {
    fn from(err: regex::Error) -> LicenseError {
        LicenseError::Regex(err)
    }
}

// the template is parsed using a state machine
enum ParsingState {
    Lit,
    LitEsc,
    // the u32 keeps track of brace nesting
    Re(u32),
    ReEsc(u32),
    Abort(String),
}

use self::ParsingState::*;

pub struct TemplateParser {
    parsed: String,
    buffer: String,
    state: ParsingState,
    linum: u32,
    open_brace_line: u32,
}

impl TemplateParser {
    fn new() -> Self {
        Self {
            parsed: "^".to_owned(),
            buffer: String::new(),
            state: Lit,
            linum: 1,
            // keeps track of last line on which a regex placeholder was started
            open_brace_line: 0,
        }
    }

    /// Convert a license template into a string which can be turned into a regex.
    ///
    /// The license template could use regex syntax directly, but that would require a lot of manual
    /// escaping, which is inconvenient. It is therefore literal by default, with optional regex
    /// subparts delimited by `{` and `}`. Additionally:
    ///
    /// - to insert literal `{`, `}` or `\`, escape it with `\`
    /// - an empty regex placeholder (`{}`) is shorthand for `{.*?}`
    ///
    /// This function parses this input format and builds a properly escaped *string* representation
    /// of the equivalent regular expression. It **does not** however guarantee that the returned
    /// string is a syntactically valid regular expression.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rustfmt_config::license::TemplateParser;
    /// assert_eq!(
    ///     TemplateParser::parse(
    ///         r"
    /// // Copyright {\d+} The \} Rust \\ Project \{ Developers. See the {([A-Z]+)}
    /// // file at the top-level directory of this distribution and at
    /// // {}.
    /// //
    /// // 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.
    /// "
    ///     ).unwrap(),
    ///     r"^
    /// // Copyright \d+ The \} Rust \\ Project \{ Developers\. See the ([A-Z]+)
    /// // file at the top\-level directory of this distribution and at
    /// // .*?\.
    /// //
    /// // 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\.
    /// "
    /// );
    /// ```
    pub fn parse(template: &str) -> Result<String, LicenseError> {
        let mut parser = Self::new();
        for chr in template.chars() {
            if chr == '\n' {
                parser.linum += 1;
            }
            parser.state = match parser.state {
                Lit => parser.trans_from_lit(chr),
                LitEsc => parser.trans_from_litesc(chr),
                Re(brace_nesting) => parser.trans_from_re(chr, brace_nesting),
                ReEsc(brace_nesting) => parser.trans_from_reesc(chr, brace_nesting),
                Abort(msg) => return Err(LicenseError::Parse(msg)),
            };
        }
        // check if we've ended parsing in a valid state
        match parser.state {
            Abort(msg) => return Err(LicenseError::Parse(msg)),
            Re(_) | ReEsc(_) => {
                return Err(LicenseError::Parse(format!(
                    "escape or balance opening brace on l. {}",
                    parser.open_brace_line
                )));
            }
            LitEsc => {
                return Err(LicenseError::Parse(format!(
                    "incomplete escape sequence on l. {}",
                    parser.linum
                )))
            }
            _ => (),
        }
        parser.parsed.push_str(&regex::escape(&parser.buffer));

        Ok(parser.parsed)
    }

    fn trans_from_lit(&mut self, chr: char) -> ParsingState {
        match chr {
            '{' => {
                self.parsed.push_str(&regex::escape(&self.buffer));
                self.buffer.clear();
                self.open_brace_line = self.linum;
                Re(1)
            }
            '}' => Abort(format!(
                "escape or balance closing brace on l. {}",
                self.linum
            )),
            '\\' => LitEsc,
            _ => {
                self.buffer.push(chr);
                Lit
            }
        }
    }

    fn trans_from_litesc(&mut self, chr: char) -> ParsingState {
        self.buffer.push(chr);
        Lit
    }

    fn trans_from_re(&mut self, chr: char, brace_nesting: u32) -> ParsingState {
        match chr {
            '{' => {
                self.buffer.push(chr);
                Re(brace_nesting + 1)
            }
            '}' => {
                match brace_nesting {
                    1 => {
                        // default regex for empty placeholder {}
                        if self.buffer.is_empty() {
                            self.parsed.push_str(".*?");
                        } else {
                            self.parsed.push_str(&self.buffer);
                        }
                        self.buffer.clear();
                        Lit
                    }
                    _ => {
                        self.buffer.push(chr);
                        Re(brace_nesting - 1)
                    }
                }
            }
            '\\' => {
                self.buffer.push(chr);
                ReEsc(brace_nesting)
            }
            _ => {
                self.buffer.push(chr);
                Re(brace_nesting)
            }
        }
    }

    fn trans_from_reesc(&mut self, chr: char, brace_nesting: u32) -> ParsingState {
        self.buffer.push(chr);
        Re(brace_nesting)
    }
}

pub fn load_and_compile_template(path: &str) -> Result<Regex, LicenseError> {
    let mut lt_file = File::open(&path)?;
    let mut lt_str = String::new();
    lt_file.read_to_string(&mut lt_str)?;
    let lt_parsed = TemplateParser::parse(&lt_str)?;
    Ok(Regex::new(&lt_parsed)?)
}

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

    #[test]
    fn test_parse_license_template() {
        assert_eq!(
            TemplateParser::parse("literal (.*)").unwrap(),
            r"^literal \(\.\*\)"
        );
        assert_eq!(
            TemplateParser::parse(r"escaping \}").unwrap(),
            r"^escaping \}"
        );
        assert!(TemplateParser::parse("unbalanced } without escape").is_err());
        assert_eq!(
            TemplateParser::parse(r"{\d+} place{-?}holder{s?}").unwrap(),
            r"^\d+ place-?holders?"
        );
        assert_eq!(TemplateParser::parse("default {}").unwrap(), "^default .*?");
        assert_eq!(
            TemplateParser::parse(r"unbalanced nested braces {\{{3}}").unwrap(),
            r"^unbalanced nested braces \{{3}"
        );
        assert_eq!(
            &TemplateParser::parse("parsing error }")
                .unwrap_err()
                .to_string(),
            "parsing failed, escape or balance closing brace on l. 1"
        );
        assert_eq!(
            &TemplateParser::parse("parsing error {\nsecond line")
                .unwrap_err()
                .to_string(),
            "parsing failed, escape or balance opening brace on l. 1"
        );
        assert_eq!(
            &TemplateParser::parse(r"parsing error \")
                .unwrap_err()
                .to_string(),
            "parsing failed, incomplete escape sequence on l. 1"
        );
    }
}
<commit_msg>Make license doctest pass again<commit_after>use std::io;
use std::fmt;
use std::fs::File;
use std::io::Read;

use regex;
use regex::Regex;

#[derive(Debug)]
pub enum LicenseError {
    IO(io::Error),
    Regex(regex::Error),
    Parse(String),
}

impl fmt::Display for LicenseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            LicenseError::IO(ref err) => err.fmt(f),
            LicenseError::Regex(ref err) => err.fmt(f),
            LicenseError::Parse(ref err) => write!(f, "parsing failed, {}", err),
        }
    }
}

impl From<io::Error> for LicenseError {
    fn from(err: io::Error) -> LicenseError {
        LicenseError::IO(err)
    }
}

impl From<regex::Error> for LicenseError {
    fn from(err: regex::Error) -> LicenseError {
        LicenseError::Regex(err)
    }
}

// the template is parsed using a state machine
enum ParsingState {
    Lit,
    LitEsc,
    // the u32 keeps track of brace nesting
    Re(u32),
    ReEsc(u32),
    Abort(String),
}

use self::ParsingState::*;

pub struct TemplateParser {
    parsed: String,
    buffer: String,
    state: ParsingState,
    linum: u32,
    open_brace_line: u32,
}

impl TemplateParser {
    fn new() -> Self {
        Self {
            parsed: "^".to_owned(),
            buffer: String::new(),
            state: Lit,
            linum: 1,
            // keeps track of last line on which a regex placeholder was started
            open_brace_line: 0,
        }
    }

    /// Convert a license template into a string which can be turned into a regex.
    ///
    /// The license template could use regex syntax directly, but that would require a lot of manual
    /// escaping, which is inconvenient. It is therefore literal by default, with optional regex
    /// subparts delimited by `{` and `}`. Additionally:
    ///
    /// - to insert literal `{`, `}` or `\`, escape it with `\`
    /// - an empty regex placeholder (`{}`) is shorthand for `{.*?}`
    ///
    /// This function parses this input format and builds a properly escaped *string* representation
    /// of the equivalent regular expression. It **does not** however guarantee that the returned
    /// string is a syntactically valid regular expression.
    ///
    /// # Examples
    ///
    /// ```
    /// # use rustfmt_nightly::config::license::TemplateParser;
    /// assert_eq!(
    ///     TemplateParser::parse(
    ///         r"
    /// // Copyright {\d+} The \} Rust \\ Project \{ Developers. See the {([A-Z]+)}
    /// // file at the top-level directory of this distribution and at
    /// // {}.
    /// //
    /// // 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.
    /// "
    ///     ).unwrap(),
    ///     r"^
    /// // Copyright \d+ The \} Rust \\ Project \{ Developers\. See the ([A-Z]+)
    /// // file at the top\-level directory of this distribution and at
    /// // .*?\.
    /// //
    /// // 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\.
    /// "
    /// );
    /// ```
    pub fn parse(template: &str) -> Result<String, LicenseError> {
        let mut parser = Self::new();
        for chr in template.chars() {
            if chr == '\n' {
                parser.linum += 1;
            }
            parser.state = match parser.state {
                Lit => parser.trans_from_lit(chr),
                LitEsc => parser.trans_from_litesc(chr),
                Re(brace_nesting) => parser.trans_from_re(chr, brace_nesting),
                ReEsc(brace_nesting) => parser.trans_from_reesc(chr, brace_nesting),
                Abort(msg) => return Err(LicenseError::Parse(msg)),
            };
        }
        // check if we've ended parsing in a valid state
        match parser.state {
            Abort(msg) => return Err(LicenseError::Parse(msg)),
            Re(_) | ReEsc(_) => {
                return Err(LicenseError::Parse(format!(
                    "escape or balance opening brace on l. {}",
                    parser.open_brace_line
                )));
            }
            LitEsc => {
                return Err(LicenseError::Parse(format!(
                    "incomplete escape sequence on l. {}",
                    parser.linum
                )))
            }
            _ => (),
        }
        parser.parsed.push_str(&regex::escape(&parser.buffer));

        Ok(parser.parsed)
    }

    fn trans_from_lit(&mut self, chr: char) -> ParsingState {
        match chr {
            '{' => {
                self.parsed.push_str(&regex::escape(&self.buffer));
                self.buffer.clear();
                self.open_brace_line = self.linum;
                Re(1)
            }
            '}' => Abort(format!(
                "escape or balance closing brace on l. {}",
                self.linum
            )),
            '\\' => LitEsc,
            _ => {
                self.buffer.push(chr);
                Lit
            }
        }
    }

    fn trans_from_litesc(&mut self, chr: char) -> ParsingState {
        self.buffer.push(chr);
        Lit
    }

    fn trans_from_re(&mut self, chr: char, brace_nesting: u32) -> ParsingState {
        match chr {
            '{' => {
                self.buffer.push(chr);
                Re(brace_nesting + 1)
            }
            '}' => {
                match brace_nesting {
                    1 => {
                        // default regex for empty placeholder {}
                        if self.buffer.is_empty() {
                            self.parsed.push_str(".*?");
                        } else {
                            self.parsed.push_str(&self.buffer);
                        }
                        self.buffer.clear();
                        Lit
                    }
                    _ => {
                        self.buffer.push(chr);
                        Re(brace_nesting - 1)
                    }
                }
            }
            '\\' => {
                self.buffer.push(chr);
                ReEsc(brace_nesting)
            }
            _ => {
                self.buffer.push(chr);
                Re(brace_nesting)
            }
        }
    }

    fn trans_from_reesc(&mut self, chr: char, brace_nesting: u32) -> ParsingState {
        self.buffer.push(chr);
        Re(brace_nesting)
    }
}

pub fn load_and_compile_template(path: &str) -> Result<Regex, LicenseError> {
    let mut lt_file = File::open(&path)?;
    let mut lt_str = String::new();
    lt_file.read_to_string(&mut lt_str)?;
    let lt_parsed = TemplateParser::parse(&lt_str)?;
    Ok(Regex::new(&lt_parsed)?)
}

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

    #[test]
    fn test_parse_license_template() {
        assert_eq!(
            TemplateParser::parse("literal (.*)").unwrap(),
            r"^literal \(\.\*\)"
        );
        assert_eq!(
            TemplateParser::parse(r"escaping \}").unwrap(),
            r"^escaping \}"
        );
        assert!(TemplateParser::parse("unbalanced } without escape").is_err());
        assert_eq!(
            TemplateParser::parse(r"{\d+} place{-?}holder{s?}").unwrap(),
            r"^\d+ place-?holders?"
        );
        assert_eq!(TemplateParser::parse("default {}").unwrap(), "^default .*?");
        assert_eq!(
            TemplateParser::parse(r"unbalanced nested braces {\{{3}}").unwrap(),
            r"^unbalanced nested braces \{{3}"
        );
        assert_eq!(
            &TemplateParser::parse("parsing error }")
                .unwrap_err()
                .to_string(),
            "parsing failed, escape or balance closing brace on l. 1"
        );
        assert_eq!(
            &TemplateParser::parse("parsing error {\nsecond line")
                .unwrap_err()
                .to_string(),
            "parsing failed, escape or balance opening brace on l. 1"
        );
        assert_eq!(
            &TemplateParser::parse(r"parsing error \")
                .unwrap_err()
                .to_string(),
            "parsing failed, incomplete escape sequence on l. 1"
        );
    }
}
<|endoftext|>


<commit_before>use satyrs::cnf::{CNF, Assignment, PartialAssignment};

/**
 * DPLL Algorithm implementation
 * Start by cloning the cnf so we can modify the HashMap
 * Clone might be slow, consider a faster option
 * TODO: Consider using HashSet instead of Vec for the clauses
 */
#[allow(non_snake_case)]
pub fn DPLL(cnf: &CNF) -> Option<Assignment> {
	//let _cnf = cnf.clone();
    //println!("DPLL {:?}", _cnf);
    let p_assn = PartialAssignment::new(cnf.nvar as usize);
    match _dpll(cnf, p_assn){
        Some(assn) => Some(assn.assignment.iter().map(|a| a.unwrap()).collect()),
        None => None
    }
}

#[allow(unused_variables)]
fn _dpll(cnf: &CNF, mut p_assn: PartialAssignment) -> Option<PartialAssignment> {
    // If consistent set of literals, return True
    if cnf.clauses.is_empty() {
        return Some(p_assn)  // Display optional value
    }
    for unit in &cnf.units {
		let clause = cnf.clauses.get(&unit).expect("Clause not found");
		let lit = clause[0];
		p_assn.assign_literal(lit);
		// cnf.unit_propogate(unit); // Only propogate in the clone
    }

    // If contains an empty clause return False
    // For every unit-clause, unit-propogate
    // For ever pure literal, pure literal assign
    // Choose literal L for split
    // Return DPLL with L and -L

    Some(p_assn)
}
<commit_msg>propogate uncommented and ready4Action<commit_after>use satyrs::cnf::{CNF, Assignment, PartialAssignment};

/**
 * DPLL Algorithm implementation
 * Start by cloning the cnf so we can modify the HashMap
 * Clone might be slow, consider a faster option
 * TODO: Consider using HashSet instead of Vec for the clauses
 */
#[allow(non_snake_case)]
pub fn DPLL(cnf: &CNF) -> Option<Assignment> {
	//let _cnf = cnf.clone();
    //println!("DPLL {:?}", _cnf);
    let p_assn = PartialAssignment::new(cnf.nvar as usize);
    match _dpll(cnf, p_assn){
        Some(assn) => Some(assn.assignment.iter().map(|a| a.unwrap()).collect()),
        None => None
    }
}

#[allow(unused_variables)]
fn _dpll(cnf: &CNF, mut p_assn: PartialAssignment) -> Option<PartialAssignment> {
    // If consistent set of literals, return True
    if cnf.clauses.is_empty() {
        return Some(p_assn)  // Display optional value
    }
    for unit in &cnf.units {
		let clause = cnf.clauses.get(&unit).expect("Clause not found");
		let lit = clause[0];
		p_assn.assign_literal(lit);
		cnf.unit_propogate(unit); // Only propogate in the clone
    }

    // If contains an empty clause return False
    // For every unit-clause, unit-propogate
    // For ever pure literal, pure literal assign
    // Choose literal L for split
    // Return DPLL with L and -L

    Some(p_assn)
}
<|endoftext|>