<commit_before>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.
use std::io::prelude::*;
use std::str::FromStr;
use hex::FromHex;

pub struct Url {
    pub scheme: String,
    pub user: Option<UserInfo>,
    pub host: String,
    pub port: Option<u16>,
    pub path: Path,
}

pub struct Path {
    pub path: String,
    pub query: Query,
    pub fragment: Option<String>,
}

pub struct UserInfo {
    pub user: String,
    pub pass: Option<String>,
}

pub type Query = Vec<(String, String)>;

impl Url {
    pub fn new(scheme: String,
               user: Option<UserInfo>,
               host: String,
               port: Option<u16>,
               path: String,
               query: Query,
               fragment: Option<String>)
               -> Url {
        Url {
            scheme: scheme,
            user: user,
            host: host,
            port: port,
            path: Path::new(path, query, fragment),
        }
    }

    pub fn parse(rawurl: &str) -> DecodeResult<Url> {
        // scheme
        let (scheme, rest) = try!(get_scheme(rawurl));

        // authority
        let (userinfo, host, port, rest) = try!(get_authority(rest));

        // path
        let has_authority = !host.is_empty();
        let (path, rest) = try!(get_path(rest, has_authority));

        // query and fragment
        let (query, fragment) = try!(get_query_fragment(rest));

        let url = Url::new(scheme.to_owned(),
                           userinfo,
                           host.to_owned(),
                           port,
                           path,
                           query,
                           fragment);
        Ok(url)
    }
}

impl Path {
    pub fn new(path: String, query: Query, fragment: Option<String>) -> Path {
        Path {
            path: path,
            query: query,
            fragment: fragment,
        }
    }

    pub fn parse(rawpath: &str) -> DecodeResult<Path> {
        let (path, rest) = try!(get_path(rawpath, false));

        // query and fragment
        let (query, fragment) = try!(get_query_fragment(&rest));

        Ok(Path {
            path: path,
            query: query,
            fragment: fragment,
        })
    }
}

impl UserInfo {
    #[inline]
    pub fn new(user: String, pass: Option<String>) -> UserInfo {
        UserInfo {
            user: user,
            pass: pass,
        }
    }
}

pub type DecodeResult<T> = Result<T, String>;

pub fn decode_component(container: &str) -> DecodeResult<String> {
    decode_inner(container, false)
}

fn decode_inner(c: &str, full_url: bool) -> DecodeResult<String> {
    let mut out = String::new();
    let mut iter = c.as_bytes().iter().cloned();

    loop {
        match iter.next() {
            Some(b) => {
                match b as char {
                    '%' => {
                        let bytes = match (iter.next(), iter.next()) {
                            (Some(one), Some(two)) => [one, two],
                            _ => {
                                return Err("Malformed input: found '%' without two \
                                                    trailing bytes".to_owned())
                            }
                        };

                        // Only decode some characters if full_url:
                        match Vec::<u8>::from_hex(&bytes)
                                  .unwrap()[0] as char {
                            // gen-delims:
                            ':' |
                            '/' |
                            '?' |
                            '#' |
                            '[' |
                            ']' |
                            '@' |
                            '!' |
                            '$' |
                            '&' |
                            '"' |
                            '(' |
                            ')' |
                            '*' |
                            '+' |
                            ',' |
                            ';' |
                            '=' if full_url => {
                                out.push('%');
                                out.push(bytes[0] as char);
                                out.push(bytes[1] as char);
                            }

                            ch => out.push(ch),
                        }
                    }
                    ch => out.push(ch),
                }
            }
            None => return Ok(out),
        }
    }
}

fn split_char_first(s: &str, c: char) -> (&str, &str) {
    let mut iter = s.splitn(2, c);

    match (iter.next(), iter.next()) {
        (Some(a), Some(b)) => (a, b),
        (Some(a), None) => (a, ""),
        (None, _) => unreachable!(),
    }
}

fn query_from_str(rawquery: &str) -> DecodeResult<Query> {
    let mut query: Query = vec![];
    if !rawquery.is_empty() {
        for p in rawquery.split('&') {
            let (k, v) = split_char_first(p, '=');
            query.push((try!(decode_component(k)), try!(decode_component(v))));
        }
    }

    Ok(query)
}

pub fn get_scheme(rawurl: &str) -> DecodeResult<(&str, &str)> {
    for (i, c) in rawurl.chars().enumerate() {
        let result = match c {
            'A'...'Z' |
            'a'...'z' => continue,
            '0'...'9' | '+' | '-' | '.' => {
                if i != 0 {
                    continue;
                }

                Err("url: Scheme must begin with a letter.".to_owned())
            }
            ':' => {
                if i == 0 {
                    Err("url: Scheme cannot be empty.".to_owned())
                } else {
                    Ok((&rawurl[0..i], &rawurl[i + 1..rawurl.len()]))
                }
            }
            _ => Err("url: Invalid character in scheme.".to_owned()),
        };

        return result;
    }

    Err("url: Scheme must be terminated with a colon.".to_owned())
}

// returns userinfo, host, port, and unparsed part, or an error
fn get_authority(rawurl: &str) -> DecodeResult<(Option<UserInfo>, &str, Option<u16>, &str)> {
    enum State {
        Start, // starting state
        PassHostPort, // could be in user or port
        Ip6Port, // either in ipv6 host or port
        Ip6Host, // are in an ipv6 host
        InHost, // are in a host - may be ipv6, but don't know yet
        InPort, // are in port
    }

    #[derive(Clone, PartialEq)]
    enum Input {
        Digit, // all digits
        Hex, // digits and letters a-f
        Unreserved, // all other legal characters
    }

    if !rawurl.starts_with("//") {
        // there is no authority.
        return Ok((None, "", None, rawurl));
    }

    let len = rawurl.len();
    let mut st = State::Start;
    let mut input = Input::Digit; // most restricted, start here.

    let mut userinfo = None;
    let mut host = "";
    let mut port = None;

    let mut colon_count = 0usize;
    let mut pos = 0;
    let mut begin = 2;
    let mut end = len;

    for (i, c) in rawurl.chars()
                        .enumerate()
                        .skip(2) {
        // deal with input class first
        match c {
            '0'...'9' => (),
            'A'...'F' |
            'a'...'f' => {
                if input == Input::Digit {
                    input = Input::Hex;
                }
            }
            'G'...'Z' |
            'g'...'z' |
            '-' |
            '.' |
            '_' |
            '~' |
            '%' |
            '&' |
            '\'' |
            '(' |
            ')' |
            '+' |
            '!' |
            '*' |
            ',' |
            ';' |
            '=' => input = Input::Unreserved,
            ':' | '@' | '?' | '#' | '/' => {
                // separators, don't change anything
            }
            _ => return Err("Illegal character in authority".to_owned()),
        }

        // now process states
        match c {
            ':' => {
                colon_count += 1;
                match st {
                    State::Start => {
                        pos = i;
                        st = State::PassHostPort;
                    }
                    State::PassHostPort => {
                        // multiple colons means ipv6 address.
                        if input == Input::Unreserved {
                            return Err("Illegal characters in IPv6 address.".to_owned());
                        }
                        st = State::Ip6Host;
                    }
                    State::InHost => {
                        pos = i;
                        if input == Input::Unreserved {
                            // must be port
                            host = &rawurl[begin..i];
                            st = State::InPort;
                        } else {
                            // can't be sure whether this is an ipv6 address or a port
                            st = State::Ip6Port;
                        }
                    }
                    State::Ip6Port => {
                        if input == Input::Unreserved {
                            return Err("Illegal characters in authority.".to_owned());
                        }
                        st = State::Ip6Host;
                    }
                    State::Ip6Host => {
                        if colon_count > 7 {
                            host = &rawurl[begin..i];
                            pos = i;
                            st = State::InPort;
                        }
                    }
                    _ => return Err("Invalid ':' in authority.".to_owned()),
                }
                input = Input::Digit; // reset input class
            }

            '@' => {
                input = Input::Digit; // reset input class
                colon_count = 0; // reset count
                match st {
                    State::Start => {
                        let user = try!(decode_component(&rawurl[begin..i]));
                        userinfo = Some(UserInfo::new(user, None));
                        st = State::InHost;
                    }
                    State::PassHostPort => {
                        let user = try!(decode_component(&rawurl[begin..pos]));
                        let pass = try!(decode_component(&rawurl[pos + 1..i]));
                        userinfo = Some(UserInfo::new(user, Some(pass)));
                        st = State::InHost;
                    }
                    _ => return Err("Invalid '@' in authority.".to_owned()),
                }
                begin = i + 1;
            }

            '?' | '#' | '/' => {
                end = i;
                break;
            }
            _ => (),
        }
    }

    // finish up
    match st {
        State::PassHostPort |
        State::Ip6Port => {
            if input != Input::Digit {
                return Err("Non-digit characters in port.".to_owned());
            }
            host = &rawurl[begin..pos];
            port = Some(&rawurl[pos + 1..end]);
        }
        State::Ip6Host |
        State::InHost |
        State::Start => host = &rawurl[begin..end],
        State::InPort => {
            if input != Input::Digit {
                return Err("Non-digit characters in port.".to_owned());
            }
            port = Some(&rawurl[pos + 1..end]);
        }
    }

    let rest = &rawurl[end..len];
    // If we have a port string, ensure it parses to u16.
    let port = match port {
        None => None,
        opt => {
            match opt.and_then(|p| FromStr::from_str(p).ok()) {
                None => return Err(format!("Failed to parse port: {:?}", port)),
                opt => opt,
            }
        }
    };

    Ok((userinfo, host, port, rest))
}

