<commit_before>#![feature(io)]

extern crate rustc_serialize;
extern crate docopt;
extern crate users;
extern crate glob;
extern crate tempfile;
extern crate libc;
extern crate cronparse;

use cronparse::{CrontabFile, CrontabFileError};
use cronparse::crontab::UserCrontabEntry;
use tempfile::NamedTempFile;
use docopt::Docopt;
use libc::{uid_t, gid_t};
use users::User;
use std::env;
use std::fs;
use std::io::{stdin, stdout, stderr, Write, Read, self};
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
use std::path::{PathBuf, Path};
use std::process::{Command, exit};
use std::ffi::CString;

include!(concat!(env!("OUT_DIR"), "/config.rs"));

extern "C" {
    fn chown(path: *const libc::c_char, owner: libc::uid_t, group: libc::gid_t) -> libc::c_int;
}

fn change_owner<P: AsRef<Path>>(path: P, owner: libc::uid_t, group: libc::gid_t) -> Result<(), io::Error> {
    match unsafe { chown(CString::new(path.as_ref().to_str().unwrap().as_bytes()).unwrap().as_ptr(), owner, group) } {
        0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
        _ => unreachable!()
    }
}

static USAGE: &'static str = r#"
Usage: crontab [-u <user>] -l
       crontab [-u <user>] -e [<file>]
       crontab [-u <user>] -s
       crontab [-u <user>] -r [-i]
       crontab -h | --help

Maintain crontab files for individual users

Options:

  -h, --help                Show this help message and exit.
  -u <user>, --user <user>  It specifies the name of the user whose crontab is to
                            be tweaked. If this option is not given, crontab
                            examines "your" crontab, i.e., the crontab of the
                            person executing the command. Note that su(8) can
                            confuse crontab and that if you are running inside of
                            su(8) you should always use the -u option for safety's
                            sake. The first form of this command is used to
                            install a new crontab from some named file or standard
                            input if the pseudo-filename "-" is given.
  -l, --list                The current crontab will be displayed on standard
                            output.
  -r, --remove              The current crontab will be removed.
  -e, --edit                This option is used to edit the current crontab using
                            the editor specified by the VISUAL or EDITOR
                            environment variables. After you exit from the editor,
                            the modified crontab will be installed automatically.
  -s, --show                Show all users who have a crontab.
  -i, --ask                 This option modifies the -r option to prompt the user
                            for a 'y/Y' response before actually removing the
                            crontab.
"#;

#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_file: Option<String>,
    flag_user: Option<String>,
    flag_list: bool,
    flag_remove: bool,
    flag_edit: bool,
    flag_show: bool,
    flag_ask: bool
}

fn get_editor() -> Option<String> {
    env::var("EDITOR").ok()
        .or_else(|| env::var("VISUAL").ok())
        .or_else(|| ["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"].iter()
                 .find(|editor| fs::metadata(editor).map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0).unwrap_or(false))
                 .map(|&s| s.to_owned()))
}

fn confirm(msg: &str) -> bool {
    let mut stdin = stdin();
    let mut stdout = stdout();
    loop {
        stdout.write_all(msg.as_bytes()).unwrap();
        stdout.flush().unwrap();
        let mut buf = [0u8; 1024];
        match stdin.read(&mut buf) {
            Ok(n) if n > 0 && (buf[0] == 121 || buf[0] == 89) => return true,
            Ok(n) if n > 0 && (buf[0] == 110 || buf[0] == 78) => return false,
            _ => { stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap(); },
        }
    }
}

fn list(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
    if let Err(e) = File::open(cron_file).map(|file| file.tee(stdout()).bytes().count()) {
        use std::io::ErrorKind::*;
        match e.kind() {
            NotFound => println!("no crontab for {}", cron_user.name),
            _ => println!("failed to read {}: {}", cron_file.display(), e),
        }
        return 1;
    }
    0
}

fn remove(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
    let mut stderr = stderr();

    if !args.flag_ask || confirm(&*format!("Are you sure you want to delete {} (y/n)? ", cron_file.display())) {
        if let Err(e) = fs::remove_file(cron_file) {
            use std::io::ErrorKind::*;
            match e.kind() {
                NotFound => writeln!(stderr, "no crontab for {}", cron_user.name),
                _ => writeln!(stderr, "failed to remove {}: {}", cron_file.display(), e)
            }.unwrap();
            return 1;
        }
    }
    0
}

fn show(_cron_file: &Path, _cron_user: &User, _args: &Args) -> i32 {
    let mut stderr = stderr();

    if let Ok(dir) = fs::read_dir(USERS_CRONTAB_DIR) {
        for item in dir {
            if let Ok(entry) = item {
                if let Some(name) = entry.file_name().to_str() {
                    if users::get_user_by_name(name).is_some() {
                        println!("{}", name);
                    } else {
                        writeln!(stderr, "WARNING: crontab found with no matching user: {}", name).unwrap();
                    }
                }
            }
        }
    }
    0
}

fn edit(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
    use std::io::ErrorKind::*;

    let mut stderr = stderr();

    let editor = match get_editor() {
        None => {
            writeln!(stderr, "no editor found").unwrap();
            return 1;
        },
        Some(editor) => editor
    };

    let mut tmpfile = NamedTempFile::new_in(USERS_CRONTAB_DIR).unwrap();

    if let Err(e) = File::open(cron_file).map(|file| file.tee(&mut tmpfile).bytes().count()) {
        match e.kind() {
            NotFound => tmpfile.write_all("# min hour dom month dow command\n".as_bytes()).unwrap(),
            _ => {
                writeln!(stderr, "error copying crontab file {}: {}", cron_file.display(), e).unwrap();
                return 1;
            }
        }
    }

    tmpfile.flush().unwrap();
    {
        change_owner(tmpfile.path(), cron_user.uid, cron_user.primary_group).unwrap();
        let _guard = users::switch_user_group(cron_user.uid, cron_user.primary_group);
        match Command::new(editor).arg(tmpfile.path()).status() {
            Ok(status) if status.success() => (),
            _ => {
                writeln!(stderr, "edit aborted, your edit is kept here: {}", tmpfile.path().display()).unwrap();
                return 1;
            }
        }
    }

    if let Err(err) = check_crontab_syntax(tmpfile.path()) {
        writeln!(stderr, "syntax error in new crontab file: {}", err).unwrap();
        return 1;
    }

    if let Err(err) = tmpfile.persist(cron_file) {
        writeln!(stderr, "unexpected error: {}, your edit is kept here: {}", err.error, err.file.path().display()).unwrap();
        return 1;
    }

    0
}

fn replace(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
    let mut stderr = stderr();
    let mut tmpfile = NamedTempFile::new_in(USERS_CRONTAB_DIR).unwrap();

    match args.arg_file {
        Some(ref name) if &**name == "-" => { stdin().tee(&mut tmpfile).bytes().count(); },
        Some(ref name) => { File::open(&**name).unwrap().tee(&mut tmpfile).bytes().count(); },
        None => unreachable!()
    }

    tmpfile.flush().unwrap();

    if let Err(err) = check_crontab_syntax(tmpfile.path()) {
        writeln!(stderr, "syntax error in new crontab file: {}", err).unwrap();
        return 1;
    }

    if let Err(e) = tmpfile.persist(cron_file) {
        writeln!(stderr, "error renaming {} to {}: {}", e.file.path().display(), cron_file.display(), e.error).unwrap();
        return 1;
    }

    change_owner(cron_file, cron_user.uid, cron_user.primary_group).unwrap();

    0
}

fn main() {
    let mut stderr = stderr();
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    let cron_user = match args.flag_user {
        Some(_) if users::get_current_uid() != 0 => {
            writeln!(stderr, "must be privileged to use -u").unwrap();
            exit(1);
        },
        Some(ref user) => match users::get_user_by_name(&**user) {
            Some(user) => user,
            None => {
                writeln!(stderr, "unknown user: {}", user).unwrap();
                exit(1);
            }
        },
        None => users::get_user_by_uid(users::get_current_uid()).unwrap(),
    };

    match fs::metadata(USERS_CRONTAB_DIR) {
        Ok(ref meta) if !meta.is_dir() => {
            writeln!(stderr, "{} is not a directory!", USERS_CRONTAB_DIR).unwrap();
            exit(1);
        },
        Err(_) => if let Err(_) = fs::create_dir_all(USERS_CRONTAB_DIR) {
            writeln!(stderr, "{} doesn't exist!", USERS_CRONTAB_DIR).unwrap();
            exit(1);
        },
        _ => ()
    }

    let cron_file = PathBuf::from(USERS_CRONTAB_DIR).join(cron_user.name.clone());

    exit(match args {
        Args { flag_show: true, .. } => show(&*cron_file, &cron_user, &args),
        Args { flag_list: true, .. } => list(&*cron_file, &cron_user, &args),
        Args { flag_edit: true, arg_file: None, .. } => edit(&*cron_file, &cron_user, &args),
        Args { flag_edit: true, .. } => replace(&*cron_file, &cron_user, &args),
        Args { flag_remove: true, .. } => remove(&*cron_file, &cron_user, &args),
        _ => unreachable!()
    })
}

fn check_crontab_syntax<P: AsRef<Path>>(path: P) -> Result<(), CrontabFileError> {
    match try!(CrontabFile::<UserCrontabEntry>::new(path)).find(Result::is_err) {
        Some(Err(err)) => Err(err),
        _ => Ok(())
    }
}
<commit_msg>get rid of unstable io feature<commit_after>extern crate rustc_serialize;
extern crate docopt;
extern crate users;
extern crate glob;
extern crate tempfile;
extern crate libc;
extern crate cronparse;

use cronparse::{CrontabFile, CrontabFileError};
use cronparse::crontab::UserCrontabEntry;
use tempfile::NamedTempFile;
use docopt::Docopt;
use libc::{uid_t, gid_t};
use users::User;
use std::env;
use std::fs;
use std::io::{stdin, stdout, stderr, Write, Read, self, copy};
use std::fs::File;
use std::os::unix::fs::PermissionsExt;
use std::path::{PathBuf, Path};
use std::process::{Command, exit};
use std::ffi::CString;

include!(concat!(env!("OUT_DIR"), "/config.rs"));

extern "C" {
    fn chown(path: *const libc::c_char, owner: libc::uid_t, group: libc::gid_t) -> libc::c_int;
}

