pijul 0.7.2

A patch-based distributed version control system, easy to use and fast. Command-line interface.
use clap::{SubCommand, ArgMatches, Arg};

use commands::{assert_no_containing_repo, create_repo, default_explain, StaticSubcommand};
use error::{ErrorKind, Error};
use commands::remote::{Remote, parse_remote};
use regex::Regex;
use libpijul::DEFAULT_BRANCH;
use std::io::{Write, stderr};
use std::process::exit;

pub fn invocation() -> StaticSubcommand {
    return SubCommand::with_name("clone")
        .about("clone a remote branch")
        .arg(Arg::with_name("from")
            .help("Repository to clone.")
            .required(true))
        .arg(Arg::with_name("from_branch")
            .long("from-branch")
            .help("The branch to pull from")
            .takes_value(true))
        .arg(Arg::with_name("to_branch")
            .long("to-branch")
            .help("The branch to pull into")
            .takes_value(true))
        .arg(Arg::with_name("to").help("Target."))
        .arg(Arg::with_name("port")
            .short("p")
            .long("port")
            .help("Port of the remote ssh server.")
            .takes_value(true)
            .validator(|val| {
                let x: Result<u16, _> = val.parse();
                match x {
                    Ok(_) => Ok(()),
                    Err(_) => Err(val),
                }
            }));
}
#[derive(Debug)]
pub struct Params<'a> {
    pub from: Remote<'a>,
    pub from_branch: &'a str,
    pub to: Remote<'a>,
    pub to_branch: &'a str,
}

pub fn parse_args<'a>(args: &'a ArgMatches) -> Params<'a> {
    // At least one must not use its "port" argument
    let from = parse_remote(args.value_of("from").unwrap(),
                            args.value_of("port").and_then(|x| Some(x.parse().unwrap())),
                            None,
                            None);
    let to = if let Some(to) = args.value_of("to") {
        parse_remote(to,
                     args.value_of("port").and_then(|x| Some(x.parse().unwrap())),
                     None,
                     None)
    } else {
        let basename = Regex::new(r"([^/:]*)").unwrap();
        let from = args.value_of("from").unwrap();
        if let Some(to) = basename.captures_iter(from).last().and_then(|to| to.get(1)) {
            parse_remote(to.as_str(),
                         args.value_of("port").and_then(|x| Some(x.parse().unwrap())),
                         None,
                         None)
        } else {
            panic!("Could not parse target")
        }
    };
    let from_branch = args.value_of("from_branch").unwrap_or(DEFAULT_BRANCH);
    let to_branch = args.value_of("to_branch").unwrap_or(from_branch);
    Params {
        from: from,
        from_branch: from_branch,
        to: to,
        to_branch: to_branch,
    }
}



pub fn run(args: &ArgMatches) -> Result<(), Error> {
    let args = parse_args(args);
    debug!("{:?}", args);
    match args.from {
        Remote::Local { ref path } => {
            let mut to_session = try!(args.to.session());
            debug!("remote init");
            try!(to_session.remote_init());
            debug!("pushable?");
            let pushable = to_session.pushable_patches(args.from_branch, args.to_branch, path)?;
            debug!("pushable = {:?}", pushable);
            let pushable = pushable.into_iter().map(|(h, _, _)| h).collect();
            to_session.push(path, args.to_branch, &pushable)
        }
        _ => {
            match args.to {
                Remote::Local { ref path } => {
                    // This is "darcs get"
                    try!(assert_no_containing_repo(path));
                    try!(create_repo(path));
                    let mut session = try!(args.from.session());
                    let mut pullable:Vec<_> = try!(session.pullable_patches(
                        args.from_branch,
                        args.to_branch,
                        path
                    )).iter().collect();
                    session.pull(path, args.to_branch, &mut pullable)
                }
                _ =>
                    // Clone between remote repositories.
                    unimplemented!(),
            }
        }
    }
}

pub fn explain(res: Result<(), Error>) {
    if let Err(Error(ref kind, _)) = res {
        if let ErrorKind::InARepository(ref p) = *kind {
            writeln!(stderr(), "error: Cannot clone onto / into existing repository {}", p.display()).unwrap();
            exit(1)
        }
    }
    default_explain(res)
}