// returns the path and unparsed part of url, or an error
fn get_path(rawurl: &str, is_authority: bool) -> DecodeResult<(String, &str)> {
    let len = rawurl.len();
    let mut end = len;
    for (i, c) in rawurl.chars().enumerate() {
        match c {
            'A'...'Z' |
            'a'...'z' |
            '0'...'9' |
            '&' |
            '\'' |
            '(' |
            ')' |
            '.' |
            '@' |
            ':' |
            '%' |
            '/' |
            '+' |
            '!' |
            '*' |
            ',' |
            ';' |
            '=' |
            '_' |
            '-' |
            '~' => continue,
            '?' | '#' => {
                end = i;
                break;
            }
            _ => return Err("Invalid character in path.".to_owned()),
        }
    }

    if is_authority && end != 0 && !rawurl.starts_with('/') {
        Err("Non-empty path must begin with '/' in presence of authority.".to_owned())
    } else {
        Ok((try!(decode_component(&rawurl[0..end])), &rawurl[end..len]))
    }
}

// returns the parsed query and the fragment, if present
fn get_query_fragment(rawurl: &str) -> DecodeResult<(Query, Option<String>)> {
    let (before_fragment, raw_fragment) = split_char_first(rawurl, '#');

    // Parse the fragment if available
    let fragment = match raw_fragment {
        "" => None,
        raw => Some(try!(decode_component(raw))),
    };

    match before_fragment.chars().next() {
        Some('?') => Ok((try!(query_from_str(&before_fragment[1..])), fragment)),
        None => Ok((vec![], fragment)),
        _ => Err(format!("Query didn't start with '?': '{}..'", before_fragment)),
    }
}

impl FromStr for Url {
    type Err = String;
    fn from_str(s: &str) -> Result<Url, String> {
        Url::parse(s)
    }
}

impl FromStr for Path {
    type Err = String;
    fn from_str(s: &str) -> Result<Path, String> {
        Path::parse(s)
    }
}
<commit_msg>Style<commit_after>// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.
use std::io::prelude::*;
use std::str::FromStr;
use hex::FromHex;

pub struct Url {
    pub scheme: String,
    pub user: Option<UserInfo>,
    pub host: String,
    pub port: Option<u16>,
    pub path: Path,
}

pub struct Path {
    pub path: String,
    pub query: Query,
    pub fragment: Option<String>,
}

pub struct UserInfo {
    pub user: String,
    pub pass: Option<String>,
}

pub type Query = Vec<(String, String)>;

impl Url {
    pub fn new(scheme: String,
               user: Option<UserInfo>,
               host: String,
               port: Option<u16>,
               path: String,
               query: Query,
               fragment: Option<String>)
               -> Url {
        Url {
            scheme: scheme,
            user: user,
            host: host,
            port: port,
            path: Path::new(path, query, fragment),
        }
    }

    pub fn parse(rawurl: &str) -> DecodeResult<Url> {
        // scheme
        let (scheme, rest) = try!(get_scheme(rawurl));

        // authority
        let (userinfo, host, port, rest) = try!(get_authority(rest));

        // path
        let has_authority = !host.is_empty();
        let (path, rest) = try!(get_path(rest, has_authority));

        // query and fragment
        let (query, fragment) = try!(get_query_fragment(rest));

        let url = Url::new(scheme.to_owned(),
                           userinfo,
                           host.to_owned(),
                           port,
                           path,
                           query,
                           fragment);
        Ok(url)
    }
}

impl Path {
    pub fn new(path: String, query: Query, fragment: Option<String>) -> Path {
        Path {
            path: path,
            query: query,
            fragment: fragment,
        }
    }

    pub fn parse(rawpath: &str) -> DecodeResult<Path> {
        let (path, rest) = try!(get_path(rawpath, false));

        // query and fragment
        let (query, fragment) = try!(get_query_fragment(&rest));

        Ok(Path {
            path: path,
            query: query,
            fragment: fragment,
        })
    }
}

impl UserInfo {
    #[inline]
    pub fn new(user: String, pass: Option<String>) -> UserInfo {
        UserInfo {
            user: user,
            pass: pass,
        }
    }
}

pub type DecodeResult<T> = Result<T, String>;

pub fn decode_component(container: &str) -> DecodeResult<String> {
    decode_inner(container, false)
}

fn decode_inner(c: &str, full_url: bool) -> DecodeResult<String> {
    let mut out = String::new();
    let mut iter = c.as_bytes().iter().cloned();

    loop {
        match iter.next() {
            Some(b) => {
                match b as char {
                    '%' => {
                        let bytes = match (iter.next(), iter.next()) {
                            (Some(one), Some(two)) => [one, two],
                            _ => {
                                return Err("Malformed input: found '%' without two \
                                                    trailing bytes".to_owned())
                            }
                        };

                        // Only decode some characters if full_url:
                        match Vec::<u8>::from_hex(&bytes).unwrap()[0] as char {
                            // gen-delims:
                            ':' |
                            '/' |
                            '?' |
                            '#' |
                            '[' |
                            ']' |
                            '@' |
                            '!' |
                            '$' |
                            '&' |
                            '"' |
                            '(' |
                            ')' |
                            '*' |
                            '+' |
                            ',' |
                            ';' |
                            '=' if full_url => {
                                out.push('%');
                                out.push(bytes[0] as char);
                                out.push(bytes[1] as char);
                            }

                            ch => out.push(ch),
                        }
                    }
                    ch => out.push(ch),
                }
            }
            None => return Ok(out),
        }
    }
}

fn split_char_first(s: &str, c: char) -> (&str, &str) {
    let mut iter = s.splitn(2, c);

    match (iter.next(), iter.next()) {
        (Some(a), Some(b)) => (a, b),
        (Some(a), None) => (a, ""),
        (None, _) => unreachable!(),
    }
}

fn query_from_str(rawquery: &str) -> DecodeResult<Query> {
    let mut query: Query = vec![];
    if !rawquery.is_empty() {
        for p in rawquery.split('&') {
            let (k, v) = split_char_first(p, '=');
            query.push((try!(decode_component(k)), try!(decode_component(v))));
        }
    }

    Ok(query)
}

pub fn get_scheme(rawurl: &str) -> DecodeResult<(&str, &str)> {
    for (i, c) in rawurl.chars().enumerate() {
        let result = match c {
            'A'...'Z' |
            'a'...'z' => continue,
            '0'...'9' | '+' | '-' | '.' => {
                if i != 0 {
                    continue;
                }

                Err("url: Scheme must begin with a letter.".to_owned())
            }
            ':' => {
                if i == 0 {
                    Err("url: Scheme cannot be empty.".to_owned())
                } else {
                    Ok((&rawurl[0..i], &rawurl[i + 1..rawurl.len()]))
                }
            }
            _ => Err("url: Invalid character in scheme.".to_owned()),
        };

        return result;
    }

    Err("url: Scheme must be terminated with a colon.".to_owned())
}

// returns userinfo, host, port, and unparsed part, or an error
fn get_authority(rawurl: &str) -> DecodeResult<(Option<UserInfo>, &str, Option<u16>, &str)> {
    enum State {
        Start, // starting state
        PassHostPort, // could be in user or port
        Ip6Port, // either in ipv6 host or port
        Ip6Host, // are in an ipv6 host
        InHost, // are in a host - may be ipv6, but don't know yet
        InPort, // are in port
    }

    #[derive(Clone, PartialEq)]
    enum Input {
        Digit, // all digits
        Hex, // digits and letters a-f
        Unreserved, // all other legal characters
    }

    if !rawurl.starts_with("//") {
        // there is no authority.
        return Ok((None, "", None, rawurl));
    }

    let len = rawurl.len();
    let mut st = State::Start;
    let mut input = Input::Digit; // most restricted, start here.

    let mut userinfo = None;
    let mut host = "";
    let mut port = None;

    let mut colon_count = 0usize;
    let mut pos = 0;
    let mut begin = 2;
    let mut end = len;

    for (i, c) in rawurl.chars()
                        .enumerate()
                        .skip(2) {
        // deal with input class first
        match c {
            '0'...'9' => (),
            'A'...'F' |
            'a'...'f' => {
                if input == Input::Digit {
                    input = Input::Hex;
                }
            }
            'G'...'Z' |
            'g'...'z' |
            '-' |
            '.' |
            '_' |
            '~' |
            '%' |
            '&' |
            '\'' |
            '(' |
            ')' |
            '+' |
            '!' |
            '*' |
            ',' |
            ';' |
            '=' => input = Input::Unreserved,
            ':' | '@' | '?' | '#' | '/' => {
                // separators, don't change anything
            }
            _ => return Err("Illegal character in authority".to_owned()),
        }

        // now process states
        match c {
            ':' => {
                colon_count += 1;
                match st {
                    State::Start => {
                        pos = i;
                        st = State::PassHostPort;
                    }
                    State::PassHostPort => {
                        // multiple colons means ipv6 address.
                        if input == Input::Unreserved {
                            return Err("Illegal characters in IPv6 address.".to_owned());
                        }
                        st = State::Ip6Host;
                    }
                    State::InHost => {
                        pos = i;
                        if input == Input::Unreserved {
                            // must be port
                            host = &rawurl[begin..i];
                            st = State::InPort;
                        } else {
                            // can't be sure whether this is an ipv6 address or a port
                            st = State::Ip6Port;
                        }
                    }
                    State::Ip6Port => {
                        if input == Input::Unreserved {
                            return Err("Illegal characters in authority.".to_owned());
                        }
                        st = State::Ip6Host;
                    }
                    State::Ip6Host => {
                        if colon_count > 7 {
                            host = &rawurl[begin..i];
                            pos = i;
                            st = State::InPort;
                        }
                    }
                    _ => return Err("Invalid ':' in authority.".to_owned()),
                }
                input = Input::Digit; // reset input class
            }

            '@' => {
                input = Input::Digit; // reset input class
                colon_count = 0; // reset count
                match st {
                    State::Start => {
                        let user = try!(decode_component(&rawurl[begin..i]));
                        userinfo = Some(UserInfo::new(user, None));
                        st = State::InHost;
                    }
                    State::PassHostPort => {
                        let user = try!(decode_component(&rawurl[begin..pos]));
                        let pass = try!(decode_component(&rawurl[pos + 1..i]));
                        userinfo = Some(UserInfo::new(user, Some(pass)));
                        st = State::InHost;
                    }
                    _ => return Err("Invalid '@' in authority.".to_owned()),
                }
                begin = i + 1;
            }

            '?' | '#' | '/' => {
                end = i;
                break;
            }
            _ => (),
        }
    }

    // finish up
    match st {
        State::PassHostPort |
        State::Ip6Port => {
            if input != Input::Digit {
                return Err("Non-digit characters in port.".to_owned());
            }
            host = &rawurl[begin..pos];
            port = Some(&rawurl[pos + 1..end]);
        }
        State::Ip6Host |
        State::InHost |
        State::Start => host = &rawurl[begin..end],
        State::InPort => {
            if input != Input::Digit {
                return Err("Non-digit characters in port.".to_owned());
            }
            port = Some(&rawurl[pos + 1..end]);
        }
    }

    let rest = &rawurl[end..len];
    // If we have a port string, ensure it parses to u16.
    let port = match port {
        None => None,
        opt => {
            match opt.and_then(|p| FromStr::from_str(p).ok()) {
                None => return Err(format!("Failed to parse port: {:?}", port)),
                opt => opt,
            }
        }
    };

    Ok((userinfo, host, port, rest))
}