fn change_owner<P: AsRef<Path>>(path: P, owner: libc::uid_t, group: libc::gid_t) -> Result<(), io::Error> {
    match unsafe { chown(CString::new(path.as_ref().to_str().unwrap().as_bytes()).unwrap().as_ptr(), owner, group) } {
        0 => Ok(()),
        -1 => Err(io::Error::last_os_error()),
        _ => unreachable!()
    }
}

static USAGE: &'static str = r#"
Usage: crontab [-u <user>] -l
       crontab [-u <user>] -e [<file>]
       crontab [-u <user>] -s
       crontab [-u <user>] -r [-i]
       crontab -h | --help

Maintain crontab files for individual users

Options:

  -h, --help                Show this help message and exit.
  -u <user>, --user <user>  It specifies the name of the user whose crontab is to
                            be tweaked. If this option is not given, crontab
                            examines "your" crontab, i.e., the crontab of the
                            person executing the command. Note that su(8) can
                            confuse crontab and that if you are running inside of
                            su(8) you should always use the -u option for safety's
                            sake. The first form of this command is used to
                            install a new crontab from some named file or standard
                            input if the pseudo-filename "-" is given.
  -l, --list                The current crontab will be displayed on standard
                            output.
  -r, --remove              The current crontab will be removed.
  -e, --edit                This option is used to edit the current crontab using
                            the editor specified by the VISUAL or EDITOR
                            environment variables. After you exit from the editor,
                            the modified crontab will be installed automatically.
  -s, --show                Show all users who have a crontab.
  -i, --ask                 This option modifies the -r option to prompt the user
                            for a 'y/Y' response before actually removing the
                            crontab.
"#;

#[derive(Debug, RustcDecodable)]
#[allow(non_snake_case)]
struct Args {
    arg_file: Option<String>,
    flag_user: Option<String>,
    flag_list: bool,
    flag_remove: bool,
    flag_edit: bool,
    flag_show: bool,
    flag_ask: bool
}

fn get_editor() -> Option<String> {
    env::var("EDITOR").ok()
        .or_else(|| env::var("VISUAL").ok())
        .or_else(|| ["/usr/bin/editor", "/usr/bin/vim", "/usr/bin/nano", "/usr/bin/mcedit"].iter()
                 .find(|editor| fs::metadata(editor).map(|meta| meta.is_file() && meta.permissions().mode() & 0o0111 != 0).unwrap_or(false))
                 .map(|&s| s.to_owned()))
}

fn confirm(msg: &str) -> bool {
    let mut stdin = stdin();
    let mut stdout = stdout();
    loop {
        stdout.write_all(msg.as_bytes()).unwrap();
        stdout.flush().unwrap();
        let mut buf = [0u8; 1024];
        match stdin.read(&mut buf) {
            Ok(n) if n > 0 && (buf[0] == 121 || buf[0] == 89) => return true,
            Ok(n) if n > 0 && (buf[0] == 110 || buf[0] == 78) => return false,
            _ => { stdout.write_all("Please reply \"y\" or \"n\"\n".as_bytes()).unwrap(); },
        }
    }
}

fn list(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
    if let Err(e) = File::open(cron_file).map(|mut file| copy(&mut file, &mut stdout())) {
        use std::io::ErrorKind::*;
        match e.kind() {
            NotFound => println!("no crontab for {}", cron_user.name),
            _ => println!("failed to read {}: {}", cron_file.display(), e),
        }
        return 1;
    }
    0
}

fn remove(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
    let mut stderr = stderr();

    if !args.flag_ask || confirm(&*format!("Are you sure you want to delete {} (y/n)? ", cron_file.display())) {
        if let Err(e) = fs::remove_file(cron_file) {
            use std::io::ErrorKind::*;
            match e.kind() {
                NotFound => writeln!(stderr, "no crontab for {}", cron_user.name),
                _ => writeln!(stderr, "failed to remove {}: {}", cron_file.display(), e)
            }.unwrap();
            return 1;
        }
    }
    0
}

fn show(_cron_file: &Path, _cron_user: &User, _args: &Args) -> i32 {
    let mut stderr = stderr();

    if let Ok(dir) = fs::read_dir(USERS_CRONTAB_DIR) {
        for item in dir {
            if let Ok(entry) = item {
                if let Some(name) = entry.file_name().to_str() {
                    if users::get_user_by_name(name).is_some() {
                        println!("{}", name);
                    } else {
                        writeln!(stderr, "WARNING: crontab found with no matching user: {}", name).unwrap();
                    }
                }
            }
        }
    }
    0
}

fn edit(cron_file: &Path, cron_user: &User, _args: &Args) -> i32 {
    use std::io::ErrorKind::*;

    let mut stderr = stderr();

    let editor = match get_editor() {
        None => {
            writeln!(stderr, "no editor found").unwrap();
            return 1;
        },
        Some(editor) => editor
    };

    let mut tmpfile = NamedTempFile::new_in(USERS_CRONTAB_DIR).unwrap();

    if let Err(e) = File::open(cron_file).map(|mut file| copy(&mut file, &mut tmpfile)) {
        match e.kind() {
            NotFound => tmpfile.write_all("# min hour dom month dow command\n".as_bytes()).unwrap(),
            _ => {
                writeln!(stderr, "error copying crontab file {}: {}", cron_file.display(), e).unwrap();
                return 1;
            }
        }
    }

    tmpfile.flush().unwrap();
    {
        change_owner(tmpfile.path(), cron_user.uid, cron_user.primary_group).unwrap();
        let _guard = users::switch_user_group(cron_user.uid, cron_user.primary_group);
        match Command::new(editor).arg(tmpfile.path()).status() {
            Ok(status) if status.success() => (),
            _ => {
                writeln!(stderr, "edit aborted, your edit is kept here: {}", tmpfile.path().display()).unwrap();
                return 1;
            }
        }
    }

    if let Err(err) = check_crontab_syntax(tmpfile.path()) {
        writeln!(stderr, "syntax error in new crontab file: {}", err).unwrap();
        return 1;
    }

    if let Err(err) = tmpfile.persist(cron_file) {
        writeln!(stderr, "unexpected error: {}, your edit is kept here: {}", err.error, err.file.path().display()).unwrap();
        return 1;
    }

    0
}

fn replace(cron_file: &Path, cron_user: &User, args: &Args) -> i32 {
    let mut stderr = stderr();
    let mut tmpfile = NamedTempFile::new_in(USERS_CRONTAB_DIR).unwrap();

    match args.arg_file {
        Some(ref name) if &**name == "-" => { let _ = copy(&mut stdin(), &mut tmpfile); },
        Some(ref name) => { let _ = copy(&mut File::open(&**name).unwrap(), &mut tmpfile); },
        None => unreachable!()
    }

    tmpfile.flush().unwrap();

    if let Err(err) = check_crontab_syntax(tmpfile.path()) {
        writeln!(stderr, "syntax error in new crontab file: {}", err).unwrap();
        return 1;
    }

    if let Err(e) = tmpfile.persist(cron_file) {
        writeln!(stderr, "error renaming {} to {}: {}", e.file.path().display(), cron_file.display(), e.error).unwrap();
        return 1;
    }

    change_owner(cron_file, cron_user.uid, cron_user.primary_group).unwrap();

    0
}

fn main() {
    let mut stderr = stderr();
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());

    let cron_user = match args.flag_user {
        Some(_) if users::get_current_uid() != 0 => {
            writeln!(stderr, "must be privileged to use -u").unwrap();
            exit(1);
        },
        Some(ref user) => match users::get_user_by_name(&**user) {
            Some(user) => user,
            None => {
                writeln!(stderr, "unknown user: {}", user).unwrap();
                exit(1);
            }
        },
        None => users::get_user_by_uid(users::get_current_uid()).unwrap(),
    };

    match fs::metadata(USERS_CRONTAB_DIR) {
        Ok(ref meta) if !meta.is_dir() => {
            writeln!(stderr, "{} is not a directory!", USERS_CRONTAB_DIR).unwrap();
            exit(1);
        },
        Err(_) => if let Err(_) = fs::create_dir_all(USERS_CRONTAB_DIR) {
            writeln!(stderr, "{} doesn't exist!", USERS_CRONTAB_DIR).unwrap();
            exit(1);
        },
        _ => ()
    }

    let cron_file = PathBuf::from(USERS_CRONTAB_DIR).join(cron_user.name.clone());

    exit(match args {
        Args { flag_show: true, .. } => show(&*cron_file, &cron_user, &args),
        Args { flag_list: true, .. } => list(&*cron_file, &cron_user, &args),
        Args { flag_edit: true, arg_file: None, .. } => edit(&*cron_file, &cron_user, &args),
        Args { flag_edit: true, .. } => replace(&*cron_file, &cron_user, &args),
        Args { flag_remove: true, .. } => remove(&*cron_file, &cron_user, &args),
        _ => unreachable!()
    })
}

fn check_crontab_syntax<P: AsRef<Path>>(path: P) -> Result<(), CrontabFileError> {
    match try!(CrontabFile::<UserCrontabEntry>::new(path)).find(Result::is_err) {
        Some(Err(err)) => Err(err),
        _ => Ok(())
    }
}
<|endoftext|>


<commit_before>use file_util::copy;

/// Reverts resolv.conf and hosts files after they might be replaces by
/// come container unpacking or copying
pub fn revert_name_files() -> Result<(), String> {
    copy("/etc/resolv.conf", "/vagga/root/etc/resolv.conf")
        .map_err(|e| format!("Error copying /etc/resolv.conf: {}", e))?;

    copy("/etc/hosts", "/vagga/root/etc/hosts")
        .map_err(|e| format!("Error copying /etc/hosts: {}", e))?;

    Ok(())
}
<commit_msg>Fixes bug when resolv.conf or hosts replaced by some weird things (like a symlink)<commit_after>use file_util::copy;
use std::fs::symlink_metadata;

/// Reverts resolv.conf and hosts files after they might be replaces by
/// come container unpacking or copying
pub fn revert_name_files() -> Result<(), String> {

    let md = symlink_metadata("/vagga/root/etc/resolv.conf");
    if md.is_err() || matches!(md, Ok(ref t) if t.is_file()) {
        copy("/etc/resolv.conf", "/vagga/root/etc/resolv.conf")
            .map_err(|e| format!("Error copying /etc/resolv.conf: {}", e))?;
    } else {
        warn!("The `/etc/resolv.conf` is not a file, we avoid replacing it. \
               Under certain conditions this may mean that DNS does not work \
               in the container.")
    }

    let md = symlink_metadata("/vagga/root/etc/hosts");
    if md.is_err() || matches!(md, Ok(ref t) if t.is_file()) {
        copy("/etc/hosts", "/vagga/root/etc/hosts")
            .map_err(|e| format!("Error copying /etc/hosts: {}", e))?;
    } else {
        warn!("The `/etc/hosts` is not a file, we avoid replacing it. \
               Under certain conditions this may mean that DNS works badly \
               in the container.")
    }

    Ok(())
}
<|endoftext|>


<commit_before>use std::default::Default;
use std::path::Path;
use std::process::exit;

use config::read_config;
use config::Settings;
use config::builders::Builder as B;
use config::builders::Source as S;
use argparse::{ArgumentParser, Store, StoreTrue};
use self::context::{Context};
use self::bld::{BuildCommand};
use self::guard::Guard;

mod context;
mod bld;
mod download;
mod tarcmd;
mod commands {
    pub mod ubuntu;
    pub mod generic;
    pub mod alpine;
    pub mod pip;
    pub mod npm;
    pub mod vcs;
    pub mod download;
}
mod capsule;
mod packages;
mod timer;
mod distrib;
mod guard;
mod error;

pub fn run() -> i32 {
    let mut container: String = "".to_string();
    let mut settings: Settings = Default::default();
    let mut sources_only: bool = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            A tool which builds containers
            ");
        ap.refer(&mut container)
          .add_argument("container", Store,
                "A container to version")
          .required();
        ap.refer(&mut sources_only)
          .add_option(&["--sources-only"], StoreTrue,
                "Only fetch sources, do not build container");
        ap.refer(&mut settings)
          .add_option(&["--settings"], Store,
                "User settings for the container build");
        match ap.parse_args() {
            Ok(()) => {}
            Err(0) => return 0,
            Err(_) => return 122,
        }
    }

    if sources_only {
        _fetch_sources(&container, settings)
            .map(|()| 0)
            .map_err(|e| error!("Error fetching sources {:?}: {}",
                                container, e))
            .unwrap_or(1)
    } else {
        _build(&container, settings)
            .map(|()| 0)
            .map_err(|e| error!("Error building container {:?}: {}",
                                container, e))
            .unwrap_or(1)
    }
}

fn _build(container: &String, settings: Settings) -> Result<(), String> {
    // TODO(tailhook) read also config from /work/.vagga/vagga.yaml
    let cfg = read_config(&Path::new("/work/vagga.yaml")).ok()
        .expect("Error parsing configuration file");  // TODO
    let cont = cfg.containers.get(container)
        .expect("Container not found");  // TODO

    Guard::build(Context::new(&cfg, container.clone(), cont, settings))
    .map_err(|e| e.to_string())
}

fn _fetch_sources(container: &String, settings: Settings)
    -> Result<(), String>
{
    // TODO(tailhook) read also config from /work/.vagga/vagga.yaml
    let cfg = read_config(&Path::new("/work/vagga.yaml")).ok()
        .expect("Error parsing configuration file");  // TODO
    let cont = cfg.containers.get(container)
        .expect("Container not found");  // TODO
    let mut caps = Default::default();

    for b in cont.setup.iter() {
        match b {
            &B::SubConfig(ref cfg) => {
                if let S::Git(ref git) = cfg.source {
                    try!(commands::vcs::fetch_git_source(
                        &mut caps, &settings, git));
                }
            }
            _ => {}
        }
    }

    Ok(())
}