// returns the path and unparsed part of url, or an error
fn get_path(rawurl: &str, is_authority: bool) -> DecodeResult<(String, &str)> {
    let len = rawurl.len();
    let mut end = len;
    for (i, c) in rawurl.chars().enumerate() {
        match c {
            'A'...'Z' |
            'a'...'z' |
            '0'...'9' |
            '&' |
            '\'' |
            '(' |
            ')' |
            '.' |
            '@' |
            ':' |
            '%' |
            '/' |
            '+' |
            '!' |
            '*' |
            ',' |
            ';' |
            '=' |
            '_' |
            '-' |
            '~' => continue,
            '?' | '#' => {
                end = i;
                break;
            }
            _ => return Err("Invalid character in path.".to_owned()),
        }
    }

    if is_authority && end != 0 && !rawurl.starts_with('/') {
        Err("Non-empty path must begin with '/' in presence of authority.".to_owned())
    } else {
        Ok((try!(decode_component(&rawurl[0..end])), &rawurl[end..len]))
    }
}

// returns the parsed query and the fragment, if present
fn get_query_fragment(rawurl: &str) -> DecodeResult<(Query, Option<String>)> {
    let (before_fragment, raw_fragment) = split_char_first(rawurl, '#');

    // Parse the fragment if available
    let fragment = match raw_fragment {
        "" => None,
        raw => Some(try!(decode_component(raw))),
    };

    match before_fragment.chars().next() {
        Some('?') => Ok((try!(query_from_str(&before_fragment[1..])), fragment)),
        None => Ok((vec![], fragment)),
        _ => Err(format!("Query didn't start with '?': '{}..'", before_fragment)),
    }
}

impl FromStr for Url {
    type Err = String;
    fn from_str(s: &str) -> Result<Url, String> {
        Url::parse(s)
    }
}

impl FromStr for Path {
    type Err = String;
    fn from_str(s: &str) -> Result<Path, String> {
        Path::parse(s)
    }
}
<|endoftext|>


<commit_before>use std::result::Result as RResult;
use error::Error;

pub type Result<T> = RResult<T, Error>;
<commit_msg>Remove unused file<commit_after><|endoftext|>


<commit_before>use std::io;
use std::to_str::ToStr;
use std::os::pipe;
use std::io::{Open, Write};
use std::io::fs::{mkdir, File};
use std::io::pipe::PipeStream;
use std::io::stdio::{stdout, stderr};

use argparse::{ArgumentParser, Store, List};

use super::env::Environ;
use super::config::Config;
use super::linux::{make_namespace, change_root, bind_mount, mount_pseudofs};
use super::linux::{execute, forkme, wait_process};

fn mount_all(root: &Path, mount_dir: &Path, project_root: &Path)
    -> Result<(), String>
{
    let vagga_marker = mount_dir.join_many(["tmp", ".vagga"]);
    try!(bind_mount(root, mount_dir, false));
    try!(bind_mount(&Path::new("/sys"), &mount_dir.join("sys"), false));
    // TODO(tailhook) use dev in /var/lib/container-dev
    try!(bind_mount(&Path::new("/dev"), &mount_dir.join("dev"), false));
    try!(bind_mount(project_root, &mount_dir.join("work"), true));
    try!(mount_pseudofs("proc", &mount_dir.join("proc"), ""));
    // TODO(tailhook) allow customize size of tmpfs
    try!(mount_pseudofs("tmpfs", &mount_dir.join("tmp"),
                        "size=102400k,mode=1777"));
    try!(ensure_dir(&vagga_marker));
    try!(bind_mount(&vagga_marker,
                    &mount_dir.join_many(["work", ".vagga"]),
                    false));
    match File::open_mode(&vagga_marker.join("CONTAINED.txt"), Open, Write)
        .write_line("You are running in vagga container.") {
        Ok(()) => {}
        Err(e) => return Err(format!("Can't write CONTAINED.txt: {}", e)),
    }
    return Ok(());
}

fn ensure_dir(p: &Path) -> Result<(),String> {
    if p.exists() {
        return Ok(());
    }
    return mkdir(p, io::UserRWX).map_err(|e| { e.to_str() });
}

pub fn run_chroot(env: &Environ, container_root: &Path, mount_dir: &Path,
    command: &String, args: &Vec<String>)
    -> Result<(),String>
{
    try!(mount_all(container_root, mount_dir, &env.project_root));
    try!(change_root(mount_dir));
    // TODO(tailhook) set environment from config
    let environ = vec!("PATH=/bin:/usr/bin:/usr/local/bin".to_string());
    try!(execute(command, args, &environ));
    unreachable!();
}

pub fn run_user_command(env: &Environ, config: &Config,
    cmdname: &String, args: Vec<String>)
    -> Result<int, String> {
    unimplemented!();
}

pub fn run_command(env: &Environ, config: &Config, args: Vec<String>)
    -> Result<int, String>
{
    let mut cname = "devel".to_string();
    let mut command = "".to_string();
    let mut cmdargs = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut cname)
            .add_argument("container", box Store::<String>,
                "A name of the container to build")
            .required();
        ap.refer(&mut command)
            .add_argument("command", box Store::<String>,
                "A command to run inside container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arguments", box List::<String>,
                "Arguments for the command")
            .required();
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    match config.containers.find(&cname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find container {} in config",
                               cname));
        }
    };
    info!("Running {}: {} {}", cname, command, cmdargs);

    let container_dir = env.project_root.join_many(
        [".vagga", cname.as_slice()]);
    let container_root = container_dir.join("root");

    for dir in ["proc", "sys", "dev", "work", "tmp"].iter() {
        try!(ensure_dir(&container_root.join(*dir)));
    }

    let mount_dir = env.project_root.join_many([".vagga", "mnt"]);
    try!(ensure_dir(&mount_dir));
    try!(make_namespace());

    let mut pipe = match PipeStream::pair() {
        Ok(pipe) => pipe,
        Err(x) => return Err(format!("Can't create pipe: {}", x)),
    };

    let pid = try!(forkme());
    if pid == 0 {
        let mut buf: [u8,..1] = [0];
        match pipe.reader.read(buf) {
            Ok(_) => {}
            Err(x) => return Err(format!("Can't read from pipe: {}", x)),
        }
        drop(pipe);
        try!(run_chroot(env, &container_root, &mount_dir, &command, &cmdargs));
        unreachable!();
    } else {
        match File::open_mode(&Path::new("/proc")
                          .join(pid.to_str())
                          .join("uid_map"), Open, Write)
                .write_str("0 1000 1") {
            Ok(()) => {}
            Err(e) => return Err(format!(
                "Error writing uid mapping: {}", e)),
        }

        match pipe.writer.write_char('x') {
            Ok(_) => {}
            Err(e) => return Err(format!(
                "Error writing to pipe: {}", e)),
        }
        drop(pipe);

        return wait_process(pid);
    }
}
<commit_msg>Implement putting real user id in uid_map<commit_after>use std::io;
use std::to_str::ToStr;
use std::os::pipe;
use std::io::{Open, Write};
use std::io::fs::{mkdir, File};
use std::io::pipe::PipeStream;
use std::io::stdio::{stdout, stderr};

use argparse::{ArgumentParser, Store, List};

use super::env::Environ;
use super::config::Config;
use super::linux::{make_namespace, change_root, bind_mount, mount_pseudofs};
use super::linux::{execute, forkme, wait_process};
use libc::funcs::posix88::unistd::getuid;

fn mount_all(root: &Path, mount_dir: &Path, project_root: &Path)
    -> Result<(), String>
{
    let vagga_marker = mount_dir.join_many(["tmp", ".vagga"]);
    try!(bind_mount(root, mount_dir, false));
    try!(bind_mount(&Path::new("/sys"), &mount_dir.join("sys"), false));
    // TODO(tailhook) use dev in /var/lib/container-dev
    try!(bind_mount(&Path::new("/dev"), &mount_dir.join("dev"), false));
    try!(bind_mount(project_root, &mount_dir.join("work"), true));
    try!(mount_pseudofs("proc", &mount_dir.join("proc"), ""));
    // TODO(tailhook) allow customize size of tmpfs
    try!(mount_pseudofs("tmpfs", &mount_dir.join("tmp"),
                        "size=102400k,mode=1777"));
    try!(ensure_dir(&vagga_marker));
    try!(bind_mount(&vagga_marker,
                    &mount_dir.join_many(["work", ".vagga"]),
                    false));
    match File::open_mode(&vagga_marker.join("CONTAINED.txt"), Open, Write)
        .write_line("You are running in vagga container.") {
        Ok(()) => {}
        Err(e) => return Err(format!("Can't write CONTAINED.txt: {}", e)),
    }
    return Ok(());
}

fn ensure_dir(p: &Path) -> Result<(),String> {
    if p.exists() {
        return Ok(());
    }
    return mkdir(p, io::UserRWX).map_err(|e| { e.to_str() });
}

pub fn run_chroot(env: &Environ, container_root: &Path, mount_dir: &Path,
    command: &String, args: &Vec<String>)
    -> Result<(),String>
{
    try!(mount_all(container_root, mount_dir, &env.project_root));
    try!(change_root(mount_dir));
    // TODO(tailhook) set environment from config
    let environ = vec!("PATH=/bin:/usr/bin:/usr/local/bin".to_string());
    try!(execute(command, args, &environ));
    unreachable!();
}

pub fn run_user_command(env: &Environ, config: &Config,
    cmdname: &String, args: Vec<String>)
    -> Result<int, String> {
    unimplemented!();
}

pub fn run_command(env: &Environ, config: &Config, args: Vec<String>)
    -> Result<int, String>
{
    let mut cname = "devel".to_string();
    let mut command = "".to_string();
    let mut cmdargs = Vec::new();
    {
        let mut ap = ArgumentParser::new();
        ap.refer(&mut cname)
            .add_argument("container", box Store::<String>,
                "A name of the container to build")
            .required();
        ap.refer(&mut command)
            .add_argument("command", box Store::<String>,
                "A command to run inside container")
            .required();
        ap.refer(&mut cmdargs)
            .add_argument("arguments", box List::<String>,
                "Arguments for the command")
            .required();
        match ap.parse(args, &mut stdout(), &mut stderr()) {
            Ok(()) => {}
            Err(0) => return Ok(0),
            Err(_) => return Ok(122),
        }
    }
    match config.containers.find(&cname) {
        Some(c) => c,
        None => {
            return Err(format!("Can't find container {} in config",
                               cname));
        }
    };
    info!("Running {}: {} {}", cname, command, cmdargs);

    let container_dir = env.project_root.join_many(
        [".vagga", cname.as_slice()]);
    let container_root = container_dir.join("root");
    let uid = unsafe { getuid() };

    for dir in ["proc", "sys", "dev", "work", "tmp"].iter() {
        try!(ensure_dir(&container_root.join(*dir)));
    }

    let mount_dir = env.project_root.join_many([".vagga", "mnt"]);
    try!(ensure_dir(&mount_dir));
    try!(make_namespace());

    let mut pipe = match PipeStream::pair() {
        Ok(pipe) => pipe,
        Err(x) => return Err(format!("Can't create pipe: {}", x)),
    };

    let pid = try!(forkme());
    if pid == 0 {
        let mut buf: [u8,..1] = [0];
        match pipe.reader.read(buf) {
            Ok(_) => {}
            Err(x) => return Err(format!("Can't read from pipe: {}", x)),
        }
        drop(pipe);
        try!(run_chroot(env, &container_root, &mount_dir, &command, &cmdargs));
        unreachable!();
    } else {
        // TODO(tailhook) set uid map from config
        let uid_map = format!("0 {} 1", uid);
        debug!("Writing uid_map: {}", uid_map);
        match File::open_mode(&Path::new("/proc")
                          .join(pid.to_str())
                          .join("uid_map"), Open, Write)
                .write_str(uid_map.as_slice()) {
            Ok(()) => {}
            Err(e) => return Err(format!(
                "Error writing uid mapping: {}", e)),
        }

        match pipe.writer.write_char('x') {
            Ok(_) => {}
            Err(e) => return Err(format!(
                "Error writing to pipe: {}", e)),
        }
        drop(pipe);

        return wait_process(pid);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::error::Error;
use std::fmt;

use iron::{Request, Response, Handler, IronResult, IronError};
use iron::{status, method, headers};
use iron::typemap::Key;
use iron::modifiers::Redirect;

use recognizer::Router as Recognizer;
use recognizer::{Match, Params};

/// `Router` provides an interface for creating complex routes as middleware
/// for the Iron framework.
pub struct Router {
    // The routers, specialized by method.
    routers: HashMap<method::Method, Recognizer<Box<Handler>>>
}

impl Router {
    /// Construct a new, empty `Router`.
    ///
    /// ```
    /// # use router::Router;
    /// let router = Router::new();
    /// ```
    pub fn new() -> Router {
        Router {
            routers: HashMap::new()
        }
    }

    /// Add a new route to a `Router`, matching both a method and glob pattern.
    ///
    /// `route` supports glob patterns: `*` for a single wildcard segment and
    /// `:param` for matching storing that segment of the request url in the `Params`
    /// object, which is stored in the request `extensions`.
    ///
    /// For instance, to route `Get` requests on any route matching
    /// `/users/:userid/:friend` and store `userid` and `friend` in
    /// the exposed Params object:
    ///
    /// ```ignore
    /// let mut router = Router::new();
    /// router.route(method::Get, "/users/:userid/:friendid", controller);
    /// ```
    ///
    /// The controller provided to route can be any `Handler`, which allows
    /// extreme flexibility when handling routes. For instance, you could provide
    /// a `Chain`, a `Handler`, which contains an authorization middleware and
    /// a controller function, so that you can confirm that the request is
    /// authorized for this route before handling it.
    pub fn route<H, S>(&mut self, method: method::Method,
                       glob: S, handler: H) -> &mut Router
    where H: Handler, S: AsRef<str> {
        match self.routers.entry(method) {
            Vacant(entry)   => entry.insert(Recognizer::new()),
            Occupied(entry) => entry.into_mut()
        }.add(glob.as_ref().trim_right_matches('/'),
              Box::new(handler) as Box<Handler>);
        self
    }

    /// Like route, but specialized to the `Get` method.
    pub fn get<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Get, glob, handler)
    }

    /// Like route, but specialized to the `Post` method.
    pub fn post<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Post, glob, handler)
    }

    /// Like route, but specialized to the `Put` method.
    pub fn put<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Put, glob, handler)
    }

    /// Like route, but specialized to the `Delete` method.
    pub fn delete<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Delete, glob, handler)
    }

    /// Like route, but specialized to the `Head` method.
    pub fn head<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Head, glob, handler)
    }

    /// Like route, but specialized to the `Patch` method.
    pub fn patch<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Patch, glob, handler)
    }

    /// Like route, but specialized to the `Options` method.
    pub fn options<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Options, glob, handler)
    }

    fn recognize<'a>(&'a self, method: &method::Method, path: &str)
                     -> Option<Match<&'a Box<Handler>>> {
        self.routers.get(method).and_then(|router| router.recognize(path).ok())
    }

    fn handle_options(&self, req: &mut Request, path: &str) -> IronResult<Response> {
        static METHODS: &'static [method::Method] =
            &[method::Get, method::Post, method::Post, method::Put,
              method::Delete, method::Head, method::Patch];

        // If there is an override, use it.
        if let Some(matched) = self.recognize(&method::Options, path) {
            req.extensions.insert::<Router>(matched.params);
            return matched.handler.handle(req);
        }

        // Else, get all the available methods and return them.
        let mut options = vec![];

        for method in METHODS.iter() {
            self.routers.get(method).map(|router| {
                if let Some(_) = router.recognize(path).ok() {
                    options.push(method.clone());
                }
            });
        }

        let mut res = Response::with(status::Ok);
        res.headers.set(headers::Allow(options));
        Ok(res)
    }

    fn handle_trailing_slash(&self, req: &mut Request) -> IronResult<Response> {
        let mut url = req.url.clone();

        // Pull off as many trailing slashes as possible.
        while url.path.len() != 1 && url.path.last() == Some(&String::new()) {
            url.path.pop();
        }

        Err(IronError::new(TrailingSlash, (status::MovedPermanently, Redirect(url))))
    }

    fn handle_method(&self, req: &mut Request, path: &str) -> IronResult<Response> {
        let matched = match self.recognize(&req.method, path) {
            Some(matched) => matched,
            // No match.
            None => return Err(IronError::new(NoRoute, status::NotFound))
        };

        req.extensions.insert::<Router>(matched.params);
        matched.handler.handle(req)
    }
}

impl Key for Router { type Value = Params; }

impl Handler for Router {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        if req.url.path.len() != 1 && Some(&String::new()) == req.url.path.last() {
            return self.handle_trailing_slash(req);
        }

        // No trailing slash
        let path = req.url.path.connect("/");

        if let method::Options = req.method {
            return self.handle_options(req, &*path);
        }

        self.handle_method(req, &*path)
    }
}

/// The error thrown by router if there is no matching route,
/// it is always accompanied by a NotFound response.
#[derive(Debug)]
pub struct NoRoute;

impl fmt::Display for NoRoute {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("No matching route found.")
    }
}

impl Error for NoRoute {
    fn description(&self) -> &str { "No Route" }
}

/// The error thrown by router if the request had a trailing slash,
/// it is always accompanied by a redirect.
#[derive(Debug)]
pub struct TrailingSlash;

impl fmt::Display for TrailingSlash {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("The request had a trailing slash.")
    }
}

impl Error for TrailingSlash {
    fn description(&self) -> &str { "Trailing Slash" }
}