pub fn main() {
    let val = run();
    exit(val);
}
<commit_msg>A temporary solution for crash of random generator (#161)<commit_after>use std::default::Default;
use std::path::Path;
use std::process::exit;
use rand;

use config::read_config;
use config::Settings;
use config::builders::Builder as B;
use config::builders::Source as S;
use argparse::{ArgumentParser, Store, StoreTrue};
use self::context::{Context};
use self::bld::{BuildCommand};
use self::guard::Guard;

mod context;
mod bld;
mod download;
mod tarcmd;
mod commands {
    pub mod ubuntu;
    pub mod generic;
    pub mod alpine;
    pub mod pip;
    pub mod npm;
    pub mod vcs;
    pub mod download;
}
mod capsule;
mod packages;
mod timer;
mod distrib;
mod guard;
mod error;

pub fn run() -> i32 {
    // Initialize thread random generator to avoid stack overflow (see #161)
    rand::thread_rng();

    let mut container: String = "".to_string();
    let mut settings: Settings = Default::default();
    let mut sources_only: bool = false;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("
            A tool which builds containers
            ");
        ap.refer(&mut container)
          .add_argument("container", Store,
                "A container to version")
          .required();
        ap.refer(&mut sources_only)
          .add_option(&["--sources-only"], StoreTrue,
                "Only fetch sources, do not build container");
        ap.refer(&mut settings)
          .add_option(&["--settings"], Store,
                "User settings for the container build");
        match ap.parse_args() {
            Ok(()) => {}
            Err(0) => return 0,
            Err(_) => return 122,
        }
    }

    if sources_only {
        _fetch_sources(&container, settings)
            .map(|()| 0)
            .map_err(|e| error!("Error fetching sources {:?}: {}",
                                container, e))
            .unwrap_or(1)
    } else {
        _build(&container, settings)
            .map(|()| 0)
            .map_err(|e| error!("Error building container {:?}: {}",
                                container, e))
            .unwrap_or(1)
    }
}

fn _build(container: &String, settings: Settings) -> Result<(), String> {
    // TODO(tailhook) read also config from /work/.vagga/vagga.yaml
    let cfg = read_config(&Path::new("/work/vagga.yaml")).ok()
        .expect("Error parsing configuration file");  // TODO
    let cont = cfg.containers.get(container)
        .expect("Container not found");  // TODO

    Guard::build(Context::new(&cfg, container.clone(), cont, settings))
    .map_err(|e| e.to_string())
}

fn _fetch_sources(container: &String, settings: Settings)
    -> Result<(), String>
{
    // TODO(tailhook) read also config from /work/.vagga/vagga.yaml
    let cfg = read_config(&Path::new("/work/vagga.yaml")).ok()
        .expect("Error parsing configuration file");  // TODO
    let cont = cfg.containers.get(container)
        .expect("Container not found");  // TODO
    let mut caps = Default::default();

    for b in cont.setup.iter() {
        match b {
            &B::SubConfig(ref cfg) => {
                if let S::Git(ref git) = cfg.source {
                    try!(commands::vcs::fetch_git_source(
                        &mut caps, &settings, git));
                }
            }
            _ => {}
        }
    }

    Ok(())
}

pub fn main() {
    let val = run();
    exit(val);
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::collections::HashMap;

pub fn balance_bots_1(file: &str) -> usize {
    parse_cmds(file)
}

pub fn balance_bots_2(file: &str) -> usize {
    parse_cmds(file)
}

fn parse_cmds(file: &str) -> usize {
    let input = File::open(file).expect("File open fail.");
    let reader = BufReader::new(input);

    let lines = reader.lines()
        .filter_map(Result::ok);

    let mut bots = HashMap::new();
    let mut out_bins = HashMap::new();

    let (transfer_tokens, input_tokens): (Vec<Vec<Token>>, Vec<Vec<Token>>) = lines.map(|line| {
            let mut words = line.split_whitespace();
            let mut tokens = Vec::new();
            loop {
                if let Some(w) = words.next() {
                    match w {
                        "value" => {
                            if let Some(v) = words.next() {
                                if let Ok(n) = v.parse::<usize>() {
                                    tokens.push(Token::Value(n))
                                }
                            }
                        }
                        "bot" => {
                            if let Some(v) = words.next() {
                                if let Ok(n) = v.parse::<usize>() {
                                    tokens.push(Token::Bot(n))
                                }
                            }
                        }
                        "output" => {
                            if let Some(v) = words.next() {
                                if let Ok(n) = v.parse::<usize>() {
                                    tokens.push(Token::Output(n))
                                }
                            }
                        }
                        _ => {}
                    };
                } else {
                    return tokens;
                }
            }
        })
        .partition(|ref t| t.len() > 2);

    // Load MicroChips from input bins
    input_tokens.iter().map(|tokens| {
        let val;
        match tokens[0] {
            Token::Value(n) => val = MicroChip(n),
            Token::Bot(_) => unreachable!(),
            Token::Output(_) => unreachable!(),
        }
        match tokens[1] {
            Token::Bot(n) => {
                bots.insert(n, Bot { chips: vec![val] });
            }
            Token::Output(_) => unreachable!(),
            Token::Value(_) => unreachable!(),
        }
    });

    // Process transfers
    transfer_tokens.iter().map(|tokens| {
        let low;
        let high;
        match tokens[0] {
            Token::Bot(n) => {
                let bot = bots.entry(n).or_insert(Bot { chips: Vec::new() });
                low = bot.low();
                high = bot.high();
            }
            Token::Value(_) => unreachable!(),
            Token::Output(_) => unreachable!(),
        }
        if let Some(low) = low {
            match tokens[1] {
                Token::Bot(n) => {
                    let mut to = bots.entry(n).or_insert(Bot { chips: Vec::new() });
                    if to.chips.len() > 0 {
                        to.chips.push(low);
                    }
                }
                Token::Output(n) => {
                    let mut to = out_bins.entry(n).or_insert(OutputBin { chip: None });
                    to.chip = Some(low);
                }
                Token::Value(_) => unreachable!(),
            }
        }
        if let Some(high) = high {
            match tokens[2] {
                Token::Bot(n) => {
                    let mut to = bots.entry(n).or_insert(Bot { chips: Vec::new() });
                    if to.chips.len() > 0 {
                        to.chips.push(high);
                    }
                }
                Token::Output(n) => {
                    let mut to = out_bins.entry(n).or_insert(OutputBin { chip: None });
                    to.chip = Some(high);
                }
                Token::Value(_) => unreachable!(),
            }
        }
    });

    42
}

#[derive(Debug)]
enum Token {
    Value(usize),
    Bot(usize),
    Output(usize),
}

#[derive(Eq, PartialEq, PartialOrd, Ord, Clone)]
struct MicroChip(usize);

struct Bot {
    chips: Vec<MicroChip>,
}

impl Bot {
    fn low(&mut self) -> Option<MicroChip> {
        self.chips.iter().min().cloned()
    }

    fn high(&mut self) -> Option<MicroChip> {
        self.chips.iter().max().cloned()
    }
}

struct OutputBin {
    chip: Option<MicroChip>,
}

trait Entity {}

impl Entity for Bot {}
impl Entity for OutputBin {}
<commit_msg>Update day 10 2016 with a better model.<commit_after>use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::collections::HashMap;

pub fn balance_bots_1(file: &str) -> usize {
    parse_cmds(file)
}

pub fn balance_bots_2(file: &str) -> usize {
    parse_cmds(file)
}

fn parse_cmds(file: &str) -> usize {
    let input = File::open(file).expect("File open fail.");
    let reader = BufReader::new(input);

    let lines = reader.lines()
        .filter_map(Result::ok);

    let mut bots = HashMap::new();
    let mut output_bins = HashMap::new();
    let mut input_bins = HashMap::new();
    for line in lines {
        let mut words = line.split_whitespace();
        loop {
            if let Some(word) = words.next() {
                match word {
                    "value" => {
                        if let Some(value) = words.next() {
                            if let Ok(value) = value.parse::<usize>() {
                                if let Some(id) = words.nth(3) {
                                    if let Ok(id) = id.parse::<usize>() {
                                        let input_bin = input_bins.entry(value).or_insert(InputBin::new());
                                        input_bin.chip = Some(Microchip(value));
                                        let bot = bots.entry(id).or_insert(Bot::new());
                                        bot.get = GetInstruction {
                                            from: Some(Entity::InputBin(value)),
                                        };
                                    }
                                }
                            }
                        }
                    },
                    "bot" => {
                        if let Some(bot_id) = words.next() {
                            if let Ok(bot_id) = bot_id.parse::<usize>() {
                                if let Some(low) = words.nth(3) {
                                    if let Some(low_id) = words.next() {
                                        if let Ok(low_id) = low_id.parse::<usize>() {
                                            if let Some(high) = words.nth(3) {
                                                if let Some(high_id) = words.next() {
                                                    if let Ok(high_id) = high_id.parse::<usize>() {
                                                        match low {
                                                            "bot" => {
                                                                let bot = bots.entry(bot_id).or_insert(Bot::new());
                                                                bot.put.low = Some(Entity::Bot(low_id));
                                                            }
                                                            "output" => {
                                                                output_bins.entry(low_id).or_insert(OutputBin::new());
                                                                let bot = bots.entry(bot_id).or_insert(Bot::new());
                                                                bot.put.low = Some(Entity::OutputBin(low_id));
                                                            }
                                                            _ => {},
                                                        };
                                                        match high {
                                                            "bot" => {
                                                                let bot = bots.entry(bot_id).or_insert(Bot::new());
                                                                bot.put.high = Some(Entity::Bot(high_id));
                                                            }
                                                            "output" => {
                                                                output_bins.entry(high_id).or_insert(OutputBin::new());
                                                                let bot = bots.entry(bot_id).or_insert(Bot::new());
                                                                bot.put.high = Some(Entity::OutputBin(high_id));
                                                            }
                                                            _ => {},
                                                        };
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    },
                    _ => {},
                }
            } else {
                break
            }
        }
    }
    println!("{:?}", bots);
    println!("{:?}", output_bins);
    println!("{:?}", input_bins);

    42
}

#[derive(Eq, PartialEq, PartialOrd, Ord, Clone, Debug)]
struct Microchip(usize);

#[derive(Debug)]
struct Bot {
    chips: Vec<Microchip>,
    get: GetInstruction,
    put: PutInstruction,
}

#[derive(Debug)]
struct GetInstruction {
    from: Option<Entity>,
}

#[derive(Debug)]
struct PutInstruction {
    low: Option<Entity>,
    high: Option<Entity>,
}

#[derive(Debug)]
struct InputBin {
    chip: Option<Microchip>,
}

#[derive(Debug)]
struct OutputBin {
    chip: Option<Microchip>,
}

#[derive(Debug)]
enum Entity {
    Bot(usize),
    OutputBin(usize),
    InputBin(usize),
}

impl Bot {
    fn new() -> Self {
        Bot {
            chips: Vec::new(),
            get: GetInstruction {
                from: None,
            },
            put: PutInstruction {
                low: None,
                high: None,
            },
        }
    }

    fn low(&mut self) -> Option<Microchip> {
        self.chips.iter().min().cloned()
    }

    fn high(&mut self) -> Option<Microchip> {
        self.chips.iter().max().cloned()
    }
}

impl InputBin {
    fn new() -> Self {
        InputBin {
            chip: None,
        }
    }
}

impl OutputBin {
    fn new() -> Self {
        OutputBin {
            chip: None,
        }
    }
}
<|endoftext|>


<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.

enum Nil {NilValue}
struct Cons<T> {head:int, tail:T}
trait Dot {fn dot(&self, other:Self) -> int;}
impl Dot for Nil {
  fn dot(&self, _:Nil) -> int {0}
}
impl<T:Dot> Dot for Cons<T> {
  fn dot(&self, other:Cons<T>) -> int {
    self.head * other.head + self.tail.dot(other.tail)
  }
}
fn test<T:Dot> (n:int, i:int, first:T, second:T) ->int {
  match n {    0 => {first.dot(second)}
      //~^ ERROR: reached the recursion limit during monomorphization
      // Error message should be here. It should be a type error
      // to instantiate `test` at a type other than T. (See #4287)
    _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}
  }
}
pub fn main() {
  let n = test(1, 0, Nil::NilValue, Nil::NilValue);
  println!("{}", n);
}
<commit_msg>Update compile-fail test with new message that is generated as a result of using `ty::type_is_sized`<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.

//~^^^^^^^^^^ ERROR overflow
//
// We also get a second error message at the top of file (dummy
// span). This is not helpful, but also kind of annoying to prevent,
// so for now just live with it, since we also get a second message
// that is more helpful.

enum Nil {NilValue}
struct Cons<T> {head:int, tail:T}
trait Dot {fn dot(&self, other:Self) -> int;}
impl Dot for Nil {
  fn dot(&self, _:Nil) -> int {0}
}
impl<T:Dot> Dot for Cons<T> {
  fn dot(&self, other:Cons<T>) -> int {
    self.head * other.head + self.tail.dot(other.tail)
  }
}
fn test<T:Dot> (n:int, i:int, first:T, second:T) ->int {
  match n {    0 => {first.dot(second)}
      //~^ ERROR: reached the recursion limit during monomorphization
      // Error message should be here. It should be a type error
      // to instantiate `test` at a type other than T. (See #4287)
    _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})}
  }
}
pub fn main() {
  let n = test(1, 0, Nil::NilValue, Nil::NilValue);
  println!("{}", n);
}
<|endoftext|>


<commit_before>
use ::prelude::*;
use std::rc::Rc;
use syn::*;

use ast_converters::*;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction { In, Out, Retval }

pub struct TypeConversion {

    /// Possible temporary values that need to be kept alive for the duration
    /// of the conversion result usage.
    pub temporary: Option<TokenStream>,

    /// Conversion result value. Possibly referencing the temporary value.
    pub value : TokenStream,
}

#[derive(PartialEq, Eq, Debug, Hash)]
pub struct ModelTypeSystemConfig {
    pub effective_system : ModelTypeSystem,
    pub is_default : bool,
}

impl ModelTypeSystemConfig {
    pub fn get_unique_name( &self, base : &str ) -> String {
        match self.is_default {
            true => base.to_string(),
            false => format!( "{}_{:?}", base, self.effective_system ),
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
pub enum ModelTypeSystem {

    /// COM Automation compatible type system.
    Automation,

    /// Raw type system.
    Raw,
}

impl ModelTypeSystem {

    /// Converts the model type system into public type system tokens.
    pub fn as_typesystem_tokens( self ) -> TokenStream {
        match self {
            ModelTypeSystem::Automation =>
                    quote!( ::intercom::TypeSystem::Automation ),
            ModelTypeSystem::Raw =>
                    quote!( ::intercom::TypeSystem::Raw ),
        }
    }
}

/// Type usage context.
pub struct TypeContext {
    dir: Direction,
    type_system: ModelTypeSystem,
}

impl TypeContext {
    pub fn new( dir : Direction, type_system : ModelTypeSystem ) -> TypeContext {
        TypeContext { dir, type_system }
    }
}

/// Defines Type-specific logic for handling the various parameter types in the
/// Rust/COM interface.
pub trait TypeHandler {

    /// The Rust type.
    fn rust_ty( &self ) -> Type;

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        self.rust_ty()
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Gets the default value for the type.
    fn default_value( &self ) -> TokenStream
    {
        match self.rust_ty() {
            Type::Path( ref p ) => {
                let ident = p.path.get_ident().unwrap();
                let name = ident.to_string();
                match name.as_ref() {
                    "c_void"
                        | "RawComPtr"
                        => quote!( ::std::ptr::null_mut() ),
                    _ => quote!( Default::default() )
                }
            },
            _ => quote!( Default::default() )
        }
    }
}

/// Identity parameter handler.
///
/// No special logic.
struct IdentityParam( Type );

impl TypeHandler for IdentityParam {
    fn rust_ty( &self ) -> Type { self.0.clone() }
}

/// `ComItf` parameter handler. Supports `ComItf` Rust type and ensures the this
/// to/from `RawComPtr` COM type.
struct ComItfParam { ty: Type, context: TypeContext }

impl TypeHandler for ComItfParam {

    fn rust_ty( &self ) -> Type { self.ty.clone() }

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        let rust_ty = self.rust_ty();

        // Extract the interface type T from the ComItf<T> type definition
        use syn;
        let itf_ty = match rust_ty {
            syn::Type::Path( path ) =>
                match path.path.segments.last().unwrap().value().arguments {
                    syn::PathArguments::AngleBracketed( ref ab ) =>
                            match ab.args.last().unwrap().value() {
                                syn::GenericArgument::Type( ref t ) => t.clone(),
                                _ => panic!( "ComItf generic argument must be type" ),
                            },
                    _ => panic!( "ComItf type parameter must be angle bracketed" ),
                },
            _ => panic!( "ComItf type parameter must be a type path" ),
        };

        // Construct the final InterfacePtr<T> type.
        parse_quote!( ::intercom::raw::InterfacePtr< #itf_ty > )
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::intercom::raw::InterfacePtr::new( ::std::ptr::null_mut() ) )
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::wrap( #ident, #ts ) ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::ptr( &#ident.into(), #ts ) )
        }
    }
}

/// `bool` parameter handler. Supports `VARIANT_BOOL` for automation type system.
struct BoolParam { context: TypeContext }

impl TypeHandler for BoolParam {

    fn rust_ty( &self ) -> Type { parse_quote!( bool ) }

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        match self.context.type_system {
            ModelTypeSystem::Automation => parse_quote!( ::intercom::raw::VariantBool ),
            ModelTypeSystem::Raw => parse_quote!( bool ),
        }
    }

    fn default_value( &self ) -> TokenStream
    {
        match self.context.type_system {
            ModelTypeSystem::Automation => quote!( false.into() ),
            ModelTypeSystem::Raw => quote!( false ),
        }
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }
}

/// `Variant` parameter handler. Supports `VARIANT` for automation type system.
struct VariantParam { ty: Type }

impl TypeHandler for VariantParam {

    fn rust_ty( &self ) -> Type { self.ty.clone() }

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        parse_quote!( ::intercom::raw::Variant )
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.com_into()? )
        }
    }
}

/// String parameter handler. Converts between Rust String and COM BSTR types.
struct StringParam { ty: Type, context: TypeContext }
impl TypeHandler for StringParam
{
    fn rust_ty( &self ) -> Type { self.ty.clone() }

    fn com_ty( &self ) -> Type
    {
        match self.context.type_system {
            ModelTypeSystem::Automation => match self.context.dir {
                Direction::In => parse_quote!( ::intercom::raw::InBSTR ),
                Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutBSTR ),
            },
            ModelTypeSystem::Raw => match self.context.dir {
                Direction::In => parse_quote!( ::intercom::raw::InCStr ),
                Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutCStr ),
            },
        }
    }

    fn com_to_rust( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let str_type = match self.context.type_system {
                    ModelTypeSystem::Automation => quote!( BStr ),
                    ModelTypeSystem::Raw => quote!( CStr ),
                };

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::#str_type );
                let to_intermediate = quote!( ::intercom::#str_type::from_ptr( #ident ) );
                let as_trait = quote!( < #target_ty as ::intercom::FromWithTemporary< #intermediate_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #to_intermediate )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )? ),
                }
            },
            Direction::Out | Direction::Retval => {

                // The type system input 'ident' should represent either a BString or CString
                // depending on the type system.
                let str_type = match self.context.type_system {
                    ModelTypeSystem::Automation => quote!( BString::from_ptr ),
                    ModelTypeSystem::Raw => quote!( CString::from_raw ),
                };

                // Get the type system string as Rust string.
                let ts_string = quote!( ::intercom::#str_type( #ident ) );

                // Convert the TS string into whatever string type the method requires.
                TypeConversion {
                    temporary: None,
                    value: quote!( #ts_string.com_into()? ),
                }
            },
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let str_type = match self.context.type_system {
                    ModelTypeSystem::Automation => quote!( BStr ),
                    ModelTypeSystem::Raw => quote!( CStr ),
                };

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::#str_type );
                let as_trait = quote!( < #intermediate_ty as ::intercom::FromWithTemporary< #target_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #ident )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )?.as_ptr() ),
                }
            },
            Direction::Out | Direction::Retval => {

                // The Rust string value `ident` must be first converted into a type system
                // compatible Rust string, either BString or CSTring.
                // depending on the type system.
                let str_type = match self.context.type_system {
                    ModelTypeSystem::Automation => quote!( BString ),
                    ModelTypeSystem::Raw => quote!( CString ),
                };

                // Convert the `ident` into the required string type.
                let ts_string = quote!( ::intercom::ComInto::< ::intercom::#str_type >::com_into( #ident )? );

                TypeConversion {
                    temporary: None,
                    value: match self.context.type_system {
                        ModelTypeSystem::Automation =>
                            quote!( #ts_string.into_ptr() ),
                        ModelTypeSystem::Raw =>
                            quote!( #ts_string.into_raw() ),
                    }
                }
            },
        }
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::std::ptr::null_mut() )
    }
}

/// Resolves the `TypeHandler` to use.
pub fn get_ty_handler(
    arg_ty : &Type,
    context : TypeContext,
) -> Rc<TypeHandler>
{
    let type_info = ::type_parser::parse( arg_ty )
            .unwrap_or_else( || panic!( "Type {:?} could not be parsed.", arg_ty ) );

    map_by_name(
            type_info.get_name().as_ref(), type_info.original.clone(),
            context )
}

/// Selects type handler based on the name of the type.
fn map_by_name(
    name: &str,
    original_type: Type,
    context: TypeContext,
) -> Rc<TypeHandler> {

    match name {

        "ComItf" => Rc::new( ComItfParam { ty: original_type, context } ),
        "CString" | "CStr" | "BString" | "BStr" | "String" | "str" =>
            Rc::new( StringParam { ty: original_type, context } ),
        "bool" =>
            Rc::new( BoolParam { context } ),
        "Variant" =>
            Rc::new( VariantParam { ty: original_type } ),
        // "str" => Rc::new( StringRefParam( original_type ) ),

        // Unknown. Use IdentityParam.
        _ => Rc::new( IdentityParam( original_type ) )
    }

}
<commit_msg>Handle pointer type defaults in tyhandler<commit_after>
use ::prelude::*;
use std::rc::Rc;
use syn::*;

use ast_converters::*;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction { In, Out, Retval }

pub struct TypeConversion {

    /// Possible temporary values that need to be kept alive for the duration
    /// of the conversion result usage.
    pub temporary: Option<TokenStream>,

    /// Conversion result value. Possibly referencing the temporary value.
    pub value : TokenStream,
}

#[derive(PartialEq, Eq, Debug, Hash)]
pub struct ModelTypeSystemConfig {
    pub effective_system : ModelTypeSystem,
    pub is_default : bool,
}

impl ModelTypeSystemConfig {
    pub fn get_unique_name( &self, base : &str ) -> String {
        match self.is_default {
            true => base.to_string(),
            false => format!( "{}_{:?}", base, self.effective_system ),
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, PartialOrd, Ord)]
pub enum ModelTypeSystem {

    /// COM Automation compatible type system.
    Automation,

    /// Raw type system.
    Raw,
}

impl ModelTypeSystem {

    /// Converts the model type system into public type system tokens.
    pub fn as_typesystem_tokens( self ) -> TokenStream {
        match self {
            ModelTypeSystem::Automation =>
                    quote!( ::intercom::TypeSystem::Automation ),
            ModelTypeSystem::Raw =>
                    quote!( ::intercom::TypeSystem::Raw ),
        }
    }
}

/// Type usage context.
pub struct TypeContext {
    dir: Direction,
    type_system: ModelTypeSystem,
}

impl TypeContext {
    pub fn new( dir : Direction, type_system : ModelTypeSystem ) -> TypeContext {
        TypeContext { dir, type_system }
    }
}

/// Defines Type-specific logic for handling the various parameter types in the
/// Rust/COM interface.
pub trait TypeHandler {

    /// The Rust type.
    fn rust_ty( &self ) -> Type;

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        self.rust_ty()
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Gets the default value for the type.
    fn default_value( &self ) -> TokenStream
    {
        match self.rust_ty() {
            Type::Path( ref p ) => {
                let ident = p.path.get_ident().unwrap();
                let name = ident.to_string();
                match name.as_ref() {
                    "RawComPtr" => quote!( ::std::ptr::null_mut() ),
                    _ => quote!( Default::default() )
                }
            },
            Type::Ptr( .. ) => quote!( ::std::ptr::null_mut() ),
            _ => quote!( Default::default() )
        }
    }
}

/// Identity parameter handler.
///
/// No special logic.
struct IdentityParam( Type );

impl TypeHandler for IdentityParam {
    fn rust_ty( &self ) -> Type { self.0.clone() }
}

/// `ComItf` parameter handler. Supports `ComItf` Rust type and ensures the this
/// to/from `RawComPtr` COM type.
struct ComItfParam { ty: Type, context: TypeContext }

impl TypeHandler for ComItfParam {

    fn rust_ty( &self ) -> Type { self.ty.clone() }

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        let rust_ty = self.rust_ty();

        // Extract the interface type T from the ComItf<T> type definition
        use syn;
        let itf_ty = match rust_ty {
            syn::Type::Path( path ) =>
                match path.path.segments.last().unwrap().value().arguments {
                    syn::PathArguments::AngleBracketed( ref ab ) =>
                            match ab.args.last().unwrap().value() {
                                syn::GenericArgument::Type( ref t ) => t.clone(),
                                _ => panic!( "ComItf generic argument must be type" ),
                            },
                    _ => panic!( "ComItf type parameter must be angle bracketed" ),
                },
            _ => panic!( "ComItf type parameter must be a type path" ),
        };

        // Construct the final InterfacePtr<T> type.
        parse_quote!( ::intercom::raw::InterfacePtr< #itf_ty > )
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::intercom::raw::InterfacePtr::new( ::std::ptr::null_mut() ) )
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::wrap( #ident, #ts ) ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::ptr( &#ident.into(), #ts ) )
        }
    }
}

/// `bool` parameter handler. Supports `VARIANT_BOOL` for automation type system.
struct BoolParam { context: TypeContext }

impl TypeHandler for BoolParam {

    fn rust_ty( &self ) -> Type { parse_quote!( bool ) }

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        match self.context.type_system {
            ModelTypeSystem::Automation => parse_quote!( ::intercom::raw::VariantBool ),
            ModelTypeSystem::Raw => parse_quote!( bool ),
        }
    }

    fn default_value( &self ) -> TokenStream
    {
        match self.context.type_system {
            ModelTypeSystem::Automation => quote!( false.into() ),
            ModelTypeSystem::Raw => quote!( false ),
        }
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }
}

/// `Variant` parameter handler. Supports `VARIANT` for automation type system.
struct VariantParam { ty: Type }

impl TypeHandler for VariantParam {

    fn rust_ty( &self ) -> Type { self.ty.clone() }

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        parse_quote!( ::intercom::raw::Variant )
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.com_into()? )
        }
    }
}