<commit_msg>Handle HEAD requests properly.<commit_after>use std::collections::HashMap;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::error::Error;
use std::fmt;

use iron::{Request, Response, Handler, IronResult, IronError};
use iron::{status, method, headers};
use iron::typemap::Key;
use iron::modifiers::Redirect;

use recognizer::Router as Recognizer;
use recognizer::{Match, Params};

/// `Router` provides an interface for creating complex routes as middleware
/// for the Iron framework.
pub struct Router {
    // The routers, specialized by method.
    routers: HashMap<method::Method, Recognizer<Box<Handler>>>
}

impl Router {
    /// Construct a new, empty `Router`.
    ///
    /// ```
    /// # use router::Router;
    /// let router = Router::new();
    /// ```
    pub fn new() -> Router {
        Router {
            routers: HashMap::new()
        }
    }

    /// Add a new route to a `Router`, matching both a method and glob pattern.
    ///
    /// `route` supports glob patterns: `*` for a single wildcard segment and
    /// `:param` for matching storing that segment of the request url in the `Params`
    /// object, which is stored in the request `extensions`.
    ///
    /// For instance, to route `Get` requests on any route matching
    /// `/users/:userid/:friend` and store `userid` and `friend` in
    /// the exposed Params object:
    ///
    /// ```ignore
    /// let mut router = Router::new();
    /// router.route(method::Get, "/users/:userid/:friendid", controller);
    /// ```
    ///
    /// The controller provided to route can be any `Handler`, which allows
    /// extreme flexibility when handling routes. For instance, you could provide
    /// a `Chain`, a `Handler`, which contains an authorization middleware and
    /// a controller function, so that you can confirm that the request is
    /// authorized for this route before handling it.
    pub fn route<H, S>(&mut self, method: method::Method,
                       glob: S, handler: H) -> &mut Router
    where H: Handler, S: AsRef<str> {
        match self.routers.entry(method) {
            Vacant(entry)   => entry.insert(Recognizer::new()),
            Occupied(entry) => entry.into_mut()
        }.add(glob.as_ref().trim_right_matches('/'),
              Box::new(handler) as Box<Handler>);
        self
    }

    /// Like route, but specialized to the `Get` method.
    pub fn get<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Get, glob, handler)
    }

    /// Like route, but specialized to the `Post` method.
    pub fn post<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Post, glob, handler)
    }

    /// Like route, but specialized to the `Put` method.
    pub fn put<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Put, glob, handler)
    }

    /// Like route, but specialized to the `Delete` method.
    pub fn delete<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Delete, glob, handler)
    }

    /// Like route, but specialized to the `Head` method.
    pub fn head<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Head, glob, handler)
    }

    /// Like route, but specialized to the `Patch` method.
    pub fn patch<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Patch, glob, handler)
    }

    /// Like route, but specialized to the `Options` method.
    pub fn options<H: Handler, S: AsRef<str>>(&mut self, glob: S, handler: H) -> &mut Router {
        self.route(method::Options, glob, handler)
    }

    fn recognize<'a>(&'a self, method: &method::Method, path: &str)
                     -> Option<Match<&'a Box<Handler>>> {
        self.routers.get(method).and_then(|router| router.recognize(path).ok())
    }

    fn handle_options(&self, req: &mut Request, path: &str) -> IronResult<Response> {
        static METHODS: &'static [method::Method] =
            &[method::Get, method::Post, method::Post, method::Put,
              method::Delete, method::Head, method::Patch];

        // If there is an override, use it.
        if let Some(matched) = self.recognize(&method::Options, path) {
            req.extensions.insert::<Router>(matched.params);
            return matched.handler.handle(req);
        }

        // Else, get all the available methods and return them.
        let mut options = vec![];

        for method in METHODS.iter() {
            self.routers.get(method).map(|router| {
                if let Some(_) = router.recognize(path).ok() {
                    options.push(method.clone());
                }
            });
        }
        // If GET is there, HEAD is also there.
        if options.contains(&method::Get) && !options.contains(&method::Head) {
            options.push(method::Head);
        }

        let mut res = Response::with(status::Ok);
        res.headers.set(headers::Allow(options));
        Ok(res)
    }

    fn handle_trailing_slash(&self, req: &mut Request) -> IronResult<Response> {
        let mut url = req.url.clone();

        // Pull off as many trailing slashes as possible.
        while url.path.len() != 1 && url.path.last() == Some(&String::new()) {
            url.path.pop();
        }

        Err(IronError::new(TrailingSlash, (status::MovedPermanently, Redirect(url))))
    }

    fn handle_method(&self, req: &mut Request, path: &str) -> IronResult<Response> {
        let mut matched = self.recognize(&req.method, path);
        if matched.is_none() && req.method == method::Head {
            // For HEAD, fall back to GET. Hyper ensures no response body is written.
            matched = self.recognize(&method::Get, path);
        }
        let matched = match matched {
            Some(matched) => matched,
            // No match.
            None => return Err(IronError::new(NoRoute, status::NotFound))
        };

        req.extensions.insert::<Router>(matched.params);
        matched.handler.handle(req)
    }
}

impl Key for Router { type Value = Params; }

impl Handler for Router {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        if req.url.path.len() != 1 && Some(&String::new()) == req.url.path.last() {
            return self.handle_trailing_slash(req);
        }

        // No trailing slash
        let path = req.url.path.connect("/");

        if let method::Options = req.method {
            return self.handle_options(req, &*path);
        }

        self.handle_method(req, &*path)
    }
}

/// The error thrown by router if there is no matching route,
/// it is always accompanied by a NotFound response.
#[derive(Debug)]
pub struct NoRoute;

impl fmt::Display for NoRoute {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("No matching route found.")
    }
}

impl Error for NoRoute {
    fn description(&self) -> &str { "No Route" }
}

/// The error thrown by router if the request had a trailing slash,
/// it is always accompanied by a redirect.
#[derive(Debug)]
pub struct TrailingSlash;

impl fmt::Display for TrailingSlash {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("The request had a trailing slash.")
    }
}

impl Error for TrailingSlash {
    fn description(&self) -> &str { "Trailing Slash" }
}

<|endoftext|>


<commit_before>use clap::{App, Arg, AppSettings};

type StaticApp = App<'static, 'static>;

fn base_app(has_default_bin: bool) -> StaticApp {
  App::new(crate_name!())
    .about("A tool for pasting from the terminal")
    .author(crate_authors!())
    .version(crate_version!())
    .help_message("print help information and exit")
    .setting(AppSettings::DisableVersion)
    .arg(Arg::with_name("inputs")
      .help("inputs to the program, either files or URLs")
      .takes_value(true)
      .value_name("input")
      .multiple(true))
    .arg(Arg::with_name("debug")
      .long("debug")
      .short("d")
      .help("enable debug output"))
    .arg(Arg::with_name("bin")
      .long("bin")
      .short("b")
      .help("specify the upload bin")
      .required(!has_default_bin)
      .takes_value(true)
      .value_name("bin")
      .possible_values(&["hastebin", "sprunge", "gist", "fedora", "bitbucket", "pastebin"]))
    .arg(Arg::with_name("public")
      .long("public")
      .short("P")
      .help("set the paste to be public")
      .conflicts_with("private"))
    .arg(Arg::with_name("private")
      .long("private")
      .short("p")
      .help("set the paste to be private"))
    .arg(Arg::with_name("authed")
      .long("authed")
      .short("a")
      .help("set the paste to be uploaded while authenticated")
      .conflicts_with("anonymous"))
    .arg(Arg::with_name("anonymous")
      .long("anonymous")
      .short("A")
      .help("set the paste to be uploaded while not authenticated"))
    .arg(Arg::with_name("json")
      .long("json")
      .short("j")
      .help("output JSON information"))
    .arg(Arg::with_name("raw-urls")
      .long("raw-urls")
      .short("r")
      .help("output URLs to the raw content")
      .conflicts_with("html-urls"))
    .arg(Arg::with_name("html-urls")
      .long("html-urls")
      .short("u")
      .help("output URLs to the HTML content"))
    .arg(Arg::with_name("message")
      .long("message")
      .short("m")
      .help("specify a message to upload instead of files or stdin")
      .takes_value(true)
      .value_name("message")
      .conflicts_with("inputs"))
    .arg(Arg::with_name("list-bins")
      .long("list-bins")
      .short("-l")
      .help("list the available bins")
      .conflicts_with_all(&["bin",
        "public",
        "private",
        "anonymous",
        "authed",
        "raw-urls",
        "html-urls",
        "message"]))
    .arg(Arg::with_name("force")
      .long("force")
      .short("f")
      .help("force upload, ignoring safety features"))
    .arg(Arg::with_name("name")
      .long("name")
      .short("N")
      .help("manually set the file name for single-file uploads")
      .takes_value(true)
      .value_name("file_name"))
    .arg(Arg::with_name("version")
      .long("version")
      .short("v")
      .help("print version information and exit")
      .overrides_with("bin"))
    .arg(Arg::with_name("list-all")
      .long("list-all")
      .short("L")
      .help("list all file names when downloading")
      .conflicts_with("range"))
    .arg(Arg::with_name("range")
      .long("range")
      .short("n")
      .help("chooses the files to get in input mode, starting from 0 (e.g. \"0\", \"0,1\", \"0-2\", \"2-0,3\")")
      .takes_value(true)
      .value_name("range")
      .use_delimiter(false))
}

pub fn add_feature_options(app: StaticApp) -> StaticApp {
  if cfg!(feature = "clipboard_support") {
    app
      .arg(Arg::with_name("copy")
        .long("copy")
        .short("C")
        .help("copy the output to the clipboard")
        .conflicts_with("no-copy"))
      .arg(Arg::with_name("no-copy")
        .long("no-copy")
        .short("c")
        .help("do not copy the output to the clipboard"))
  } else {
    app
  }
}

pub fn create_app(has_default_bin: bool) -> StaticApp {
  let app = base_app(has_default_bin);
  add_feature_options(app)
}
<commit_msg>fix: use short name l, not -l<commit_after>use clap::{App, Arg, AppSettings};

type StaticApp = App<'static, 'static>;

fn base_app(has_default_bin: bool) -> StaticApp {
  App::new(crate_name!())
    .about("A tool for pasting from the terminal")
    .author(crate_authors!())
    .version(crate_version!())
    .help_message("print help information and exit")
    .setting(AppSettings::DisableVersion)
    .arg(Arg::with_name("inputs")
      .help("inputs to the program, either files or URLs")
      .takes_value(true)
      .value_name("input")
      .multiple(true))
    .arg(Arg::with_name("debug")
      .long("debug")
      .short("d")
      .help("enable debug output"))
    .arg(Arg::with_name("bin")
      .long("bin")
      .short("b")
      .help("specify the upload bin")
      .required(!has_default_bin)
      .takes_value(true)
      .value_name("bin")
      .possible_values(&["hastebin", "sprunge", "gist", "fedora", "bitbucket", "pastebin"]))
    .arg(Arg::with_name("public")
      .long("public")
      .short("P")
      .help("set the paste to be public")
      .conflicts_with("private"))
    .arg(Arg::with_name("private")
      .long("private")
      .short("p")
      .help("set the paste to be private"))
    .arg(Arg::with_name("authed")
      .long("authed")
      .short("a")
      .help("set the paste to be uploaded while authenticated")
      .conflicts_with("anonymous"))
    .arg(Arg::with_name("anonymous")
      .long("anonymous")
      .short("A")
      .help("set the paste to be uploaded while not authenticated"))
    .arg(Arg::with_name("json")
      .long("json")
      .short("j")
      .help("output JSON information"))
    .arg(Arg::with_name("raw-urls")
      .long("raw-urls")
      .short("r")
      .help("output URLs to the raw content")
      .conflicts_with("html-urls"))
    .arg(Arg::with_name("html-urls")
      .long("html-urls")
      .short("u")
      .help("output URLs to the HTML content"))
    .arg(Arg::with_name("message")
      .long("message")
      .short("m")
      .help("specify a message to upload instead of files or stdin")
      .takes_value(true)
      .value_name("message")
      .conflicts_with("inputs"))
    .arg(Arg::with_name("list-bins")
      .long("list-bins")
      .short("l")
      .help("list the available bins")
      .conflicts_with_all(&["bin",
        "public",
        "private",
        "anonymous",
        "authed",
        "raw-urls",
        "html-urls",
        "message"]))
    .arg(Arg::with_name("force")
      .long("force")
      .short("f")
      .help("force upload, ignoring safety features"))
    .arg(Arg::with_name("name")
      .long("name")
      .short("N")
      .help("manually set the file name for single-file uploads")
      .takes_value(true)
      .value_name("file_name"))
    .arg(Arg::with_name("version")
      .long("version")
      .short("v")
      .help("print version information and exit")
      .overrides_with("bin"))
    .arg(Arg::with_name("list-all")
      .long("list-all")
      .short("L")
      .help("list all file names when downloading")
      .conflicts_with("range"))
    .arg(Arg::with_name("range")
      .long("range")
      .short("n")
      .help("chooses the files to get in input mode, starting from 0 (e.g. \"0\", \"0,1\", \"0-2\", \"2-0,3\")")
      .takes_value(true)
      .value_name("range")
      .use_delimiter(false))
    .arg(Arg::with_name("output")
      .long("output")
      .short("o")
      .help("writes one file to the given path or writes multiple files in the given path as a directory")
      .takes_value(true)
      .value_name("path"))
}

pub fn add_feature_options(app: StaticApp) -> StaticApp {
  if cfg!(feature = "clipboard_support") {
    app
      .arg(Arg::with_name("copy")
        .long("copy")
        .short("C")
        .help("copy the output to the clipboard")
        .conflicts_with("no-copy"))
      .arg(Arg::with_name("no-copy")
        .long("no-copy")
        .short("c")
        .help("do not copy the output to the clipboard"))
  } else {
    app
  }
}

pub fn create_app(has_default_bin: bool) -> StaticApp {
  let app = base_app(has_default_bin);
  add_feature_options(app)
}
<|endoftext|>


<commit_before>use std::env;
use std::marker::PhantomData;
use clap::{self, SubCommand};
use shlex;

use app::{ClapApp, ClapRun};
use core::{Repository, Workspace};
use util;

/// Toplevel application
pub enum Command<'a> {
  New(NewCommand<'a>),
  Add(AddCommand<'a>),
  Clone(CloneCommand<'a>),
  Scan(ScanCommand<'a>),
  List(ListCommand<'a>),
  Foreach(ForeachCommand<'a>),
}

impl<'a> ClapApp for Command<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.subcommand(NewCommand::make_app(SubCommand::with_name("new")))
       .subcommand(AddCommand::make_app(SubCommand::with_name("add")))
       .subcommand(CloneCommand::make_app(SubCommand::with_name("clone")))
       .subcommand(ListCommand::make_app(SubCommand::with_name("list")))
       .subcommand(ForeachCommand::make_app(SubCommand::with_name("foreach")))
       .subcommand(ScanCommand::make_app(SubCommand::with_name("scan")))
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for Command<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> Command<'a> {
    match m.subcommand() {
      ("new", Some(m)) => Command::New(m.into()),
      ("add", Some(m)) => Command::Add(m.into()),
      ("clone", Some(m)) => Command::Clone(m.into()),
      ("list", Some(m)) => Command::List(m.into()),
      ("foreach", Some(m)) => Command::Foreach(m.into()),
      ("scan", Some(m)) => Command::Scan(m.into()),
      _ => unreachable!(),
    }
  }
}

impl<'a> Command<'a> {
  pub fn run(self) -> ::Result<()> {
    match self {
      Command::New(m) => m.run(),
      Command::Add(m) => m.run(),
      Command::Clone(m) => m.run(),
      Command::List(m) => m.run(),
      Command::Foreach(m) => m.run(),
      Command::Scan(m) => m.run(),
    }
  }
}

/// Subcommand `new`
pub struct NewCommand<'a> {
  query: &'a str,
  root: Option<&'a str>,
  dry_run: bool,
  ssh: bool,
}

impl<'a> ClapApp for NewCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Create a new Git repository with intuitive directory structure")
       .arg_from_usage("<query>         'URL or query of remote repository'")
       .arg_from_usage("--root=[root]   'Target root directory of repository")
       .arg_from_usage("-n, --dry-run   'Do not actually create a new repository'")
       .arg_from_usage("-s, --ssh       'Use SSH protocol'")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for NewCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> NewCommand<'a> {
    NewCommand {
      query: m.value_of("query").unwrap(),
      root: m.value_of("root"),
      dry_run: m.is_present("dry-run"),
      ssh: m.is_present("ssh"),
    }
  }
}

impl<'a> ClapRun for NewCommand<'a> {
  fn run(self) -> ::Result<()> {
    let mut workspace = Workspace::new(self.root)?;

    let query = self.query.parse()?;
    let root = workspace.root_dir().ok_or("Unknown root directory")?;
    let repo = Repository::from_query(root, query, self.ssh)?;

    if self.dry_run {
      println!("+ git init \"{}\"", repo.path_string());
    } else {
      repo.do_init()?;
      workspace.add_repository(repo);
      workspace.save_cache()?;
    }

    Ok(())
  }
}

pub struct AddCommand<'a> {
  path: Option<&'a str>,
}

impl<'a> ClapApp for AddCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Add existed repository into managed")
       .arg_from_usage("[path]  'Path of local repository'")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for AddCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> AddCommand<'a> {
    AddCommand { path: m.value_of("path") }
  }
}

impl<'a> ClapRun for AddCommand<'a> {
  fn run(self) -> ::Result<()> {
    let mut workspace = Workspace::new(None)?;

    let mut path = if let Some(path) = self.path {
      util::make_path_buf(path)?
    } else {
      env::current_dir()?
    };
    if !path.is_absolute() {
      path = env::current_dir()?.join(path);
    }

    let repo = Repository::from_path(path)?;
    if !repo.is_vcs() {
      Err("Given path is not a repository")?;
    }
    workspace.add_repository(repo);
    workspace.save_cache()?;

    Ok(())
  }
}

/// Subcommand `clone`
pub struct CloneCommand<'a> {
  query: Option<&'a str>,
  arg: Option<&'a str>,
  root: Option<&'a str>,
  dry_run: bool,
  ssh: bool,
}

impl<'a> ClapApp for CloneCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Clone remote repositories into the root directory")
       .arg_from_usage("[query]         'URL or query of remote repository'")
       .arg_from_usage("--root=[root]   'Target root directory of cloned repository'")
       .arg_from_usage("--arg=[arg]     'Supplemental arguments for Git command'")
       .arg_from_usage("-n, --dry-run   'Do not actually execute Git command'")
       .arg_from_usage("-s, --ssh       'Use SSH protocol'")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for CloneCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> CloneCommand<'a> {
    CloneCommand {
      query: m.value_of("query"),
      arg: m.value_of("arg"),
      root: m.value_of("root"),
      dry_run: m.is_present("dry-run"),
      ssh: m.is_present("ssh"),
    }
  }
}