/// String parameter handler. Converts between Rust String and COM BSTR types.
struct StringParam { ty: Type, context: TypeContext }
impl TypeHandler for StringParam
{
    fn rust_ty( &self ) -> Type { self.ty.clone() }

    fn com_ty( &self ) -> Type
    {
        match self.context.type_system {
            ModelTypeSystem::Automation => match self.context.dir {
                Direction::In => parse_quote!( ::intercom::raw::InBSTR ),
                Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutBSTR ),
            },
            ModelTypeSystem::Raw => match self.context.dir {
                Direction::In => parse_quote!( ::intercom::raw::InCStr ),
                Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutCStr ),
            },
        }
    }

    fn com_to_rust( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let str_type = match self.context.type_system {
                    ModelTypeSystem::Automation => quote!( BStr ),
                    ModelTypeSystem::Raw => quote!( CStr ),
                };

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::#str_type );
                let to_intermediate = quote!( ::intercom::#str_type::from_ptr( #ident ) );
                let as_trait = quote!( < #target_ty as ::intercom::FromWithTemporary< #intermediate_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #to_intermediate )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )? ),
                }
            },
            Direction::Out | Direction::Retval => {

                // The type system input 'ident' should represent either a BString or CString
                // depending on the type system.
                let str_type = match self.context.type_system {
                    ModelTypeSystem::Automation => quote!( BString::from_ptr ),
                    ModelTypeSystem::Raw => quote!( CString::from_raw ),
                };

                // Get the type system string as Rust string.
                let ts_string = quote!( ::intercom::#str_type( #ident ) );

                // Convert the TS string into whatever string type the method requires.
                TypeConversion {
                    temporary: None,
                    value: quote!( #ts_string.com_into()? ),
                }
            },
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let str_type = match self.context.type_system {
                    ModelTypeSystem::Automation => quote!( BStr ),
                    ModelTypeSystem::Raw => quote!( CStr ),
                };

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::#str_type );
                let as_trait = quote!( < #intermediate_ty as ::intercom::FromWithTemporary< #target_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #ident )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )?.as_ptr() ),
                }
            },
            Direction::Out | Direction::Retval => {

                // The Rust string value `ident` must be first converted into a type system
                // compatible Rust string, either BString or CSTring.
                // depending on the type system.
                let str_type = match self.context.type_system {
                    ModelTypeSystem::Automation => quote!( BString ),
                    ModelTypeSystem::Raw => quote!( CString ),
                };

                // Convert the `ident` into the required string type.
                let ts_string = quote!( ::intercom::ComInto::< ::intercom::#str_type >::com_into( #ident )? );

                TypeConversion {
                    temporary: None,
                    value: match self.context.type_system {
                        ModelTypeSystem::Automation =>
                            quote!( #ts_string.into_ptr() ),
                        ModelTypeSystem::Raw =>
                            quote!( #ts_string.into_raw() ),
                    }
                }
            },
        }
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::std::ptr::null_mut() )
    }
}

/// Resolves the `TypeHandler` to use.
pub fn get_ty_handler(
    arg_ty : &Type,
    context : TypeContext,
) -> Rc<TypeHandler>
{
    let type_info = ::type_parser::parse( arg_ty )
            .unwrap_or_else( || panic!( "Type {:?} could not be parsed.", arg_ty ) );

    map_by_name(
            type_info.get_name().as_ref(), type_info.original.clone(),
            context )
}

/// Selects type handler based on the name of the type.
fn map_by_name(
    name: &str,
    original_type: Type,
    context: TypeContext,
) -> Rc<TypeHandler> {

    match name {

        "ComItf" => Rc::new( ComItfParam { ty: original_type, context } ),
        "CString" | "CStr" | "BString" | "BStr" | "String" | "str" =>
            Rc::new( StringParam { ty: original_type, context } ),
        "bool" =>
            Rc::new( BoolParam { context } ),
        "Variant" =>
            Rc::new( VariantParam { ty: original_type } ),
        // "str" => Rc::new( StringRefParam( original_type ) ),

        // Unknown. Use IdentityParam.
        _ => Rc::new( IdentityParam( original_type ) )
    }

}
<|endoftext|>


<commit_before>
use ::prelude::*;
use std::rc::Rc;
use syn::*;

use ast_converters::*;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction { In, Out, Retval }

pub struct TypeConversion {

    /// Possible temporary values that need to be kept alive for the duration
    /// of the conversion result usage.
    pub temporary: Option<TokenStream>,

    /// Conversion result value. Possibly referencing the temporary value.
    pub value : TokenStream,
}

#[derive(PartialEq, Eq, Debug)]
pub struct ModelTypeSystemConfig {
    pub effective_system : ModelTypeSystem,
    pub is_default : bool,
}

impl ModelTypeSystemConfig {
    pub fn get_unique_name( &self, base : &str ) -> String {
        match self.is_default {
            true => base.to_string(),
            false => format!( "{}_{:?}", base, self.effective_system ),
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ModelTypeSystem {

    /// COM Automation compatible type system.
    Automation,

    /// Raw type system.
    Raw,
}

impl ModelTypeSystem {

    /// Converts the model type system into public type system tokens.
    pub fn as_typesystem_tokens( &self ) -> TokenStream {
        match self {
            ModelTypeSystem::Automation =>
                    quote!( ::intercom::TypeSystem::Automation ),
            ModelTypeSystem::Raw =>
                    quote!( ::intercom::TypeSystem::Raw ),
        }
    }
}

/// Type usage context.
pub struct TypeContext {
    dir: Direction,
    type_system: ModelTypeSystem,
}

impl TypeContext {
    pub fn new( dir : Direction, type_system : ModelTypeSystem ) -> TypeContext {
        TypeContext { dir, type_system }
    }
}

/// Defines Type-specific logic for handling the various parameter types in the
/// Rust/COM interface.
pub trait TypeHandler {

    /// The Rust type.
    fn rust_ty( &self ) -> Type;

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        self.rust_ty()
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Gets the default value for the type.
    fn default_value( &self ) -> TokenStream
    {
        match self.rust_ty() {
            Type::Path( ref p ) => {
                let ident = p.path.get_ident().unwrap();
                let name = ident.to_string();
                match name.as_ref() {
                    "c_void"
                        | "RawComPtr"
                        => quote!( ::std::ptr::null_mut() ),
                    _ => quote!( Default::default() )
                }
            },
            _ => quote!( Default::default() )
        }
    }

    /// Gets the sype system the handler serves if the handler is type system specific. Returns
    /// None if the handler is type system agnostic.
    fn type_system( &self ) -> Option<ModelTypeSystem> { None }
}

/// Identity parameter handler.
///
/// No special logic.
struct IdentityParam( Type );

impl TypeHandler for IdentityParam {
    fn rust_ty( &self ) -> Type { self.0.clone() }
}

/// `ComItf` parameter handler. Supports `ComItf` Rust type and ensures the this
/// to/from `RawComPtr` COM type.
struct ComItfParam( Type );

impl TypeHandler for ComItfParam {

    fn rust_ty( &self ) -> Type { self.0.clone() }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ComItf::null_itf() )
    }
}

/// String parameter handler. Converts between Rust String and COM BSTR types.
struct StringParam { ty: Type, context: TypeContext }
impl TypeHandler for StringParam
{
    fn rust_ty( &self ) -> Type { self.ty.clone() }

    fn com_ty( &self ) -> Type
    {
        match self.context.dir {
            Direction::In => parse_quote!( ::intercom::raw::InBSTR ),
            Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutBSTR ),
        }
    }

    fn com_to_rust( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::BStr );
                let to_intermediate = quote!( ::intercom::BStr::from_ptr( #ident ) );
                let as_trait = quote!( < #target_ty as ::intercom::FromWithTemporary< #intermediate_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #to_intermediate )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )? ),
                }
            },
            Direction::Out | Direction::Retval => {
                TypeConversion {
                    temporary: None,
                    value: quote!( ::intercom::BString::from_ptr( #ident ).com_into()? ),
                }
            },
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::BStr );
                let as_trait = quote!( < #intermediate_ty as ::intercom::FromWithTemporary< #target_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #ident )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )?.as_ptr() ),
                }
            },
            Direction::Out | Direction::Retval => {
                TypeConversion {
                    temporary: None,
                    value: quote!( ::intercom::BString::from( #ident ).into_ptr() ),
                }
            },
        }
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::std::ptr::null_mut() )
    }

    /// String parameters differ between the type systems.
    fn type_system( &self ) -> Option<ModelTypeSystem> {
        Some( self.context.type_system )
    }
}

/// Resolves the `TypeHandler` to use.
pub fn get_ty_handler(
    arg_ty : &Type,
    context : TypeContext,
) -> Rc<TypeHandler>
{
    let type_info = ::type_parser::parse( arg_ty )
            .unwrap_or_else( || panic!( "Type {:?} could not be parsed.", arg_ty ) );

    map_by_name(
            type_info.get_name().as_ref(), type_info.original.clone(),
            context )
}

/// Selects type handler based on the name of the type.
fn map_by_name(
    name: &str,
    original_type: Type,
    context: TypeContext,
) -> Rc<TypeHandler> {

    match name {

        "ComItf" => Rc::new( ComItfParam( original_type ) ),
        "BString" | "BStr" | "String" | "str" =>
            Rc::new( StringParam { ty: original_type, context } ),
        // "str" => Rc::new( StringRefParam( original_type ) ),

        // Unknown. Use IdentityParam.
        _ => Rc::new( IdentityParam( original_type ) )
    }

}
<commit_msg>Handle ComItf parameters and return values<commit_after>
use ::prelude::*;
use std::rc::Rc;
use syn::*;

use ast_converters::*;

#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Direction { In, Out, Retval }

pub struct TypeConversion {

    /// Possible temporary values that need to be kept alive for the duration
    /// of the conversion result usage.
    pub temporary: Option<TokenStream>,

    /// Conversion result value. Possibly referencing the temporary value.
    pub value : TokenStream,
}

#[derive(PartialEq, Eq, Debug)]
pub struct ModelTypeSystemConfig {
    pub effective_system : ModelTypeSystem,
    pub is_default : bool,
}

impl ModelTypeSystemConfig {
    pub fn get_unique_name( &self, base : &str ) -> String {
        match self.is_default {
            true => base.to_string(),
            false => format!( "{}_{:?}", base, self.effective_system ),
        }
    }
}

#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
pub enum ModelTypeSystem {

    /// COM Automation compatible type system.
    Automation,

    /// Raw type system.
    Raw,
}

impl ModelTypeSystem {

    /// Converts the model type system into public type system tokens.
    pub fn as_typesystem_tokens( &self ) -> TokenStream {
        match self {
            ModelTypeSystem::Automation =>
                    quote!( ::intercom::TypeSystem::Automation ),
            ModelTypeSystem::Raw =>
                    quote!( ::intercom::TypeSystem::Raw ),
        }
    }
}

/// Type usage context.
pub struct TypeContext {
    dir: Direction,
    type_system: ModelTypeSystem,
}

impl TypeContext {
    pub fn new( dir : Direction, type_system : ModelTypeSystem ) -> TypeContext {
        TypeContext { dir, type_system }
    }
}

/// Defines Type-specific logic for handling the various parameter types in the
/// Rust/COM interface.
pub trait TypeHandler {

    /// The Rust type.
    fn rust_ty( &self ) -> Type;

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        self.rust_ty()
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        TypeConversion {
            temporary: None,
            value: quote!( #ident.into() )
        }
    }

    /// Gets the default value for the type.
    fn default_value( &self ) -> TokenStream
    {
        match self.rust_ty() {
            Type::Path( ref p ) => {
                let ident = p.path.get_ident().unwrap();
                let name = ident.to_string();
                match name.as_ref() {
                    "c_void"
                        | "RawComPtr"
                        => quote!( ::std::ptr::null_mut() ),
                    _ => quote!( Default::default() )
                }
            },
            _ => quote!( Default::default() )
        }
    }

    /// Gets the sype system the handler serves if the handler is type system specific. Returns
    /// None if the handler is type system agnostic.
    fn type_system( &self ) -> Option<ModelTypeSystem> { None }
}

/// Identity parameter handler.
///
/// No special logic.
struct IdentityParam( Type );

impl TypeHandler for IdentityParam {
    fn rust_ty( &self ) -> Type { self.0.clone() }
}

/// `ComItf` parameter handler. Supports `ComItf` Rust type and ensures the this
/// to/from `RawComPtr` COM type.
struct ComItfParam { ty: Type, context: TypeContext }

impl TypeHandler for ComItfParam {

    fn rust_ty( &self ) -> Type { self.ty.clone() }

    /// The COM type.
    fn com_ty( &self ) -> Type
    {
        parse_quote!( ::intercom::RawComPtr )
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::std::ptr::null_mut() )
    }

    /// Converts a COM parameter named by the ident into a Rust type.
    fn com_to_rust(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::wrap( #ident, #ts ) ),
        }
    }

    /// Converts a Rust parameter named by the ident into a COM type.
    fn rust_to_com(
        &self, ident : &Ident
    ) -> TypeConversion
    {
        let ts = self.context.type_system.as_typesystem_tokens();
        TypeConversion {
            temporary: None,
            value: quote!( ::intercom::ComItf::ptr( #ident.into(), #ts ) )
        }
    }
}

/// String parameter handler. Converts between Rust String and COM BSTR types.
struct StringParam { ty: Type, context: TypeContext }
impl TypeHandler for StringParam
{
    fn rust_ty( &self ) -> Type { self.ty.clone() }

    fn com_ty( &self ) -> Type
    {
        match self.context.dir {
            Direction::In => parse_quote!( ::intercom::raw::InBSTR ),
            Direction::Out | Direction::Retval => parse_quote!( ::intercom::raw::OutBSTR ),
        }
    }

    fn com_to_rust( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::BStr );
                let to_intermediate = quote!( ::intercom::BStr::from_ptr( #ident ) );
                let as_trait = quote!( < #target_ty as ::intercom::FromWithTemporary< #intermediate_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #to_intermediate )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )? ),
                }
            },
            Direction::Out | Direction::Retval => {
                TypeConversion {
                    temporary: None,
                    value: quote!( ::intercom::BString::from_ptr( #ident ).com_into()? ),
                }
            },
        }
    }

    fn rust_to_com( &self, ident : &Ident ) -> TypeConversion
    {
        match self.context.dir {

            Direction::In => {

                let target_ty = self.rust_ty();
                let intermediate_ty = quote!( &::intercom::BStr );
                let as_trait = quote!( < #intermediate_ty as ::intercom::FromWithTemporary< #target_ty > > );

                let temp_ident = Ident::new( &format!( "__{}_temporary", ident.to_string() ), Span::call_site() );
                TypeConversion {
                    temporary: Some( quote!( let mut #temp_ident = #as_trait::to_temporary( #ident )?; ) ),
                    value: quote!( #as_trait::from_temporary( &mut #temp_ident )?.as_ptr() ),
                }
            },
            Direction::Out | Direction::Retval => {
                TypeConversion {
                    temporary: None,
                    value: quote!( ::intercom::BString::from( #ident ).into_ptr() ),
                }
            },
        }
    }

    fn default_value( &self ) -> TokenStream
    {
        quote!( ::std::ptr::null_mut() )
    }

    /// String parameters differ between the type systems.
    fn type_system( &self ) -> Option<ModelTypeSystem> {
        Some( self.context.type_system )
    }
}

/// Resolves the `TypeHandler` to use.
pub fn get_ty_handler(
    arg_ty : &Type,
    context : TypeContext,
) -> Rc<TypeHandler>
{
    let type_info = ::type_parser::parse( arg_ty )
            .unwrap_or_else( || panic!( "Type {:?} could not be parsed.", arg_ty ) );

    map_by_name(
            type_info.get_name().as_ref(), type_info.original.clone(),
            context )
}

/// Selects type handler based on the name of the type.
fn map_by_name(
    name: &str,
    original_type: Type,
    context: TypeContext,
) -> Rc<TypeHandler> {

    match name {

        "ComItf" => Rc::new( ComItfParam { ty: original_type, context } ),
        "BString" | "BStr" | "String" | "str" =>
            Rc::new( StringParam { ty: original_type, context } ),
        // "str" => Rc::new( StringRefParam( original_type ) ),

        // Unknown. Use IdentityParam.
        _ => Rc::new( IdentityParam( original_type ) )
    }

}
<|endoftext|>


<commit_before>//! Collections for use with GC references.

use traits::{InHeap, IntoHeap, IntoHeapAllocation};
use gcref::GCRef;
use heap::HeapId;
use std::marker::PhantomData;
use std::mem;

/// An implementation detail.
pub struct VecStorage<'a, U: InHeap<'a>>(Vec<U>, HeapId<'a>);

unsafe impl<'a, U: InHeap<'a>> InHeap<'a> for VecStorage<'a, U> {
    type Out = Vec<U::Out>;

    unsafe fn mark(&self) {
        for r in &self.0 {
            r.mark();
        }
    }

    unsafe fn from_heap(&self) -> Vec<U::Out> {
        self.0.iter().map(|x| x.from_heap()).collect()
    }
}

unsafe impl<'a, T: IntoHeap<'a>> IntoHeap<'a> for Vec<T> {
    type In = VecStorage<'a, T::In>;

    fn into_heap(self) -> VecStorage<'a, T::In> {
        VecStorage(self.into_iter().map(|x| x.into_heap()).collect(), PhantomData)
    }
}

impl<'a, T: IntoHeap<'a>> IntoHeapAllocation<'a> for Vec<T> {
    type Ref = VecRef<'a, T>;

    fn wrap_gcref(gcref: GCRef<'a, VecStorage<'a, T::In>>) -> VecRef<'a, T> {
        VecRef(gcref)
    }
}

/// A reference to a GC-heap-allocated `Vec`.
///
/// To allocate a vector in the heap, simply call `heap.alloc` on a plain old `Vec`.
/// It will return a `VecRef` object which has many of the familiar `Vec` methods,
/// as well as element accessors `get_at` and `set_at`.
///
///     #[macro_use] extern crate cell_gc;
///     use cell_gc::collections::VecRef;
///
///     fn main() {
///         cell_gc::with_heap(|heap| {
///             let r: VecRef<i32> = heap.alloc(vec![20, 10, 40, 30]);
///             assert_eq!(r.length(), 4);
///             assert_eq!(r.get(0), 20);
///             r.push(100);
///             r.sort();
///             assert_eq!(r.get_all(), vec![10, 20, 30, 40, 100]);
///         });
///     }
///
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VecRef<'a, T: IntoHeap<'a>>(GCRef<'a, VecStorage<'a, T::In>>);

unsafe impl<'a, U: InHeap<'a>> InHeap<'a> for *mut VecStorage<'a, U> {
    type Out = VecRef<'a, U::Out>;

    unsafe fn mark(&self) {
        for r in &(**self).0 {
            r.mark();
        }
    }

    unsafe fn from_heap(&self) -> VecRef<'a, U::Out> {
        VecRef(GCRef::new(*self))
    }
}

unsafe impl<'a, T: IntoHeap<'a>> IntoHeap<'a> for VecRef<'a, T> {
    type In = *mut VecStorage<'a, T::In>;

    fn into_heap(self) -> *mut VecStorage<'a, T::In> {
        self.0.as_mut_ptr()
    }
}

impl<'a, T: IntoHeap<'a>> VecRef<'a, T> {
    /// Unsafe to call: During the lifetime of the reference passed to the
    /// callback, the caller must ensure that the vector is not modified. Look
    /// out for destructors and `to_heap/from_heap` methods.
    fn unsafe_deref<R, F>(&self, f: F) -> R
        where F: for<'b> FnOnce(&'b Vec<T::In>) -> R
    {
        // NOTE: the signature above is overly restrictive.
        // I should be able to figure out a better one when I have a little time.
        // TODO lock here!
        f(unsafe { &(*self.0.as_ptr()).0 })
        // TODO release lock here!
    }

    /// Unsafe to call: During the lifetime of the reference passed to the
    /// callback, the caller must ensure that no other references exist to the
    /// vector.  Destructors and `from_heap` methods are presumed safe. Avoid
    /// `to_heap`.
    fn unsafe_deref_mut<R, F>(&self, f: F) -> R
        where F: for<'b> FnOnce(&'b mut Vec<T::In>) -> R
    {
        // TODO lock here!
        f(unsafe { &mut (*self.0.as_mut_ptr()).0 })
        // TODO release lock here!
    }

    pub fn length(&self) -> usize {
        self.unsafe_deref(|v| v.len())
    }

    pub fn capacity(&self) -> usize {
        self.unsafe_deref(|v| v.capacity())
    }

    pub fn get(&self, i: usize) -> T {
        self.unsafe_deref(|v| unsafe { v[i].from_heap() })
    }

    pub fn get_all(&self) -> Vec<T> {
        // NOTE: not using unsafe_deref because of the `for<'b>` in its signature.
        unsafe { (*self.0.as_ptr()).from_heap() }
    }

    pub fn set(&self, i: usize, value: T) {
        let u = value.into_heap();
        self.unsafe_deref_mut(|v| v[i] = u);
    }

    pub fn truncate(&self, len: usize) {
        self.unsafe_deref_mut(|v| v.truncate(len));
    }

    pub fn swap_remove(&self, index: usize) -> T {
        let u = self.unsafe_deref_mut(|v| v.swap_remove(index));
        unsafe { u.from_heap() }
    }

    pub fn insert(&self, index: usize, element: T) {
        let u = element.into_heap();
        self.unsafe_deref_mut(|v| v.insert(index, u));
    }

    pub fn remove(&self, index: usize) -> T {
        let u = self.unsafe_deref_mut(|v| v.remove(index));
        unsafe { u.from_heap() }
    }

    pub fn push(&self, value: T) {
        let u = value.into_heap();
        self.unsafe_deref_mut(|v| v.push(u));
    }

    pub fn pop(&self) -> Option<T> {
        self.unsafe_deref_mut(|v| v.pop()).map(|u| unsafe { u.from_heap() })
    }

    // BUG: the `T::In: Ord` bound here is going to be weird for users.
    pub fn sort(&self) where T::In: Ord {
        // Make the heap vector empty while we're sorting it.  This way, the
        // user can do whatever malicious nonsense they like to the heap in
        // Ord::compare, and our copy of the vector is unaffected.
        let mut tmp = vec![];
        self.unsafe_deref_mut(|v| mem::swap(&mut tmp, v));
        tmp.sort();
        self.unsafe_deref_mut(|v| mem::swap(&mut tmp, v));
    }
}
<commit_msg>Refinements to `VecRef`. `.length()` is now spelled `.len()`; many more methods are supported, including `.is_empty()`, `.first()`, `.last()`, and `.clear()`; more documentation; fix the signature of `.sort()` to do the comparisons "in userspace", not in the heap.<commit_after>//! Collections for use with GC references.

use traits::{InHeap, IntoHeap, IntoHeapAllocation};
use gcref::GCRef;
use heap::HeapId;
use std::marker::PhantomData;
use std::mem;
use std::cmp::Ordering;

/// An implementation detail.
pub struct VecStorage<'a, U: InHeap<'a>>(Vec<U>, HeapId<'a>);

unsafe impl<'a, U: InHeap<'a>> InHeap<'a> for VecStorage<'a, U> {
    type Out = Vec<U::Out>;

    unsafe fn mark(&self) {
        for r in &self.0 {
            r.mark();
        }
    }

    unsafe fn from_heap(&self) -> Vec<U::Out> {
        self.0.iter().map(|x| x.from_heap()).collect()
    }
}

unsafe impl<'a, T: IntoHeap<'a>> IntoHeap<'a> for Vec<T> {
    type In = VecStorage<'a, T::In>;

    fn into_heap(self) -> VecStorage<'a, T::In> {
        VecStorage(self.into_iter().map(|x| x.into_heap()).collect(), PhantomData)
    }
}