impl<'a> ClapRun for CloneCommand<'a> {
  fn run(self) -> ::Result<()> {
    let args = self.arg
                   .and_then(|s| shlex::split(s))
                   .unwrap_or_default();
    let queries = if let Some(query) = self.query {
      vec![query.parse()?]
    } else {
      use std::io::BufRead;
      let stdin = ::std::io::stdin();
      let mut queries = Vec::new();
      for query in stdin.lock().lines().filter_map(|l| l.ok()) {
        queries.push(query.parse()?);
      }
      queries
    };

    let mut workspace = Workspace::new(self.root)?;
    let root = workspace.root_dir().ok_or("Unknown root directory")?;

    for query in queries {
      let repo = Repository::from_query(&root, query, self.ssh)?;
      if self.dry_run {
        let url = repo.url_string().ok_or("Unknown URL")?;
        println!("+ git clone \"{}\" \"{}\" {}",
                 url,
                 repo.path_string(),
                 util::join_str(&args));
      } else {
        repo.do_clone(&args)?;
        workspace.add_repository(repo);
      }
    }

    if !self.dry_run {
      workspace.save_cache()?;
    }

    Ok(())
  }
}

/// Subcommand `scan`
pub struct ScanCommand<'a> {
  verbose: bool,
  prune: bool,
  depth: Option<usize>,
  marker: PhantomData<&'a usize>,
}

impl<'a> ClapApp for ScanCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Scan directories to create cache of repositories list")
       .arg_from_usage("-v, --verbose    'Use verbose output'")
       .arg_from_usage("-p, --prune      'Ignore repositories located at outside of base directories'")
       .arg_from_usage("--depth=[depth]  'Maximal depth of entries for each base directory")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for ScanCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> ScanCommand<'a> {
    ScanCommand {
      verbose: m.is_present("verbose"),
      prune: m.is_present("prune"),
      depth: m.value_of("depth").and_then(|s| s.parse().ok()),
      marker: PhantomData,
    }
  }
}

impl<'a> ClapRun for ScanCommand<'a> {
  fn run(self) -> ::Result<()> {
    let mut workspace = Workspace::new(None)?;
    workspace.scan_repositories(self.verbose, self.prune, self.depth)?;
    workspace.save_cache()?;
    Ok(())
  }
}

/// Subcommand `list`
pub struct ListCommand<'a> {
  marker: PhantomData<&'a usize>,
}

impl<'a> ClapApp for ListCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("List local repositories managed by rhq")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for ListCommand<'a> {
  fn from(_: &'b clap::ArgMatches<'a>) -> ListCommand<'a> {
    ListCommand { marker: PhantomData }
  }
}

impl<'a> ClapRun for ListCommand<'a> {
  fn run(self) -> ::Result<()> {
    let workspace = Workspace::new(None)?;
    let repos = workspace.repositories()
                         .ok_or("The cache has not initialized yet")?;
    for repo in repos {
      println!("{}", repo.path_string());
    }
    Ok(())
  }
}

/// Subcommand `foreach`
pub struct ForeachCommand<'a> {
  command: &'a str,
  args: Option<clap::Values<'a>>,
  dry_run: bool,
}

impl<'a> ClapApp for ForeachCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Execute command into each repositories")
       .arg_from_usage("<command>       'Command name'")
       .arg_from_usage("[args]...       'Arguments of command'")
       .arg_from_usage("-n, --dry-run   'Do not actually execute command'")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for ForeachCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> ForeachCommand<'a> {
    ForeachCommand {
      command: m.value_of("command").unwrap(),
      args: m.values_of("args"),
      dry_run: m.is_present("dry-run"),
    }
  }
}

impl<'a> ClapRun for ForeachCommand<'a> {
  fn run(self) -> ::Result<()> {
    let args: Vec<_> = self.args.map(|s| s.collect()).unwrap_or_default();
    let workspace = Workspace::new(None)?;
    let repos = workspace.repositories()
                         .ok_or("The cache has not initialized yet")?;
    for repo in repos {
      if self.dry_run {
        println!("+ {} {}", self.command, util::join_str(&args));
      } else {
        repo.run_command(self.command, &args)?;
      }
    }
    Ok(())
  }
}
<commit_msg>remove option: '--ssh'<commit_after>use std::env;
use std::marker::PhantomData;
use clap::{self, SubCommand};
use shlex;

use app::{ClapApp, ClapRun};
use core::{Repository, Workspace};
use util;

/// Toplevel application
pub enum Command<'a> {
  New(NewCommand<'a>),
  Add(AddCommand<'a>),
  Clone(CloneCommand<'a>),
  Scan(ScanCommand<'a>),
  List(ListCommand<'a>),
  Foreach(ForeachCommand<'a>),
}

impl<'a> ClapApp for Command<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.subcommand(NewCommand::make_app(SubCommand::with_name("new")))
       .subcommand(AddCommand::make_app(SubCommand::with_name("add")))
       .subcommand(CloneCommand::make_app(SubCommand::with_name("clone")))
       .subcommand(ListCommand::make_app(SubCommand::with_name("list")))
       .subcommand(ForeachCommand::make_app(SubCommand::with_name("foreach")))
       .subcommand(ScanCommand::make_app(SubCommand::with_name("scan")))
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for Command<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> Command<'a> {
    match m.subcommand() {
      ("new", Some(m)) => Command::New(m.into()),
      ("add", Some(m)) => Command::Add(m.into()),
      ("clone", Some(m)) => Command::Clone(m.into()),
      ("list", Some(m)) => Command::List(m.into()),
      ("foreach", Some(m)) => Command::Foreach(m.into()),
      ("scan", Some(m)) => Command::Scan(m.into()),
      _ => unreachable!(),
    }
  }
}

impl<'a> Command<'a> {
  pub fn run(self) -> ::Result<()> {
    match self {
      Command::New(m) => m.run(),
      Command::Add(m) => m.run(),
      Command::Clone(m) => m.run(),
      Command::List(m) => m.run(),
      Command::Foreach(m) => m.run(),
      Command::Scan(m) => m.run(),
    }
  }
}

/// Subcommand `new`
pub struct NewCommand<'a> {
  query: &'a str,
  root: Option<&'a str>,
  dry_run: bool,
}

impl<'a> ClapApp for NewCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Create a new Git repository with intuitive directory structure")
       .arg_from_usage("<query>         'URL or query of remote repository'")
       .arg_from_usage("--root=[root]   'Target root directory of repository")
       .arg_from_usage("-n, --dry-run   'Do not actually create a new repository'")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for NewCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> NewCommand<'a> {
    NewCommand {
      query: m.value_of("query").unwrap(),
      root: m.value_of("root"),
      dry_run: m.is_present("dry-run"),
    }
  }
}

impl<'a> ClapRun for NewCommand<'a> {
  fn run(self) -> ::Result<()> {
    let mut workspace = Workspace::new(self.root)?;

    let query = self.query.parse()?;
    let root = workspace.root_dir().ok_or("Unknown root directory")?;
    let repo = Repository::from_query(root, query, false)?;

    if self.dry_run {
      println!("+ git init \"{}\"", repo.path_string());
    } else {
      repo.do_init()?;
      workspace.add_repository(repo);
      workspace.save_cache()?;
    }

    Ok(())
  }
}

pub struct AddCommand<'a> {
  path: Option<&'a str>,
}

impl<'a> ClapApp for AddCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Add existed repository into managed")
       .arg_from_usage("[path]  'Path of local repository'")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for AddCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> AddCommand<'a> {
    AddCommand { path: m.value_of("path") }
  }
}

impl<'a> ClapRun for AddCommand<'a> {
  fn run(self) -> ::Result<()> {
    let mut workspace = Workspace::new(None)?;

    let mut path = if let Some(path) = self.path {
      util::make_path_buf(path)?
    } else {
      env::current_dir()?
    };
    if !path.is_absolute() {
      path = env::current_dir()?.join(path);
    }

    let repo = Repository::from_path(path)?;
    if !repo.is_vcs() {
      Err("Given path is not a repository")?;
    }
    workspace.add_repository(repo);
    workspace.save_cache()?;

    Ok(())
  }
}

/// Subcommand `clone`
pub struct CloneCommand<'a> {
  query: Option<&'a str>,
  arg: Option<&'a str>,
  root: Option<&'a str>,
  dry_run: bool,
  ssh: bool,
}

impl<'a> ClapApp for CloneCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Clone remote repositories into the root directory")
       .arg_from_usage("[query]         'URL or query of remote repository'")
       .arg_from_usage("--root=[root]   'Target root directory of cloned repository'")
       .arg_from_usage("--arg=[arg]     'Supplemental arguments for Git command'")
       .arg_from_usage("-n, --dry-run   'Do not actually execute Git command'")
       .arg_from_usage("-s, --ssh       'Use SSH protocol'")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for CloneCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> CloneCommand<'a> {
    CloneCommand {
      query: m.value_of("query"),
      arg: m.value_of("arg"),
      root: m.value_of("root"),
      dry_run: m.is_present("dry-run"),
      ssh: m.is_present("ssh"),
    }
  }
}

impl<'a> ClapRun for CloneCommand<'a> {
  fn run(self) -> ::Result<()> {
    let args = self.arg
                   .and_then(|s| shlex::split(s))
                   .unwrap_or_default();
    let queries = if let Some(query) = self.query {
      vec![query.parse()?]
    } else {
      use std::io::BufRead;
      let stdin = ::std::io::stdin();
      let mut queries = Vec::new();
      for query in stdin.lock().lines().filter_map(|l| l.ok()) {
        queries.push(query.parse()?);
      }
      queries
    };

    let mut workspace = Workspace::new(self.root)?;
    let root = workspace.root_dir().ok_or("Unknown root directory")?;

    for query in queries {
      let repo = Repository::from_query(&root, query, self.ssh)?;
      if self.dry_run {
        let url = repo.url_string().ok_or("Unknown URL")?;
        println!("+ git clone \"{}\" \"{}\" {}",
                 url,
                 repo.path_string(),
                 util::join_str(&args));
      } else {
        repo.do_clone(&args)?;
        workspace.add_repository(repo);
      }
    }

    if !self.dry_run {
      workspace.save_cache()?;
    }

    Ok(())
  }
}