impl<'a, T: IntoHeap<'a>> IntoHeapAllocation<'a> for Vec<T> {
    type Ref = VecRef<'a, T>;

    fn wrap_gcref(gcref: GCRef<'a, VecStorage<'a, T::In>>) -> VecRef<'a, T> {
        VecRef(gcref)
    }
}

/// A reference to a GC-heap-allocated `Vec`.
///
/// To allocate a vector in the heap, simply call `heap.alloc` on a plain old `Vec`.
/// It will return a `VecRef` object which has many of the familiar `Vec` methods,
/// as well as element accessors `get_at` and `set_at`.
///
/// ```rust
/// use cell_gc::collections::VecRef;
///
/// cell_gc::with_heap(|heap| {
///     let r: VecRef<i32> = heap.alloc(vec![20, 10, 40, 30]);
///     assert_eq!(r.len(), 4);
///     assert_eq!(r.get(0), 20);
///     r.push(100);
///     r.sort();
///     assert_eq!(r.get_all(), vec![10, 20, 30, 40, 100]);
/// });
/// ```
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct VecRef<'a, T: IntoHeap<'a>>(GCRef<'a, VecStorage<'a, T::In>>);

unsafe impl<'a, U: InHeap<'a>> InHeap<'a> for *mut VecStorage<'a, U> {
    type Out = VecRef<'a, U::Out>;

    unsafe fn mark(&self) {
        for r in &(**self).0 {
            r.mark();
        }
    }

    unsafe fn from_heap(&self) -> VecRef<'a, U::Out> {
        VecRef(GCRef::new(*self))
    }
}

unsafe impl<'a, T: IntoHeap<'a>> IntoHeap<'a> for VecRef<'a, T> {
    type In = *mut VecStorage<'a, T::In>;

    fn into_heap(self) -> *mut VecStorage<'a, T::In> {
        self.0.as_mut_ptr()
    }
}

impl<'a, T: IntoHeap<'a>> VecRef<'a, T> {
    /// Unsafe to call: During the lifetime of the reference passed to the
    /// callback, the caller must ensure that the vector is not modified.
    /// Destructors and `from_heap` methods are presumed safe. Avoid
    /// `to_heap`.
    fn unsafe_deref<'b, R, F>(&'b self, f: F) -> R
        where F: FnOnce(&'b Vec<T::In>) -> R
    {
        // NOTE: the signature above is overly restrictive.
        // I should be able to figure out a better one when I have a little time.
        // TODO: in debug builds at least there should be a RefCell-like "lock" here,
        // asserting that no direct Rust references exist to anything in the GC heap.
        f(unsafe { &(*self.0.as_ptr()).0 })
    }

    /// Unsafe to call: During the lifetime of the reference passed to the
    /// callback, the caller must ensure that no other references exist to the
    /// vector.  Destructors and `from_heap` methods are presumed safe. Avoid
    /// `to_heap`.
    fn unsafe_deref_mut<'b, R, F>(&'b self, f: F) -> R
        where F: FnOnce(&'b mut Vec<T::In>) -> R
    {
        // TODO should assert here, like unsafe_deref
        f(unsafe { &mut (*self.0.as_mut_ptr()).0 })
    }

    /// Get the element `index` from the vector.
    ///
    /// This clones the element, so `VecRef<LargeStruct>::get()` would be slow.
    /// `VecRef<LargeStructRef>::get()` only clones a reference, so it is
    /// relatively quick.
    ///
    /// # Panics
    ///
    /// Panics if `index` is out of bounds.
    pub fn get(&self, index: usize) -> T {
        self.unsafe_deref(|v| unsafe { v[index].from_heap() })
    }

    /// Copy the vector out of the GC-managed heap. This returns an ordinary,
    /// non-GC-managed Rust `Vec`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # use cell_gc::collections::VecRef;
    /// # cell_gc::with_heap(|heap| {
    /// let gc_vec: VecRef<i32> = heap.alloc(vec![]);
    /// for i in 0 .. 4 {
    ///     gc_vec.push(i);
    /// }
    /// let plain_vec = gc_vec.get_all();
    /// assert_eq!(plain_vec, vec![0, 1, 2, 3]);
    /// # });
    /// ```
    pub fn get_all(&self) -> Vec<T> {
        unsafe { (*self.0.as_ptr()).from_heap() }
    }

    /// Set element `index` of the vector to `value`.
    ///
    /// # Panics
    ///
    /// Panics if `index` is out of bounds.
    pub fn set(&self, index: usize, value: T) {
        let u = value.into_heap();
        self.unsafe_deref_mut(|v| v[index] = u);
    }

    pub fn capacity(&self) -> usize {
        self.unsafe_deref(|v| v.capacity())
    }

    pub fn reserve(&self, additional: usize) {
        self.unsafe_deref_mut(|v| v.reserve(additional));
    }

    pub fn reserve_exact(&self, additional: usize) {
        self.unsafe_deref_mut(|v| v.reserve_exact(additional));
    }

    pub fn shrink_to_fit(&self) {
        self.unsafe_deref_mut(|v| v.shrink_to_fit());
    }

    pub fn truncate(&self, len: usize) {
        self.unsafe_deref_mut(|v| v.truncate(len));
    }

    pub fn swap_remove(&self, index: usize) -> T {
        let u = self.unsafe_deref_mut(|v| v.swap_remove(index));
        unsafe { u.from_heap() }
    }

    pub fn insert(&self, index: usize, element: T) {
        let u = element.into_heap();
        self.unsafe_deref_mut(|v| v.insert(index, u));
    }

    pub fn remove(&self, index: usize) -> T {
        let u = self.unsafe_deref_mut(|v| v.remove(index));
        unsafe { u.from_heap() }
    }

    pub fn push(&self, value: T) {
        let u = value.into_heap();
        self.unsafe_deref_mut(|v| v.push(u));
    }

    pub fn pop(&self) -> Option<T> {
        self.unsafe_deref_mut(|v| v.pop()).map(|u| unsafe { u.from_heap() })
    }

    pub fn append(&self, other: &VecRef<'a, T>) {
        let mut tmp: Vec<T::In> = vec![];
        other.unsafe_deref_mut(|src| mem::swap(src, &mut tmp));
        self.unsafe_deref_mut(|dest| dest.append(&mut tmp));
    }

    pub fn clear(&self) {
        self.unsafe_deref_mut(|v| v.clear());
    }

    pub fn len(&self) -> usize {
        self.unsafe_deref(|v| v.len())
    }

    pub fn is_empty(&self) -> bool {
        self.len() == 0
    }

    /// Get the first element of the vector, or `None` if the vector is empty.
    ///
    /// The standard `Vec<T>::first` returns an `&T` reference into the vector,
    /// but cell-gc never hands out Rust references to heap values. Instead
    /// this returns a clone of the value.
    pub fn first(&self) -> Option<T> {
        let r = self.unsafe_deref(|v| v.first());
        r.map(|u| unsafe { u.from_heap() })
    }

    /// Get the last element of the vector, or `None` if the vector is empty.
    ///
    /// Like `first()`, this clones the value.
    pub fn last(&self) -> Option<T> {
        let r = self.unsafe_deref(|v| v.last());
        r.map(|u| unsafe { u.from_heap() })
    }

    /// Sort the vector in place.
    pub fn sort(&self) where T: Ord {
        self.sort_by(Ord::cmp);
    }

    pub fn sort_by<F>(&self, mut compare: F) where F: FnMut(&T, &T) -> Ordering {
        // Make the heap vector empty while we're sorting it.  This way, the
        // user can do whatever malicious nonsense they like to the heap in
        // `compare`, and our copy of the vector is unaffected.
        let mut tmp = vec![];
        self.unsafe_deref_mut(|v| mem::swap(&mut tmp, v));
        tmp.sort_by(|ru1, ru2| {
            let t1 = unsafe { ru1.from_heap() };
            let t2 = unsafe { ru2.from_heap() };
            compare(&t1, &t2)
        });
        self.unsafe_deref_mut(|v| mem::swap(&mut tmp, v));
    }
}
<|endoftext|>