/// Subcommand `scan`
pub struct ScanCommand<'a> {
  verbose: bool,
  prune: bool,
  depth: Option<usize>,
  marker: PhantomData<&'a usize>,
}

impl<'a> ClapApp for ScanCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Scan directories to create cache of repositories list")
       .arg_from_usage("-v, --verbose    'Use verbose output'")
       .arg_from_usage("-p, --prune      'Ignore repositories located at outside of base directories'")
       .arg_from_usage("--depth=[depth]  'Maximal depth of entries for each base directory")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for ScanCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> ScanCommand<'a> {
    ScanCommand {
      verbose: m.is_present("verbose"),
      prune: m.is_present("prune"),
      depth: m.value_of("depth").and_then(|s| s.parse().ok()),
      marker: PhantomData,
    }
  }
}

impl<'a> ClapRun for ScanCommand<'a> {
  fn run(self) -> ::Result<()> {
    let mut workspace = Workspace::new(None)?;
    workspace.scan_repositories(self.verbose, self.prune, self.depth)?;
    workspace.save_cache()?;
    Ok(())
  }
}

/// Subcommand `list`
pub struct ListCommand<'a> {
  marker: PhantomData<&'a usize>,
}

impl<'a> ClapApp for ListCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("List local repositories managed by rhq")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for ListCommand<'a> {
  fn from(_: &'b clap::ArgMatches<'a>) -> ListCommand<'a> {
    ListCommand { marker: PhantomData }
  }
}

impl<'a> ClapRun for ListCommand<'a> {
  fn run(self) -> ::Result<()> {
    let workspace = Workspace::new(None)?;
    let repos = workspace.repositories()
                         .ok_or("The cache has not initialized yet")?;
    for repo in repos {
      println!("{}", repo.path_string());
    }
    Ok(())
  }
}

/// Subcommand `foreach`
pub struct ForeachCommand<'a> {
  command: &'a str,
  args: Option<clap::Values<'a>>,
  dry_run: bool,
}

impl<'a> ClapApp for ForeachCommand<'a> {
  fn make_app<'b, 'c: 'b>(app: clap::App<'b, 'c>) -> clap::App<'b, 'c> {
    app.about("Execute command into each repositories")
       .arg_from_usage("<command>       'Command name'")
       .arg_from_usage("[args]...       'Arguments of command'")
       .arg_from_usage("-n, --dry-run   'Do not actually execute command'")
  }
}

impl<'a, 'b: 'a> From<&'b clap::ArgMatches<'a>> for ForeachCommand<'a> {
  fn from(m: &'b clap::ArgMatches<'a>) -> ForeachCommand<'a> {
    ForeachCommand {
      command: m.value_of("command").unwrap(),
      args: m.values_of("args"),
      dry_run: m.is_present("dry-run"),
    }
  }
}

impl<'a> ClapRun for ForeachCommand<'a> {
  fn run(self) -> ::Result<()> {
    let args: Vec<_> = self.args.map(|s| s.collect()).unwrap_or_default();
    let workspace = Workspace::new(None)?;
    let repos = workspace.repositories()
                         .ok_or("The cache has not initialized yet")?;
    for repo in repos {
      if self.dry_run {
        println!("+ {} {}", self.command, util::join_str(&args));
      } else {
        repo.run_command(self.command, &args)?;
      }
    }
    Ok(())
  }
}
<|endoftext|>


<commit_before>// (c) 2016 Productize SPRL <joost@productize.be>

use std::io;

use formatter::*;

use Sexp;
use Result;
use encode_string;

struct Serializer<W, F=CompactFormatter> {
    writer: W,
    formatter: F,
}

// dispatches only based on Formatter
impl<W> Serializer<W>
    where W: io::Write,
{
    fn new(writer: W) -> Self {
        Serializer::with_formatter(writer, CompactFormatter)
    }
}

impl<W> Serializer<W, RulesFormatter>
    where W: io::Write,
{
    fn new_rules(writer: W, rules:Rules) -> Self {
        Serializer::with_formatter(writer, RulesFormatter::new(rules))
    }
}

impl<W, F> Serializer<W, F>
    where W: io::Write,
          F: Formatter,
{
    fn with_formatter(writer: W, formatter: F) -> Self {
        Serializer {
            writer: writer,
            formatter: formatter,
        }
    }

    fn serialize_str(&mut self, value:&str) -> Result<()> {
        write!(&mut self.writer, "{}", encode_string(value)).map_err(From::from)
    }

    fn serialize(&mut self, value:&Sexp) -> Result<()> {
        match *value {
            Sexp::String(ref s) => {
                self.serialize_str(s)
            },
            Sexp::List(ref list) => {
                let mut first = true;
                if list.is_empty() {
                    try!(self.formatter.open(&mut self.writer, None));
                } else {
                    for v in list {
                        if first {
                            try!(self.formatter.open(&mut self.writer, Some(v)));
                            try!(self.serialize(v));
                            first = false;
                        } else {
                            try!(self.formatter.element(&mut self.writer, v));
                            try!(self.serialize(v));
                        }
                    }
                }
                self.formatter.close(&mut self.writer)
            },
            Sexp::Empty => Ok(()),
        }

    }
}

pub fn to_writer<W>(writer: &mut W, value: &Sexp) -> Result<()>
    where W: io::Write
{
    let mut ser = Serializer::new(writer);
    ser.serialize(value)
}

pub fn to_writer_with_rules<W>(writer: &mut W, rules:Rules, value: &Sexp) -> Result<()>
    where W: io::Write
{
    let mut ser = Serializer::new_rules(writer, rules);
    ser.serialize(value)
}

pub fn to_vec(value:&Sexp) -> Result<Vec<u8>> {
    let mut writer = Vec::with_capacity(128);
    try!(to_writer(&mut writer, value));
    Ok(writer)
}

pub fn to_vec_with_rules(value:&Sexp, rules:Rules) -> Result<Vec<u8>> {
    let mut writer = Vec::with_capacity(128);
    try!(to_writer_with_rules(&mut writer, rules, value));
    Ok(writer)
}

pub fn to_string(value:&Sexp) -> Result<String> {
    let vec = try!(to_vec(value));
    let string = try!(String::from_utf8(vec));
    Ok(string)
}

pub fn to_string_with_rules(value:&Sexp, rules:Rules) -> Result<String> {
    let vec = try!(to_vec_with_rules(value, rules));
    let string = try!(String::from_utf8(vec));
    Ok(string)
}
<commit_msg>cosmetic fix<commit_after>// (c) 2016 Productize SPRL <joost@productize.be>

use std::io;

use formatter::*;

use Sexp;
use Result;
use encode_string;

struct Serializer<W, F=CompactFormatter> {
    writer: W,
    formatter: F,
}

// dispatches only based on Formatter
impl<W> Serializer<W>
    where W: io::Write,
{
    fn new(writer: W) -> Self {
        Serializer::with_formatter(writer, CompactFormatter)
    }
}

impl<W> Serializer<W, RulesFormatter>
    where W: io::Write,
{
    fn new_rules(writer: W, rules:Rules) -> Self {
        Serializer::with_formatter(writer, RulesFormatter::new(rules))
    }
}

impl<W, F> Serializer<W, F>
    where W: io::Write,
          F: Formatter,
{
    fn with_formatter(writer: W, formatter: F) -> Self {
        Serializer {
            writer: writer,
            formatter: formatter,
        }
    }

    fn serialize_str(&mut self, value:&str) -> Result<()> {
        write!(&mut self.writer, "{}", encode_string(value)).map_err(From::from)
    }

    fn serialize(&mut self, value:&Sexp) -> Result<()> {
        match *value {
            Sexp::String(ref s) => {
                self.serialize_str(s)
            },
            Sexp::List(ref list) => {
                let mut first = true;
                if list.is_empty() {
                    try!(self.formatter.open(&mut self.writer, None));
                } else {
                    for v in list {
                        if first {
                            try!(self.formatter.open(&mut self.writer, Some(v)));
                            try!(self.serialize(v));
                            first = false;
                        } else {
                            try!(self.formatter.element(&mut self.writer, v));
                            try!(self.serialize(v));
                        }
                    }
                }
                self.formatter.close(&mut self.writer)
            },
            Sexp::Empty => Ok(()),
        }

    }
}

pub fn to_writer<W>(writer: &mut W, value: &Sexp) -> Result<()>
    where W: io::Write
{
    let mut ser = Serializer::new(writer);
    ser.serialize(value)
}

pub fn to_writer_with_rules<W>(writer: &mut W, rules:Rules, value:&Sexp) -> Result<()>
    where W: io::Write
{
    let mut ser = Serializer::new_rules(writer, rules);
    ser.serialize(value)
}

pub fn to_vec(value:&Sexp) -> Result<Vec<u8>> {
    let mut writer = Vec::with_capacity(128);
    try!(to_writer(&mut writer, value));
    Ok(writer)
}

pub fn to_vec_with_rules(value:&Sexp, rules:Rules) -> Result<Vec<u8>> {
    let mut writer = Vec::with_capacity(128);
    try!(to_writer_with_rules(&mut writer, rules, value));
    Ok(writer)
}

pub fn to_string(value:&Sexp) -> Result<String> {
    let vec = try!(to_vec(value));
    let string = try!(String::from_utf8(vec));
    Ok(string)
}

pub fn to_string_with_rules(value:&Sexp, rules:Rules) -> Result<String> {
    let vec = try!(to_vec_with_rules(value, rules));
    let string = try!(String::from_utf8(vec));
    Ok(string)
}
<|endoftext|>