<commit_before>//! APIs for working with the git repositories associated with a `Project`.

use std::collections::BTreeMap;
use std::collections::btree_map;
use std::path::{Path, PathBuf};

use command_runner::{Command, CommandRunner};
#[cfg(test)] use command_runner::TestCommandRunner;
use ext::service::ServiceExt;
use git_url::GitUrl;
use project::Project;
use pod::Pod;
use util::{ConductorPathExt, Error};

/// All the git repositories associated with a project.
#[derive(Debug)]
pub struct Repos {
    /// Our repositories, indexed by their local alias.
    repos: BTreeMap<String, Repo>,
}

impl Repos {
    /// Create a collection of repositories based on a list of pods.
    #[doc(hidden)]
    pub fn new(pods: &[Pod]) -> Result<Repos, Error> {
        let mut repos: BTreeMap<String, Repo> = BTreeMap::new();
        for pod in pods {
            for file in pod.all_files() {
                for (_name, service) in &file.services {
                    if let Some(git_url) = try!(service.git_url()) {
                        // Figure out what alias we want to use.  The final
                        // `unwrap` should be safe because we know the input
                        // was UTF-8.
                        //
                        // TODO LOW: We may need to unescape this.
                        let url_path =
                            Path::new(try!(git_url.to_url()).path()).to_owned();
                        let alias = try!(url_path.file_stem().ok_or_else(|| {
                            err!("Can't get repo name from {}", &git_url)
                        })).to_str().unwrap().to_owned();

                        // Build our repository.
                        let repo = Repo { alias: alias, git_url: git_url };

                        // Insert our repository our map, checking for alias
                        // clashes.
                        match repos.entry(repo.alias.clone()) {
                            btree_map::Entry::Vacant(vacant) => {
                                vacant.insert(repo);
                            }
                            btree_map::Entry::Occupied(occupied) => {
                                if &repo.alias != &occupied.get().alias {
                                    return Err(err!("{} and {} would both alias to {}",
                                                    &occupied.get().git_url,
                                                    &repo.git_url,
                                                    &repo.alias));
                                }
                            }
                        }
                    }
                }
            }
        }
        Ok(Repos { repos: repos })
    }

    /// Iterate over all repositories associated with this project.
    pub fn iter(&self) -> Iter {
        Iter { iter: self.repos.iter() }
    }

    /// Look up a repository using the short-form local alias.
    pub fn find_by_alias(&self, alias: &str) -> Option<&Repo> {
        self.repos.get(alias)
    }
}

/// An iterator over all repositories associated with this project.
#[derive(Clone)]
pub struct Iter<'a> {
    // We wrap this in our own struct to make the underlying type opaque.
    iter: btree_map::Iter<'a, String, Repo>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = &'a Repo;

    fn next(&mut self) -> Option<&'a Repo> {
        self.iter.next().map(|(_alias, repo)| repo)
    }
}

/// A single repository.
#[derive(Debug)]
pub struct Repo {
    alias: String,
    git_url: GitUrl,
}

impl Repo {
    /// A short local name for this git repository, suitable for use as
    /// a directory name or command-line argument.
    pub fn alias(&self) -> &str {
        &self.alias
    }

    /// The remote git URL from which we can clone this repository.
    pub fn git_url(&self) -> &GitUrl {
        &self.git_url
    }

    /// The path to which we would check out this repository, relative to
    /// `Project::src_dir`.
    pub fn rel_path(&self) -> PathBuf {
        Path::new(self.alias()).to_owned()
    }

    /// Clone the source code of this repository using git.
    pub fn clone_source<CR>(&self, runner: &CR, project: &Project) ->
        Result<(), Error>
        where CR: CommandRunner
    {
        let dest =
            try!(project.src_dir().join(&self.alias).with_guaranteed_parent());
        let status = try!(runner.build("git")
            .arg("clone")
            .arg(self.git_url())
            .arg(dest)
            .status());
        if !status.success() {
            return Err(err!("Error cloning {} to {}", &self.git_url,
                            self.rel_path().display()));
        }
        Ok(())
    }
}

#[test]
fn are_loaded_with_projects() {
    let proj = Project::from_example("hello").unwrap();
    let repos = proj.repos();
    assert_eq!(repos.iter().count(), 2);
    let hello = repos.find_by_alias("dockercloud-hello-world")
        .expect("repos should include dockercloud-hello-world");
    assert_eq!(hello.alias(), "dockercloud-hello-world");
    assert_eq!(hello.git_url().as_ref() as &str,
               "https://github.com/docker/dockercloud-hello-world.git");
    assert_eq!(hello.rel_path(), Path::new("dockercloud-hello-world"));
}

#[test]
fn can_be_cloned_out() {
    let proj = Project::from_example("hello").unwrap();
    let repo = proj.repos().find_by_alias("dockercloud-hello-world").unwrap();
    let runner = TestCommandRunner::new();
    repo.clone_source(&runner, &proj).unwrap();
    assert_ran!(runner, {
        ["git", "clone", repo.git_url(),
         proj.src_dir().join(repo.rel_path())]
    });
}
<commit_msg>Determine whether a repository is cloned<commit_after>//! APIs for working with the git repositories associated with a `Project`.

use std::collections::BTreeMap;
use std::collections::btree_map;
use std::path::{Path, PathBuf};

use command_runner::{Command, CommandRunner};
#[cfg(test)] use command_runner::TestCommandRunner;
use ext::service::ServiceExt;
#[cfg(test)] use std::fs;
use git_url::GitUrl;
use project::Project;
use pod::Pod;
use util::{ConductorPathExt, Error};

/// All the git repositories associated with a project.
#[derive(Debug)]
pub struct Repos {
    /// Our repositories, indexed by their local alias.
    repos: BTreeMap<String, Repo>,
}

impl Repos {
    /// Create a collection of repositories based on a list of pods.
    #[doc(hidden)]
    pub fn new(pods: &[Pod]) -> Result<Repos, Error> {
        let mut repos: BTreeMap<String, Repo> = BTreeMap::new();
        for pod in pods {
            for file in pod.all_files() {
                for (_name, service) in &file.services {
                    if let Some(git_url) = try!(service.git_url()) {
                        // Figure out what alias we want to use.  The final
                        // `unwrap` should be safe because we know the input
                        // was UTF-8.
                        //
                        // TODO LOW: We may need to unescape this.
                        let url_path =
                            Path::new(try!(git_url.to_url()).path()).to_owned();
                        let alias = try!(url_path.file_stem().ok_or_else(|| {
                            err!("Can't get repo name from {}", &git_url)
                        })).to_str().unwrap().to_owned();

                        // Build our repository.
                        let repo = Repo { alias: alias, git_url: git_url };

                        // Insert our repository our map, checking for alias
                        // clashes.
                        match repos.entry(repo.alias.clone()) {
                            btree_map::Entry::Vacant(vacant) => {
                                vacant.insert(repo);
                            }
                            btree_map::Entry::Occupied(occupied) => {
                                if &repo.alias != &occupied.get().alias {
                                    return Err(err!("{} and {} would both alias to {}",
                                                    &occupied.get().git_url,
                                                    &repo.git_url,
                                                    &repo.alias));
                                }
                            }
                        }
                    }
                }
            }
        }
        Ok(Repos { repos: repos })
    }

    /// Iterate over all repositories associated with this project.
    pub fn iter(&self) -> Iter {
        Iter { iter: self.repos.iter() }
    }

    /// Look up a repository using the short-form local alias.
    pub fn find_by_alias(&self, alias: &str) -> Option<&Repo> {
        self.repos.get(alias)
    }
}

/// An iterator over all repositories associated with this project.
#[derive(Clone)]
pub struct Iter<'a> {
    // We wrap this in our own struct to make the underlying type opaque.
    iter: btree_map::Iter<'a, String, Repo>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = &'a Repo;

    fn next(&mut self) -> Option<&'a Repo> {
        self.iter.next().map(|(_alias, repo)| repo)
    }
}

/// A single repository.
#[derive(Debug)]
pub struct Repo {
    alias: String,
    git_url: GitUrl,
}

impl Repo {
    /// A short local name for this git repository, suitable for use as
    /// a directory name or command-line argument.
    pub fn alias(&self) -> &str {
        &self.alias
    }

    /// The remote git URL from which we can clone this repository.
    pub fn git_url(&self) -> &GitUrl {
        &self.git_url
    }

    /// The path to which we would check out this repository, relative to
    /// `Project::src_dir`.
    pub fn rel_path(&self) -> PathBuf {
        Path::new(self.alias()).to_owned()
    }

    /// Has this project been cloned locally?
    pub fn is_cloned(&self, project: &Project) -> bool {
        project.src_dir().join(&self.alias).exists()
    }

    /// Clone the source code of this repository using git.
    pub fn clone_source<CR>(&self, runner: &CR, project: &Project) ->
        Result<(), Error>
        where CR: CommandRunner
    {
        let dest =
            try!(project.src_dir().join(&self.alias).with_guaranteed_parent());
        let status = try!(runner.build("git")
            .arg("clone")
            .arg(self.git_url())
            .arg(dest)
            .status());
        if !status.success() {
            return Err(err!("Error cloning {} to {}", &self.git_url,
                            self.rel_path().display()));
        }
        Ok(())
    }
}

#[test]
fn are_loaded_with_projects() {
    let proj = Project::from_example("hello").unwrap();
    let repos = proj.repos();
    assert_eq!(repos.iter().count(), 2);
    let hello = repos.find_by_alias("dockercloud-hello-world")
        .expect("repos should include dockercloud-hello-world");
    assert_eq!(hello.alias(), "dockercloud-hello-world");
    assert_eq!(hello.git_url().as_ref() as &str,
               "https://github.com/docker/dockercloud-hello-world.git");
    assert_eq!(hello.rel_path(), Path::new("dockercloud-hello-world"));
}

#[test]
fn can_be_cloned() {
    let proj = Project::from_example("hello").unwrap();
    let repo = proj.repos().find_by_alias("dockercloud-hello-world").unwrap();
    let runner = TestCommandRunner::new();
    repo.clone_source(&runner, &proj).unwrap();
    assert_ran!(runner, {
        ["git", "clone", repo.git_url(),
         proj.src_dir().join(repo.rel_path())]
    });
    proj.remove_test_output().unwrap();
}

#[test]
fn can_be_checked_to_see_if_cloned() {
    let proj = Project::from_example("hello").unwrap();
    let repo = proj.repos().find_by_alias("dockercloud-hello-world").unwrap();
    assert!(!repo.is_cloned(&proj));
    fs::create_dir_all(proj.src_dir().join(repo.rel_path())).unwrap();
    assert!(repo.is_cloned(&proj));
    proj.remove_test_output().unwrap();
}
<|endoftext|>


<commit_before>#![crate_name = "rm"]
#![feature(collections, core, old_io, old_path, rustc_private)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::old_io::{print, stdin, stdio, fs, BufferedReader};
use std::old_io::fs::PathExtensions;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

#[derive(Eq, PartialEq)]
enum InteractiveMode {
    InteractiveNone,
    InteractiveOnce,
    InteractiveAlways
}

impl Copy for InteractiveMode {}

static NAME: &'static str = "rm";

pub fn uumain(args: Vec<String>) -> i32 {
    let program = args[0].clone();

    // TODO: make getopts support -R in addition to -r
    let opts = [
        getopts::optflag("f", "force", "ignore nonexistent files and arguments, never prompt"),
        getopts::optflag("i", "", "prompt before every removal"),
        getopts::optflag("I", "", "prompt once before removing more than three files, or when removing recursively.  Less intrusive than -i, while still giving some protection against most mistakes"),
        getopts::optflagopt("", "interactive", "prompt according to WHEN: never, once (-I), or always (-i).  Without WHEN, prompts always", "WHEN"),
        getopts::optflag("", "one-file-system", "when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)"),
        getopts::optflag("", "no-preserve-root", "do not treat '/' specially"),
        getopts::optflag("", "preserve-root", "do not remove '/' (default)"),
        getopts::optflag("r", "recursive", "remove directories and their contents recursively"),
        getopts::optflag("d", "dir", "remove empty directories"),
        getopts::optflag("v", "verbose", "explain what is being done"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit")
    ];
    let matches = match getopts::getopts(args.tail(), &opts) {
        Ok(m) => m,
        Err(f) => {
            crash!(1, "{}", f)
        }
    };
    if matches.opt_present("help") {
        println!("rm 1.0.0");
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", program);
        println!("");
        print(getopts::usage("Remove (unlink) the FILE(s).", &opts).as_slice());
        println!("");
        println!("By default, rm does not remove directories.  Use the --recursive (-r)");
        println!("option to remove each listed directory, too, along with all of its contents");
        println!("");
        println!("To remove a file whose name starts with a '-', for example '-foo',");
        println!("use one of these commands:");
        println!("rm -- -foo");
        println!("");
        println!("rm ./-foo");
        println!("");
        println!("Note that if you use rm to remove a file, it might be possible to recover");
        println!("some of its contents, given sufficient expertise and/or time.  For greater");
        println!("assurance that the contents are truly unrecoverable, consider using shred.");
    } else if matches.opt_present("version") {
        println!("rm 1.0.0");
    } else if matches.free.is_empty() {
        show_error!("missing an argument");
        show_error!("for help, try '{0} --help'", program);
        return 1;
    } else {
        let force = matches.opt_present("force");
        let interactive =
            if matches.opt_present("i") {
                InteractiveMode::InteractiveAlways
            } else if matches.opt_present("I") {
                InteractiveMode::InteractiveOnce
            } else if matches.opt_present("interactive") {
                match matches.opt_str("interactive").unwrap().as_slice() {
                    "none" => InteractiveMode::InteractiveNone,
                    "once" => InteractiveMode::InteractiveOnce,
                    "always" => InteractiveMode::InteractiveAlways,
                    val => {
                        crash!(1, "Invalid argument to interactive ({})", val)
                    }
                }
            } else {
                InteractiveMode::InteractiveNone
            };
        let one_fs = matches.opt_present("one-file-system");
        let preserve_root = !matches.opt_present("no-preserve-root");
        let recursive = matches.opt_present("recursive");
        let dir = matches.opt_present("dir");
        let verbose = matches.opt_present("verbose");
        if interactive == InteractiveMode::InteractiveOnce && (recursive || matches.free.len() > 3) {
            let msg =
                if recursive {
                    "Remove all arguments recursively? "
                } else {
                    "Remove all arguments? "
                };
            if !prompt(msg) {
                return 0;
            }
        }
        if let Err(e) = remove(matches.free, force, interactive, one_fs, preserve_root,
                               recursive, dir, verbose) {
            return e;
        }
    }

    0
}

// TODO: implement one-file-system
fn remove(files: Vec<String>, force: bool, interactive: InteractiveMode, one_fs: bool, preserve_root: bool, recursive: bool, dir: bool, verbose: bool) -> Result<(), i32> {
    let mut r = Ok(());

    for filename in files.iter() {
        let filename = filename.as_slice();
        let file = Path::new(filename);
        if file.exists() {
            if file.is_dir() {
                if recursive && (filename != "/" || !preserve_root) {
                    let walk_dir = match fs::walk_dir(&file) {
                        Ok(m) => m,
                        Err(f) => {
                            crash!(1, "{}", f.to_string());
                        }
                    };
                    r = remove(walk_dir.map(|x| x.as_str().unwrap().to_string()).collect(), force, interactive, one_fs, preserve_root, recursive, dir, verbose).and(r);
                    r = remove_dir(&file, filename, interactive, verbose).and(r);
                } else if dir && (filename != "/" || !preserve_root) {
                    r = remove_dir(&file, filename, interactive, verbose).and(r);
                } else {
                    if recursive {
                        show_error!("could not remove directory '{}'",
                                       filename);
                        r = Err(1);
                    } else {
                        show_error!("could not remove directory '{}' (did you mean to pass '-r'?)",
                                    filename);
                        r = Err(1);
                    }
                }
            } else {
                r = remove_file(&file, filename.as_slice(), interactive, verbose).and(r);
            }
        } else if !force {
            show_error!("no such file or directory '{}'", filename);
            r = Err(1);
        }
    }

    r
}

fn remove_dir(path: &Path, name: &str, interactive: InteractiveMode, verbose: bool) -> Result<(), i32> {
    let response =
        if interactive == InteractiveMode::InteractiveAlways {
            prompt_file(path, name)
        } else {
            true
        };
    if response {
        match fs::rmdir(path) {
            Ok(_) => if verbose { println!("Removed '{}'", name); },
            Err(f) => {
                show_error!("{}", f.to_string());
                return Err(1);
            }
        }
    }

    Ok(())
}

fn remove_file(path: &Path, name: &str, interactive: InteractiveMode, verbose: bool) -> Result<(), i32> {
    let response =
        if interactive == InteractiveMode::InteractiveAlways {
            prompt_file(path, name)
        } else {
            true
        };
    if response {
        match fs::unlink(path) {
            Ok(_) => if verbose { println!("Removed '{}'", name); },
            Err(f) => {
                show_error!("{}", f.to_string());
                return Err(1);
            }
        }
    }

    Ok(())
}

fn prompt_file(path: &Path, name: &str) -> bool {
    if path.is_dir() {
        prompt(format!("Remove directory '{}'? ", name).as_slice())
    } else {
        prompt(format!("Remove file '{}'? ", name).as_slice())
    }
}

fn prompt(msg: &str) -> bool {
    print(msg);
    read_prompt()
}

fn read_prompt() -> bool {
    stdio::flush();
    match BufferedReader::new(stdin()).read_line() {
        Ok(line) => {
            match line.as_slice().char_at(0) {
                'y' | 'Y' => true,
                'n' | 'N' => false,
                _ => {
                    print!("Please enter either Y or N: ");
                    read_prompt()
                }
            }
        }
        Err(_) => true
    }
}
<commit_msg>fix `rm`<commit_after>#![crate_name = "rm"]
#![feature(file_type, dir_entry_ext, path_ext, rustc_private)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Arcterus <arcterus@mail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

extern crate getopts;
extern crate libc;

use std::collections::VecDeque;
use std::fs::{self, PathExt};
use std::io::{stdin, stderr, BufRead, Write};
use std::ops::BitOr;
use std::path::{Path, PathBuf};

#[path = "../common/util.rs"]
#[macro_use]
mod util;

#[derive(Eq, PartialEq, Clone, Copy)]
enum InteractiveMode {
    InteractiveNone,
    InteractiveOnce,
    InteractiveAlways
}

static NAME: &'static str = "rm";

pub fn uumain(args: Vec<String>) -> i32 {
    // TODO: make getopts support -R in addition to -r
    let opts = [
        getopts::optflag("f", "force", "ignore nonexistent files and arguments, never prompt"),
        getopts::optflag("i", "", "prompt before every removal"),
        getopts::optflag("I", "", "prompt once before removing more than three files, or when removing recursively.  Less intrusive than -i, while still giving some protection against most mistakes"),
        getopts::optflagopt("", "interactive", "prompt according to WHEN: never, once (-I), or always (-i).  Without WHEN, prompts always", "WHEN"),
        getopts::optflag("", "one-file-system", "when removing a hierarchy recursively, skip any directory that is on a file system different from that of the corresponding command line argument (NOT IMPLEMENTED)"),
        getopts::optflag("", "no-preserve-root", "do not treat '/' specially"),
        getopts::optflag("", "preserve-root", "do not remove '/' (default)"),
        getopts::optflag("r", "recursive", "remove directories and their contents recursively"),
        getopts::optflag("d", "dir", "remove empty directories"),
        getopts::optflag("v", "verbose", "explain what is being done"),
        getopts::optflag("h", "help", "display this help and exit"),
        getopts::optflag("V", "version", "output version information and exit")
    ];
    let matches = match getopts::getopts(&args[1..], &opts) {
        Ok(m) => m,
        Err(f) => {
            crash!(1, "{}", f)
        }
    };
    if matches.opt_present("help") {
        println!("rm 1.0.0");
        println!("");
        println!("Usage:");
        println!("  {0} [OPTION]... [FILE]...", &args[0][..]);
        println!("");
        println!("{}", &getopts::usage("Remove (unlink) the FILE(s).", &opts)[..]);
        println!("By default, rm does not remove directories.  Use the --recursive (-r)");
        println!("option to remove each listed directory, too, along with all of its contents");
        println!("");
        println!("To remove a file whose name starts with a '-', for example '-foo',");
        println!("use one of these commands:");
        println!("rm -- -foo");
        println!("");
        println!("rm ./-foo");
        println!("");
        println!("Note that if you use rm to remove a file, it might be possible to recover");
        println!("some of its contents, given sufficient expertise and/or time.  For greater");
        println!("assurance that the contents are truly unrecoverable, consider using shred.");
    } else if matches.opt_present("version") {
        println!("rm 1.0.0");
    } else if matches.free.is_empty() {
        show_error!("missing an argument");
        show_error!("for help, try '{0} --help'", &args[0][..]);
        return 1;
    } else {
        let force = matches.opt_present("force");
        let interactive =
            if matches.opt_present("i") {
                InteractiveMode::InteractiveAlways
            } else if matches.opt_present("I") {
                InteractiveMode::InteractiveOnce
            } else if matches.opt_present("interactive") {
                match &matches.opt_str("interactive").unwrap()[..] {
                    "none" => InteractiveMode::InteractiveNone,
                    "once" => InteractiveMode::InteractiveOnce,
                    "always" => InteractiveMode::InteractiveAlways,
                    val => {
                        crash!(1, "Invalid argument to interactive ({})", val)
                    }
                }
            } else {
                InteractiveMode::InteractiveNone
            };
        let one_fs = matches.opt_present("one-file-system");
        let preserve_root = !matches.opt_present("no-preserve-root");
        let recursive = matches.opt_present("recursive");
        let dir = matches.opt_present("dir");
        let verbose = matches.opt_present("verbose");
        if interactive == InteractiveMode::InteractiveOnce && (recursive || matches.free.len() > 3) {
            let msg =
                if recursive {
                    "Remove all arguments recursively? "
                } else {
                    "Remove all arguments? "
                };
            if !prompt(msg) {
                return 0;
            }
        }

        if remove(matches.free, force, interactive, one_fs, preserve_root, recursive, dir, verbose) {
            return 1;
        }
    }

    0
}

// TODO: implement one-file-system
#[allow(unused_variables)]
fn remove(files: Vec<String>, force: bool, interactive: InteractiveMode, one_fs: bool, preserve_root: bool, recursive: bool, dir: bool, verbose: bool) -> bool {
    let mut had_err = false;

    for filename in files.iter() {
        let filename = &filename[..];
        let file = Path::new(filename);
        if file.exists() {
            if file.is_dir() {
                if recursive && (filename != "/" || !preserve_root) {
                    if interactive != InteractiveMode::InteractiveAlways {
                        match fs::remove_dir_all(file) {
                            Err(e) => {
                                had_err = true;
                                show_error!("could not remove '{}': {}", filename, e);
                            },
                            _ => (),
                        };
                    } else {
                        let mut dirs: VecDeque<PathBuf> = VecDeque::new();
                        let mut files: Vec<PathBuf> = Vec::new();
                        let mut rmdirstack: Vec<PathBuf> = Vec::new();
                        dirs.push_back(file.to_path_buf());

                        while !dirs.is_empty() {
                            let dir = dirs.pop_front().unwrap();
                            if !prompt(&(format!("rm: descend into directory '{}'? ", dir.display()))[..]) {
                                continue;
                            }

                            // iterate over items in this directory, adding to either file or
                            // directory queue
                            match fs::read_dir(dir.as_path()) {
                                Ok(rdir) => {
                                    for ent in rdir {
                                        match ent {
                                            Ok(ref f) => match f.file_type() {
                                                Ok(t) => {
                                                    if t.is_dir() {
                                                        dirs.push_back(f.path());
                                                    } else {
                                                        files.push(f.path());
                                                    }
                                                },
                                                Err(e) => {
                                                    had_err = true;
                                                    show_error!("reading '{}': {}", f.path().display(), e);
                                                },
                                            },
                                            Err(ref e) => {
                                                had_err = true;
                                                show_error!("recursing into '{}': {}", filename, e);
                                            },
                                        };
                                    }
                                },
                                Err(e) => {
                                    had_err = true;
                                    show_error!("could not recurse into '{}': {}", dir.display(), e);
                                    continue;
                                },
                            };

                            for f in files.iter() {
                                had_err = remove_file(f.as_path(), interactive, verbose).bitor(had_err);
                            }

                            files.clear();
                            rmdirstack.push(dir);
                        }

                        for d in rmdirstack.iter().rev() {
                            had_err = remove_dir(d.as_path(), interactive, verbose).bitor(had_err);
                        }
                    }
                } else if dir && (filename != "/" || !preserve_root) {
                    had_err = remove_dir(&file, interactive, verbose).bitor(had_err);
                } else {
                    if recursive {
                        show_error!("could not remove directory '{}'", filename);
                        had_err = true;
                    } else {
                        show_error!("could not remove directory '{}' (did you mean to pass '-r'?)", filename);
                        had_err = true;
                    }
                }
            } else {
                had_err = remove_file(&file, interactive, verbose).bitor(had_err);
            }
        } else if !force {
            show_error!("no such file or directory '{}'", filename);
            had_err = true;
        }
    }

    had_err
}

fn remove_dir(path: &Path, interactive: InteractiveMode, verbose: bool) -> bool {
    let response =
        if interactive == InteractiveMode::InteractiveAlways {
            prompt_file(path, true)
        } else {
            true
        };
    if response {
        match fs::remove_dir(path) {
            Ok(_) => if verbose { println!("removed '{}'", path.display()); },
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn remove_file(path: &Path, interactive: InteractiveMode, verbose: bool) -> bool {
    let response =
        if interactive == InteractiveMode::InteractiveAlways {
            prompt_file(path, false)
        } else {
            true
        };
    if response {
        match fs::remove_file(path) {
            Ok(_) => if verbose { println!("removed '{}'", path.display()); },
            Err(e) => {
                show_error!("removing '{}': {}", path.display(), e);
                return true;
            }
        }
    }

    false
}

fn prompt_file(path: &Path, is_dir: bool) -> bool {
    if is_dir {
        prompt(&(format!("rm: remove directory '{}'? ", path.display()))[..])
    } else {
        prompt(&(format!("rm: remove file '{}'? ", path.display()))[..])
    }
}

fn prompt(msg: &str) -> bool {
    stderr().write_all(msg.as_bytes()).unwrap_or(());
    stderr().flush().unwrap_or(());
    let mut buf = Vec::new();
    let stdin = stdin();
    let mut stdin = stdin.lock();
    match stdin.read_until('\n' as u8, &mut buf) {
        Ok(x) if x > 0 => {
            match buf[0] {
                0x59 | 0x79 => true,
                _ => false,
            }
        }
        _ => false,
    }
}
<|endoftext|>


<commit_before>use std::ascii::{AsciiCast, StrAsciiExt};
use std::fmt::{Formatter, FormatError, Show};

/// Status code of a request or response.
#[deriving(Eq, PartialEq, Clone, Show, Ord, PartialOrd)]
#[stable]
pub struct StatusCode(pub uint);

impl StatusCode {
    #[stable]
    /// Returns the status code as a number.
    pub fn as_uint(&self) -> uint {
        match *self { StatusCode(n) => n }
    }

    #[stable]
    /// Returns the default reason phrase for this status code.
    /// For example the status code 404 corresponds to "Not Found".
    pub fn get_default_reason_phrase(&self) -> &'static str {
        match self.as_uint() {
            100 => "Continue",
            101 => "Switching Protocols",
            102 => "Processing",
            118 => "Connection timed out",
            200 => "OK",
            201 => "Created",
            202 => "Accepted",
            203 => "Non-Authoritative Information",
            204 => "No Content",
            205 => "Reset Content",
            206 => "Partial Content",
            207 => "Multi-Status",
            210 => "Content Different",
            300 => "Multiple Choices",
            301 => "Moved Permanently",
            302 => "Found",
            303 => "See Other",
            304 => "Not Modified",
            305 => "Use Proxy",
            307 => "Temporary Redirect",
            400 => "Bad Request",
            401 => "Unauthorized",
            402 => "Payment Required",
            403 => "Forbidden",
            404 => "Not Found",
            405 => "Method Not Allowed",
            406 => "Not Acceptable",
            407 => "Proxy Authentication Required",
            408 => "Request Time-out",
            409 => "Conflict",
            410 => "Gone",
            411 => "Length Required",
            412 => "Precondition Failed",
            413 => "Request Entity Too Large",
            414 => "Reques-URI Too Large",
            415 => "Unsupported Media Type",
            416 => "Request range not satisfiable",
            417 => "Expectation Failed",
            500 => "Internal Server Error",
            501 => "Not Implemented",
            502 => "Bad Gateway",
            503 => "Service Unavailable",
            504 => "Gateway Time-out",
            505 => "HTTP Version not supported",
            _ => "Unknown"
        }
    }
}

impl Equiv<uint> for StatusCode {
    fn equiv(&self, other: &uint) -> bool {
        self.as_uint() == *other
    }
}

/// Represents a HTTP header.
///
/// The easiest way to create a `Header` object is to call `from_str`.
///
/// ```
/// let header: Header = from_str("Content-Type: text/plain").unwrap();
/// ```
#[deriving(Clone)]
#[unstable]
pub struct Header {
    pub field: HeaderField,
    pub value: String,
}

impl ::std::from_str::FromStr for Header {
    fn from_str(input: &str) -> Option<Header> {
        let elems = input.splitn(':', 1).map(|e| e.to_string()).collect::<Vec<String>>();

        if elems.len() <= 1 {
            return None;
        }

        let field = match from_str(elems.get(0).as_slice().trim()) {
            None => return None,
            Some(f) => f
        };

        Some(Header {
            field: field,
            value: elems.get(1).as_slice().trim().to_string()
        })
    }
}

impl Show for Header {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        (format!("{}: {}", self.field, self.value)).fmt(formatter)
    }
}

/// Field of a header (eg. `Content-Type`, `Content-Length`, etc.)
///
/// Comparaison between two `HeaderField`s ignores case.
#[unstable]
#[deriving(Clone)]
pub struct HeaderField(Vec<Ascii>);

impl HeaderField {
    fn as_str<'a>(&'a self) -> &'a [Ascii] {
        match self { &HeaderField(ref s) => s.as_slice() }
    }
}

impl ::std::from_str::FromStr for HeaderField {
    fn from_str(s: &str) -> Option<HeaderField> {
        s.trim().to_ascii_opt().map(|s| HeaderField(Vec::from_slice(s)))
    }
}

impl IntoStr for HeaderField {
    fn into_string(self) -> String {
        match self { HeaderField(s) => s.into_string() }
    }
}

impl Show for HeaderField {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let method = self.as_str();
        method.as_str_ascii().fmt(formatter)
    }
}

impl PartialEq for HeaderField {
    fn eq(&self, other: &HeaderField) -> bool {
        self.as_str().eq_ignore_case(other.as_str())
    }
}

impl Eq for HeaderField {}

impl<S: Str> Equiv<S> for HeaderField {
    fn equiv(&self, other: &S) -> bool {
        other.as_slice().eq_ignore_ascii_case(self.as_str().as_str_ascii())
    }
}

/// HTTP method (eg. `GET`, `POST`, etc.)
///
/// The user chooses the method he wants.
///
/// Comparaison between two `Method`s ignores case.
#[unstable]
#[deriving(Clone)]
pub struct Method(Vec<Ascii>);

impl Method {
    fn as_str<'a>(&'a self) -> &'a [Ascii] {
        match self { &Method(ref s) => s.as_slice() }
    }
}

impl ::std::from_str::FromStr for Method {
    fn from_str(s: &str) -> Option<Method> {
        s.to_ascii_opt().map(|s| Method(Vec::from_slice(s)))
    }
}

impl IntoStr for Method {
    fn into_string(self) -> String {
        match self { Method(s) => s.into_string() }
    }
}

impl Show for Method {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let method = self.as_str();
        method.as_str_ascii().fmt(formatter)
    }
}

impl PartialEq for Method {
    fn eq(&self, other: &Method) -> bool {
        self.as_str().eq_ignore_case(other.as_str())
    }
}

impl Eq for Method {}

impl<S: Str> Equiv<S> for Method {
    fn equiv(&self, other: &S) -> bool {
        other.as_slice().eq_ignore_ascii_case(self.as_str().as_str_ascii())
    }
}

/// HTTP version (usually 1.0 or 1.1).
#[unstable]
#[deriving(Clone, PartialEq, Eq, Ord)]
pub struct HTTPVersion(pub uint, pub uint);

impl Show for HTTPVersion {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let (major, minor) = match self { &HTTPVersion(m, n) => (m, n) };
        (format!("{}.{}", major, minor)).fmt(formatter)
    }
}

impl PartialOrd for HTTPVersion {
    fn partial_cmp(&self, other: &HTTPVersion) -> Option<Ordering> {
        let (my_major, my_minor) = match self { &HTTPVersion(m, n) => (m, n) };
        let (other_major, other_minor) = match other { &HTTPVersion(m, n) => (m, n) };

        if my_major != other_major {
            return my_major.partial_cmp(&other_major)
        }

        my_minor.partial_cmp(&other_minor)
    }
}

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

    #[test]
    fn test_parse_header() {
        let header: Header = from_str("Content-Type: text/html").unwrap();

        assert!(header.field.equiv(&"content-type"));
        assert!(header.value.as_slice() == "text/html");

        assert!(from_str::<Header>("hello world").is_none());
    }

    #[test]
    fn test_parse_header_with_doublecolon() {
        let header: Header = from_str("Time: 20: 34").unwrap();

        assert!(header.field.equiv(&"time"));
        assert!(header.value.as_slice() == "20: 34");
    }
}
<commit_msg>Cleaner implementation of Header::from_str<commit_after>use std::ascii::{AsciiCast, StrAsciiExt};
use std::fmt::{Formatter, FormatError, Show};

/// Status code of a request or response.
#[deriving(Eq, PartialEq, Clone, Show, Ord, PartialOrd)]
#[stable]
pub struct StatusCode(pub uint);

impl StatusCode {
    #[stable]
    /// Returns the status code as a number.
    pub fn as_uint(&self) -> uint {
        match *self { StatusCode(n) => n }
    }

    #[stable]
    /// Returns the default reason phrase for this status code.
    /// For example the status code 404 corresponds to "Not Found".
    pub fn get_default_reason_phrase(&self) -> &'static str {
        match self.as_uint() {
            100 => "Continue",
            101 => "Switching Protocols",
            102 => "Processing",
            118 => "Connection timed out",
            200 => "OK",
            201 => "Created",
            202 => "Accepted",
            203 => "Non-Authoritative Information",
            204 => "No Content",
            205 => "Reset Content",
            206 => "Partial Content",
            207 => "Multi-Status",
            210 => "Content Different",
            300 => "Multiple Choices",
            301 => "Moved Permanently",
            302 => "Found",
            303 => "See Other",
            304 => "Not Modified",
            305 => "Use Proxy",
            307 => "Temporary Redirect",
            400 => "Bad Request",
            401 => "Unauthorized",
            402 => "Payment Required",
            403 => "Forbidden",
            404 => "Not Found",
            405 => "Method Not Allowed",
            406 => "Not Acceptable",
            407 => "Proxy Authentication Required",
            408 => "Request Time-out",
            409 => "Conflict",
            410 => "Gone",
            411 => "Length Required",
            412 => "Precondition Failed",
            413 => "Request Entity Too Large",
            414 => "Reques-URI Too Large",
            415 => "Unsupported Media Type",
            416 => "Request range not satisfiable",
            417 => "Expectation Failed",
            500 => "Internal Server Error",
            501 => "Not Implemented",
            502 => "Bad Gateway",
            503 => "Service Unavailable",
            504 => "Gateway Time-out",
            505 => "HTTP Version not supported",
            _ => "Unknown"
        }
    }
}

impl Equiv<uint> for StatusCode {
    fn equiv(&self, other: &uint) -> bool {
        self.as_uint() == *other
    }
}

/// Represents a HTTP header.
///
/// The easiest way to create a `Header` object is to call `from_str`.
///
/// ```
/// let header: Header = from_str("Content-Type: text/plain").unwrap();
/// ```
#[deriving(Clone)]
#[unstable]
pub struct Header {
    pub field: HeaderField,
    pub value: String,
}

impl ::std::from_str::FromStr for Header {
    fn from_str(input: &str) -> Option<Header> {
        let mut elems = input.splitn(':', 1);

        let field = elems.next();
        let value = elems.next();

        let (field, value) = match (field, value) {
            (Some(f), Some(v)) => (f, v),
            _ => return None
        };

        let field = match from_str(field) {
            Some(f) => f,
            None => return None
        };

        Some(Header {
            field: field,
            value: value.trim().to_string(),
        })
    }
}

impl Show for Header {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        (format!("{}: {}", self.field, self.value)).fmt(formatter)
    }
}

/// Field of a header (eg. `Content-Type`, `Content-Length`, etc.)
///
/// Comparaison between two `HeaderField`s ignores case.
#[unstable]
#[deriving(Clone)]
pub struct HeaderField(Vec<Ascii>);

impl HeaderField {
    fn as_str<'a>(&'a self) -> &'a [Ascii] {
        match self { &HeaderField(ref s) => s.as_slice() }
    }
}

impl ::std::from_str::FromStr for HeaderField {
    fn from_str(s: &str) -> Option<HeaderField> {
        s.trim().to_ascii_opt().map(|s| HeaderField(Vec::from_slice(s)))
    }
}

impl IntoStr for HeaderField {
    fn into_string(self) -> String {
        match self { HeaderField(s) => s.into_string() }
    }
}

impl Show for HeaderField {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let method = self.as_str();
        method.as_str_ascii().fmt(formatter)
    }
}

impl PartialEq for HeaderField {
    fn eq(&self, other: &HeaderField) -> bool {
        self.as_str().eq_ignore_case(other.as_str())
    }
}

impl Eq for HeaderField {}

impl<S: Str> Equiv<S> for HeaderField {
    fn equiv(&self, other: &S) -> bool {
        other.as_slice().eq_ignore_ascii_case(self.as_str().as_str_ascii())
    }
}

/// HTTP method (eg. `GET`, `POST`, etc.)
///
/// The user chooses the method he wants.
///
/// Comparaison between two `Method`s ignores case.
#[unstable]
#[deriving(Clone)]
pub struct Method(Vec<Ascii>);

impl Method {
    fn as_str<'a>(&'a self) -> &'a [Ascii] {
        match self { &Method(ref s) => s.as_slice() }
    }
}

impl ::std::from_str::FromStr for Method {
    fn from_str(s: &str) -> Option<Method> {
        s.to_ascii_opt().map(|s| Method(Vec::from_slice(s)))
    }
}

impl IntoStr for Method {
    fn into_string(self) -> String {
        match self { Method(s) => s.into_string() }
    }
}

impl Show for Method {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let method = self.as_str();
        method.as_str_ascii().fmt(formatter)
    }
}

impl PartialEq for Method {
    fn eq(&self, other: &Method) -> bool {
        self.as_str().eq_ignore_case(other.as_str())
    }
}

impl Eq for Method {}

impl<S: Str> Equiv<S> for Method {
    fn equiv(&self, other: &S) -> bool {
        other.as_slice().eq_ignore_ascii_case(self.as_str().as_str_ascii())
    }
}

/// HTTP version (usually 1.0 or 1.1).
#[unstable]
#[deriving(Clone, PartialEq, Eq, Ord)]
pub struct HTTPVersion(pub uint, pub uint);

impl Show for HTTPVersion {
    fn fmt(&self, formatter: &mut Formatter) -> Result<(), FormatError> {
        let (major, minor) = match self { &HTTPVersion(m, n) => (m, n) };
        (format!("{}.{}", major, minor)).fmt(formatter)
    }
}

impl PartialOrd for HTTPVersion {
    fn partial_cmp(&self, other: &HTTPVersion) -> Option<Ordering> {
        let (my_major, my_minor) = match self { &HTTPVersion(m, n) => (m, n) };
        let (other_major, other_minor) = match other { &HTTPVersion(m, n) => (m, n) };

        if my_major != other_major {
            return my_major.partial_cmp(&other_major)
        }

        my_minor.partial_cmp(&other_minor)
    }
}

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

    #[test]
    fn test_parse_header() {
        let header: Header = from_str("Content-Type: text/html").unwrap();

        assert!(header.field.equiv(&"content-type"));
        assert!(header.value.as_slice() == "text/html");

        assert!(from_str::<Header>("hello world").is_none());
    }

    #[test]
    fn test_parse_header_with_doublecolon() {
        let header: Header = from_str("Time: 20: 34").unwrap();

        assert!(header.field.equiv(&"time"));
        assert!(header.value.as_slice() == "20: 34");
    }
}
<|endoftext|>


<commit_before>use hb;
use std::borrow::Borrow;
use std::ops::{Deref, DerefMut};

/// A type to represent 4-byte SFNT tags.
///
/// Tables, features, etc. in OpenType and many other font formats use SFNT tags
/// as identifiers. These are 4-bytes long and usually each byte represents an
/// ASCII value. `Tag` provides methods to create such identifiers from
/// individual `chars` or a `str` slice and to get the string representation of
/// a `Tag`.
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct Tag(pub hb::hb_tag_t);

impl Tag {
    /// Create a `Tag` from its four-char textual representation.
    ///
    /// # Examples
    ///
    /// ```
    /// use harfbuzz_rs::Tag;
    /// let cmap_tag = Tag::new('c', 'm', 'a', 'p');
    /// assert_eq!(cmap_tag.to_string(), "cmap")
    /// ```
    ///
    pub fn new(a: char, b: char, c: char, d: char) -> Self {
        Tag(((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32))
    }

    fn tag_to_string(self) -> String {
        let mut buf: [u8; 4] = [0; 4];
        unsafe { hb::hb_tag_to_string(self.0, buf.as_mut_ptr() as *mut _) };
        String::from_utf8_lossy(&buf).into()
    }
}

use std::fmt;
use std::fmt::{Debug, Display, Formatter};
impl Debug for Tag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let string = self.tag_to_string();
        let mut chars = string.chars().chain(std::iter::repeat('\u{FFFD}'));
        write!(
            f,
            "Tag({:?}, {:?}, {:?}, {:?})",
            chars.next().unwrap(),
            chars.next().unwrap(),
            chars.next().unwrap(),
            chars.next().unwrap()
        )
    }
}

impl Display for Tag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.tag_to_string())
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
/// An Error generated when a `Tag` fails to parse from a `&str` with the
/// `from_str` function.
pub enum TagFromStrErr {
    /// The string contains non-ASCII characters.
    NonAscii,
    /// The string has length zero.
    ZeroLengthString,
}

use std;
use std::str::FromStr;

impl FromStr for Tag {
    type Err = TagFromStrErr;
    /// Parses a `Tag` from a `&str` that contains four or less ASCII
    /// characters. When the string's length is smaller than 4 it is extended
    /// with `' '` (Space) characters. The remaining bytes of strings longer
    /// than 4 bytes are ignored.
    ///
    /// # Examples
    ///
    /// ```
    /// use harfbuzz_rs::Tag;
    /// use std::str::FromStr;
    /// let tag1 = Tag::from_str("ABCD").unwrap();
    /// let tag2 = Tag::new('A', 'B', 'C', 'D');
    /// assert_eq!(tag1, tag2);
    /// ```
    ///
    fn from_str(s: &str) -> Result<Tag, TagFromStrErr> {
        if !s.is_ascii() {
            return Err(TagFromStrErr::NonAscii);
        }
        if s.is_empty() {
            return Err(TagFromStrErr::ZeroLengthString);
        }
        let len = std::cmp::max(s.len(), 4) as i32;
        unsafe { Ok(Tag(hb::hb_tag_from_string(s.as_ptr() as *mut _, len))) }
    }
}

pub struct Language(pub hb::hb_language_t);

impl Default for Language {
    fn default() -> Language {
        Language(unsafe { hb::hb_language_get_default() })
    }
}

impl Debug for Language {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Language(\"{}\")", self)
    }
}

use std::ffi::CStr;
impl Display for Language {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let string = unsafe {
            let char_ptr = hb::hb_language_to_string(self.0);
            if char_ptr.is_null() {
                return Err(fmt::Error);
            }
            CStr::from_ptr(char_ptr)
                .to_str()
                .expect("String representation of language is not valid utf8.")
        };
        write!(f, "{}", string)
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct InvalidLanguage;

impl FromStr for Language {
    type Err = InvalidLanguage;
    fn from_str(s: &str) -> Result<Language, InvalidLanguage> {
        let len = std::cmp::min(s.len(), std::i32::MAX as _) as i32;
        let lang = unsafe { hb::hb_language_from_string(s.as_ptr() as *mut _, len) };
        if lang.is_null() {
            Err(InvalidLanguage {})
        } else {
            Ok(Language(lang))
        }
    }
}

/// A trait which is implemented for all harffbuzz wrapper structs. It exposes
/// common functionality for converting from and to the underlying raw harfbuzz
/// pointers that are useful for ffi.
///
/// This trait may only be implemented for structs that are zero-sized and is
/// therefore unsafe to implement.
pub unsafe trait HarfbuzzObject: Sized {
    /// Type of the raw harfbuzz object.
    type Raw;

    /// Creates a reference from a harfbuzz object pointer.
    ///
    /// Unsafe because a raw pointer may be accessed. The reference count is not
    /// changed. Should not be called directly by a library user.
    ///
    /// Use the Owned and Shared abstractions instead.
    #[doc(hide)]
    unsafe fn from_raw(val: *const Self::Raw) -> Self;

    /// Returns the underlying harfbuzz object pointer.
    ///
    /// The caller must ensure, that this pointer is not used after `self`'s
    /// destruction.
    fn as_raw(&self) -> *mut Self::Raw;

    /// Increases the reference count of the HarfBuzz object.
    ///
    /// Wraps a `hb_TYPE_reference()` call.
    unsafe fn reference(&self);

    /// Decreases the reference count of the HarfBuzz object and destroys it if
    /// the reference count reaches zero.
    ///
    /// Wraps a `hb_TYPE_destroy()` call.
    unsafe fn dereference(&self);
}

/// A smart pointer that wraps an atomically reference counted HarfBuzz object.
///
/// Usually you don't create a `Shared` yourself, but get it from another
/// function in this crate. You can just use the methods of the wrapped object
/// through its `Deref` implementation.
///
/// A `Shared` is a safe wrapper for reference counted HarfBuzz objects and
/// provides shared immutable access to its inner object. As HarfBuzz' objects
/// are all thread-safe `Shared` implements `Send` and `Sync`.
///
/// Tries to mirror the stdlib `Arc` interface where applicable as HarfBuzz'
/// reference counting has similar semantics.
#[derive(Debug, PartialEq, Eq)]
pub struct Shared<T: HarfbuzzObject> {
    object: T,
}

impl<T: HarfbuzzObject> Shared<T> {
    /// Creates a `Shared` from an owned raw harfbuzz pointer.
    ///
    /// Transfers ownership. _Use of the original pointer is now forbidden!_
    /// Unsafe because dereferencing a raw pointer is necessary.
    pub unsafe fn from_raw_owned(raw: *mut T::Raw) -> Self {
        let object = T::from_raw(raw);
        Shared { object }
    }

    /// Converts `self` into the underlying harfbuzz object pointer value. The
    /// resulting pointer has to be manually destroyed using `hb_TYPE_destroy`
    /// or be converted back into the wrapper using the `from_raw` function to
    /// avoid leaking memory.
    pub fn into_raw(shared: Shared<T>) -> *mut T::Raw {
        let result = shared.object.as_raw();
        std::mem::forget(shared);
        result
    }

    /// Creates a `Shared` by cloning a raw harfbuzz pointer.
    ///
    /// The original pointer can still be safely used but must be released
    /// at the end to avoid memory leaks.
    pub unsafe fn from_raw_ref(raw: *mut T::Raw) -> Self {
        let object = T::from_raw(raw);
        object.reference();
        Shared { object }
    }
}

impl<T: HarfbuzzObject> Clone for Shared<T> {
    /// Returns a copy and increases the reference count.
    ///
    /// This behaviour is exactly like `Arc::clone` in the standard library.
    fn clone(&self) -> Self {
        unsafe { Self::from_raw_ref(self.object.as_raw()) }
    }
}

impl<T: HarfbuzzObject> Deref for Shared<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.object
    }
}

impl<T: HarfbuzzObject> Borrow<T> for Shared<T> {
    fn borrow(&self) -> &T {
        self
    }
}

impl<T: HarfbuzzObject> From<Owned<T>> for Shared<T> {
    fn from(t: Owned<T>) -> Self {
        let ptr = t.object.as_raw();
        std::mem::forget(t);
        unsafe { Shared::from_raw_owned(ptr) }
    }
}

impl<T: HarfbuzzObject> Drop for Shared<T> {
    fn drop(&mut self) {
        unsafe { self.dereference() }
    }
}

unsafe impl<T: HarfbuzzObject + Sync + Send> Send for Shared<T> {}
unsafe impl<T: HarfbuzzObject + Sync + Send> Sync for Shared<T> {}

/// A smart pointer that wraps a singly owned harfbuzz object.
///
/// A `Owned` is used to wrap freshly created owned HarfBuzz objects. It permits
/// mutable, non-shared access to the enclosed HarfBuzz value so it can be used
/// e.g. to set up a `Font` or `Face` after its creation.
///
/// There is no safe way to construct an `Owned` pointer and usually you don't
/// need to create a `Owned` yourself, but get it from another function in this
/// crate. You can just use the methods of the wrapped object through its
/// `Deref` implementation.
///
/// Interaction with `Shared`
/// -------------------------
/// When you are finished mutating the inner value, you usually want to pass it
/// to other HarfBuzz functions that expect shared access. Thus you need to
/// convert the `Owned` to a `Shared` pointer using `.into()`. Note however that
/// once a value is converted to  a `Shared<T>`, it will not possible to mutate
/// it anymore.
#[derive(Debug, PartialEq, Eq)]
pub struct Owned<T: HarfbuzzObject> {
    object: T,
}

impl<T: HarfbuzzObject> Owned<T> {
    /// Creates a `Owned` safely wrapping a raw harfbuzz pointer.
    ///
    /// This fully transfers ownership. _Use of the original pointer is now
    /// forbidden!_ Unsafe because a dereference of a raw pointer is necessary.
    ///
    /// Use this only to wrap freshly created HarfBuzz object that is not
    /// shared!
    pub unsafe fn from_raw(raw: *mut T::Raw) -> Self {
        Owned {
            object: T::from_raw(raw),
        }
    }

    /// Converts `self` into the underlying harfbuzz object pointer value. The
    /// resulting pointer has to be manually destroyed using `hb_TYPE_destroy`
    /// or be converted back into the wrapper using the `from_raw` function to
    /// avoid leaking memory.
    pub fn into_raw(owned: Owned<T>) -> *mut T::Raw {
        let result = owned.object.as_raw();
        std::mem::forget(owned);
        result
    }

    /// Demotes an `Owned` pointer to a `Shared` pointer.
    ///
    /// Use this method when you don't need exclusive (mutable) access to the
    /// object anymore. For differences between `Owned` and `Shared` pointers
    /// see the documentation on the respective structs.
    ///
    /// Note that `Shared<T>` also implements `From<Owned<T>>` which allows
    /// implicit conversions in many functions.
    pub fn to_shared(self) -> Shared<T> {
        self.into()
    }
}

impl<T: HarfbuzzObject> Drop for Owned<T> {
    fn drop(&mut self) {
        unsafe { self.dereference() }
    }
}

impl<T: HarfbuzzObject> Deref for Owned<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.object
    }
}

impl<T: HarfbuzzObject> DerefMut for Owned<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.object
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::Cell;
    use std::mem;
    use std::rc::Rc;
    use std::str::FromStr;

    #[test]
    fn test_tag_debugging() {
        let tag = Tag::from_str("ABCD").unwrap();
        assert_eq!("ABCD", format!("{}", tag));
        assert_eq!("Tag('A', 'B', 'C', 'D')", format!("{:?}", tag));
    }

    #[test]
    fn test_tag_creation() {
        assert!(Tag::from_str("∞BCD").is_err());
        assert!(Tag::from_str("").is_err());
        assert_eq!(Tag::from_str("ABCDE"), Tag::from_str("ABCD"));
        assert_eq!(Tag::from_str("abWd").unwrap(), Tag::new('a', 'b', 'W', 'd'));
    }

    #[test]
    fn test_language() {
        assert_eq!(Language::default().to_string(), "c");
        assert_eq!(Language::from_str("ger").unwrap().to_string(), "ger");
        assert_eq!(Language::from_str("ge!").unwrap().to_string(), "ge");
        assert_eq!(Language::from_str("German").unwrap().to_string(), "german");
    }

    // this is a mock struct for testing HarfbuzzObject's behaviour.
    #[derive(Debug)]
    struct ReferenceCounter {
        rc: Rc<Cell<isize>>,
    }

    unsafe impl HarfbuzzObject for ReferenceCounter {
        type Raw = Cell<isize>;

        unsafe fn from_raw(raw: *const Cell<isize>) -> Self {
            ReferenceCounter {
                rc: Rc::from_raw(raw as *mut _),
            }
        }

        fn as_raw(&self) -> *mut Cell<isize> {
            Rc::into_raw(self.rc.clone()) as *mut _
        }

        unsafe fn reference(&self) {
            println!("referencing {:?}", self);
            let rc = self.rc.get();
            self.rc.set(rc + 1);
        }

        unsafe fn dereference(&self) {
            println!("dereferencing {:?}", self);
            let rc = self.rc.get();
            self.rc.set(rc - 1);
        }
    }

    #[test]
    fn reference_counting_shared() {
        // Mimic a C-API that returns a pointer to a reference counted value.
        let object = Rc::new(ReferenceCounter {
            rc: Rc::new(Cell::new(1)),
        });
        let raw = object.as_raw();

        let arc: Shared<ReferenceCounter> = unsafe { Shared::from_raw_owned(raw) };
        assert_eq!(object.rc.get(), 1);
        {
            let arc2 = Shared::clone(&arc);
            assert_eq!(object.rc.get(), 2);
            mem::drop(arc2);
        }
        assert_eq!(object.rc.get(), 1);
        mem::drop(arc);
        assert_eq!(object.rc.get(), 0);

        // don't leak memory
        let _ = unsafe { Rc::from_raw(raw) };
    }
}
<commit_msg>fix reference counting test<commit_after>use hb;
use std::borrow::Borrow;
use std::ops::{Deref, DerefMut};

/// A type to represent 4-byte SFNT tags.
///
/// Tables, features, etc. in OpenType and many other font formats use SFNT tags
/// as identifiers. These are 4-bytes long and usually each byte represents an
/// ASCII value. `Tag` provides methods to create such identifiers from
/// individual `chars` or a `str` slice and to get the string representation of
/// a `Tag`.
#[derive(Copy, Clone, Hash, PartialEq, Eq)]
pub struct Tag(pub hb::hb_tag_t);

impl Tag {
    /// Create a `Tag` from its four-char textual representation.
    ///
    /// # Examples
    ///
    /// ```
    /// use harfbuzz_rs::Tag;
    /// let cmap_tag = Tag::new('c', 'm', 'a', 'p');
    /// assert_eq!(cmap_tag.to_string(), "cmap")
    /// ```
    ///
    pub fn new(a: char, b: char, c: char, d: char) -> Self {
        Tag(((a as u32) << 24) | ((b as u32) << 16) | ((c as u32) << 8) | (d as u32))
    }

    fn tag_to_string(self) -> String {
        let mut buf: [u8; 4] = [0; 4];
        unsafe { hb::hb_tag_to_string(self.0, buf.as_mut_ptr() as *mut _) };
        String::from_utf8_lossy(&buf).into()
    }
}

use std::fmt;
use std::fmt::{Debug, Display, Formatter};
impl Debug for Tag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let string = self.tag_to_string();
        let mut chars = string.chars().chain(std::iter::repeat('\u{FFFD}'));
        write!(
            f,
            "Tag({:?}, {:?}, {:?}, {:?})",
            chars.next().unwrap(),
            chars.next().unwrap(),
            chars.next().unwrap(),
            chars.next().unwrap()
        )
    }
}

impl Display for Tag {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.tag_to_string())
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
/// An Error generated when a `Tag` fails to parse from a `&str` with the
/// `from_str` function.
pub enum TagFromStrErr {
    /// The string contains non-ASCII characters.
    NonAscii,
    /// The string has length zero.
    ZeroLengthString,
}

use std;
use std::str::FromStr;

impl FromStr for Tag {
    type Err = TagFromStrErr;
    /// Parses a `Tag` from a `&str` that contains four or less ASCII
    /// characters. When the string's length is smaller than 4 it is extended
    /// with `' '` (Space) characters. The remaining bytes of strings longer
    /// than 4 bytes are ignored.
    ///
    /// # Examples
    ///
    /// ```
    /// use harfbuzz_rs::Tag;
    /// use std::str::FromStr;
    /// let tag1 = Tag::from_str("ABCD").unwrap();
    /// let tag2 = Tag::new('A', 'B', 'C', 'D');
    /// assert_eq!(tag1, tag2);
    /// ```
    ///
    fn from_str(s: &str) -> Result<Tag, TagFromStrErr> {
        if !s.is_ascii() {
            return Err(TagFromStrErr::NonAscii);
        }
        if s.is_empty() {
            return Err(TagFromStrErr::ZeroLengthString);
        }
        let len = std::cmp::max(s.len(), 4) as i32;
        unsafe { Ok(Tag(hb::hb_tag_from_string(s.as_ptr() as *mut _, len))) }
    }
}

pub struct Language(pub hb::hb_language_t);

impl Default for Language {
    fn default() -> Language {
        Language(unsafe { hb::hb_language_get_default() })
    }
}

impl Debug for Language {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Language(\"{}\")", self)
    }
}

use std::ffi::CStr;
impl Display for Language {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let string = unsafe {
            let char_ptr = hb::hb_language_to_string(self.0);
            if char_ptr.is_null() {
                return Err(fmt::Error);
            }
            CStr::from_ptr(char_ptr)
                .to_str()
                .expect("String representation of language is not valid utf8.")
        };
        write!(f, "{}", string)
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct InvalidLanguage;

impl FromStr for Language {
    type Err = InvalidLanguage;
    fn from_str(s: &str) -> Result<Language, InvalidLanguage> {
        let len = std::cmp::min(s.len(), std::i32::MAX as _) as i32;
        let lang = unsafe { hb::hb_language_from_string(s.as_ptr() as *mut _, len) };
        if lang.is_null() {
            Err(InvalidLanguage {})
        } else {
            Ok(Language(lang))
        }
    }
}

/// A trait which is implemented for all harffbuzz wrapper structs. It exposes
/// common functionality for converting from and to the underlying raw harfbuzz
/// pointers that are useful for ffi.
///
/// This trait may only be implemented for structs that are zero-sized and is
/// therefore unsafe to implement.
pub unsafe trait HarfbuzzObject: Sized {
    /// Type of the raw harfbuzz object.
    type Raw;

    /// Creates a reference from a harfbuzz object pointer.
    ///
    /// Unsafe because a raw pointer may be accessed. The reference count is not
    /// changed. Should not be called directly by a library user.
    ///
    /// Use the Owned and Shared abstractions instead.
    #[doc(hide)]
    unsafe fn from_raw(val: *const Self::Raw) -> Self;

    /// Returns the underlying harfbuzz object pointer.
    ///
    /// The caller must ensure, that this pointer is not used after `self`'s
    /// destruction.
    fn as_raw(&self) -> *mut Self::Raw;

    /// Increases the reference count of the HarfBuzz object.
    ///
    /// Wraps a `hb_TYPE_reference()` call.
    unsafe fn reference(&self);

    /// Decreases the reference count of the HarfBuzz object and destroys it if
    /// the reference count reaches zero.
    ///
    /// Wraps a `hb_TYPE_destroy()` call.
    unsafe fn dereference(&self);
}

/// A smart pointer that wraps an atomically reference counted HarfBuzz object.
///
/// Usually you don't create a `Shared` yourself, but get it from another
/// function in this crate. You can just use the methods of the wrapped object
/// through its `Deref` implementation.
///
/// A `Shared` is a safe wrapper for reference counted HarfBuzz objects and
/// provides shared immutable access to its inner object. As HarfBuzz' objects
/// are all thread-safe `Shared` implements `Send` and `Sync`.
///
/// Tries to mirror the stdlib `Arc` interface where applicable as HarfBuzz'
/// reference counting has similar semantics.
#[derive(Debug, PartialEq, Eq)]
pub struct Shared<T: HarfbuzzObject> {
    object: T,
}

impl<T: HarfbuzzObject> Shared<T> {
    /// Creates a `Shared` from an owned raw harfbuzz pointer.
    ///
    /// Transfers ownership. _Use of the original pointer is now forbidden!_
    /// Unsafe because dereferencing a raw pointer is necessary.
    pub unsafe fn from_raw_owned(raw: *mut T::Raw) -> Self {
        let object = T::from_raw(raw);
        Shared { object }
    }

    /// Converts `self` into the underlying harfbuzz object pointer value. The
    /// resulting pointer has to be manually destroyed using `hb_TYPE_destroy`
    /// or be converted back into the wrapper using the `from_raw` function to
    /// avoid leaking memory.
    pub fn into_raw(shared: Shared<T>) -> *mut T::Raw {
        let result = shared.object.as_raw();
        std::mem::forget(shared);
        result
    }

    /// Creates a `Shared` by cloning a raw harfbuzz pointer.
    ///
    /// The original pointer can still be safely used but must be released
    /// at the end to avoid memory leaks.
    pub unsafe fn from_raw_ref(raw: *mut T::Raw) -> Self {
        let object = T::from_raw(raw);
        object.reference();
        Shared { object }
    }
}

impl<T: HarfbuzzObject> Clone for Shared<T> {
    /// Returns a copy and increases the reference count.
    ///
    /// This behaviour is exactly like `Arc::clone` in the standard library.
    fn clone(&self) -> Self {
        unsafe { Self::from_raw_ref(self.object.as_raw()) }
    }
}

impl<T: HarfbuzzObject> Deref for Shared<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.object
    }
}

impl<T: HarfbuzzObject> Borrow<T> for Shared<T> {
    fn borrow(&self) -> &T {
        self
    }
}

impl<T: HarfbuzzObject> From<Owned<T>> for Shared<T> {
    fn from(t: Owned<T>) -> Self {
        let ptr = t.object.as_raw();
        std::mem::forget(t);
        unsafe { Shared::from_raw_owned(ptr) }
    }
}

impl<T: HarfbuzzObject> Drop for Shared<T> {
    fn drop(&mut self) {
        unsafe { self.dereference() }
    }
}

unsafe impl<T: HarfbuzzObject + Sync + Send> Send for Shared<T> {}
unsafe impl<T: HarfbuzzObject + Sync + Send> Sync for Shared<T> {}

/// A smart pointer that wraps a singly owned harfbuzz object.
///
/// A `Owned` is used to wrap freshly created owned HarfBuzz objects. It permits
/// mutable, non-shared access to the enclosed HarfBuzz value so it can be used
/// e.g. to set up a `Font` or `Face` after its creation.
///
/// There is no safe way to construct an `Owned` pointer and usually you don't
/// need to create a `Owned` yourself, but get it from another function in this
/// crate. You can just use the methods of the wrapped object through its
/// `Deref` implementation.
///
/// Interaction with `Shared`
/// -------------------------
/// When you are finished mutating the inner value, you usually want to pass it
/// to other HarfBuzz functions that expect shared access. Thus you need to
/// convert the `Owned` to a `Shared` pointer using `.into()`. Note however that
/// once a value is converted to  a `Shared<T>`, it will not possible to mutate
/// it anymore.
#[derive(Debug, PartialEq, Eq)]
pub struct Owned<T: HarfbuzzObject> {
    object: T,
}

impl<T: HarfbuzzObject> Owned<T> {
    /// Creates a `Owned` safely wrapping a raw harfbuzz pointer.
    ///
    /// This fully transfers ownership. _Use of the original pointer is now
    /// forbidden!_ Unsafe because a dereference of a raw pointer is necessary.
    ///
    /// Use this only to wrap freshly created HarfBuzz object that is not
    /// shared!
    pub unsafe fn from_raw(raw: *mut T::Raw) -> Self {
        Owned {
            object: T::from_raw(raw),
        }
    }

    /// Converts `self` into the underlying harfbuzz object pointer value. The
    /// resulting pointer has to be manually destroyed using `hb_TYPE_destroy`
    /// or be converted back into the wrapper using the `from_raw` function to
    /// avoid leaking memory.
    pub fn into_raw(owned: Owned<T>) -> *mut T::Raw {
        let result = owned.object.as_raw();
        std::mem::forget(owned);
        result
    }

    /// Demotes an `Owned` pointer to a `Shared` pointer.
    ///
    /// Use this method when you don't need exclusive (mutable) access to the
    /// object anymore. For differences between `Owned` and `Shared` pointers
    /// see the documentation on the respective structs.
    ///
    /// Note that `Shared<T>` also implements `From<Owned<T>>` which allows
    /// implicit conversions in many functions.
    pub fn to_shared(self) -> Shared<T> {
        self.into()
    }
}

impl<T: HarfbuzzObject> Drop for Owned<T> {
    fn drop(&mut self) {
        unsafe { self.dereference() }
    }
}

impl<T: HarfbuzzObject> Deref for Owned<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.object
    }
}

impl<T: HarfbuzzObject> DerefMut for Owned<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.object
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use std::cell::Cell;
    use std::mem;
    use std::rc::Rc;
    use std::str::FromStr;

    #[test]
    fn test_tag_debugging() {
        let tag = Tag::from_str("ABCD").unwrap();
        assert_eq!("ABCD", format!("{}", tag));
        assert_eq!("Tag('A', 'B', 'C', 'D')", format!("{:?}", tag));
    }

    #[test]
    fn test_tag_creation() {
        assert!(Tag::from_str("∞BCD").is_err());
        assert!(Tag::from_str("").is_err());
        assert_eq!(Tag::from_str("ABCDE"), Tag::from_str("ABCD"));
        assert_eq!(Tag::from_str("abWd").unwrap(), Tag::new('a', 'b', 'W', 'd'));
    }

    #[test]
    fn test_language() {
        assert_eq!(Language::default().to_string(), "c");
        assert_eq!(Language::from_str("ger").unwrap().to_string(), "ger");
        assert_eq!(Language::from_str("ge!").unwrap().to_string(), "ge");
        assert_eq!(Language::from_str("German").unwrap().to_string(), "german");
    }

    // this is a mock struct for testing HarfbuzzObject's behaviour.
    #[derive(Debug, Clone)]
    struct ReferenceCounter {
        share_count: Rc<Cell<isize>>,
    }

    unsafe impl HarfbuzzObject for ReferenceCounter {
        type Raw = Cell<isize>;

        unsafe fn from_raw(raw: *const Cell<isize>) -> Self {
            ReferenceCounter {
                share_count: Rc::from_raw(raw as *mut _),
            }
        }

        fn as_raw(&self) -> *mut Cell<isize> {
            Rc::into_raw(self.share_count.clone()) as *mut _
        }

        unsafe fn reference(&self) {
            println!("referencing {:?}", self);
            let rc = self.share_count.get();
            self.share_count.set(rc + 1);
        }

        unsafe fn dereference(&self) {
            println!("dereferencing {:?}", self);
            let rc = self.share_count.get();
            self.share_count.set(rc - 1);
        }
    }

    #[test]
    fn reference_counting_shared() {
        // Mimic a C-API that returns a pointer to a reference counted value.
        let object = ReferenceCounter {
            share_count: Rc::new(Cell::new(1)),
        };

        // this clones the underlying `Rc`
        let raw = object.as_raw();

        // so we expect two shared owners
        assert_eq!(Rc::strong_count(&object.share_count), 2);

        let shared: Shared<ReferenceCounter> = unsafe { Shared::from_raw_owned(raw) };

        assert_eq!(shared.share_count.get(), 1);
        {
            // we create another `Shared` pointer...
            let shared2 = Shared::clone(&shared);
            // which clones
            assert_eq!(shared.share_count.get(), 2);
            mem::drop(shared2);
        }
        assert_eq!(shared.share_count.get(), 1);
        mem::drop(shared);

        assert_eq!(object.share_count.get(), 0);

        // ensure there are no dangling references
        assert_eq!(Rc::strong_count(&object.share_count), 1);
    }
}
<|endoftext|>


<commit_before>//! # yeild
//!
//! generator yield implmentation
//!

// use generator::Generator;
use gen_impl::GeneratorImpl;
use rt::{Error, Context, ContextStack};
use yield_::raw_yield_now;

/// passed in scope tpye
/// it not use the context to pass data, but keep it's own data ref
/// this struct provide both compile type info and runtime data
pub struct Scope<A, T> {
    para: *mut Option<A>,
    ret: *mut Option<T>,
}

impl<A, T> Scope<A, T> {
    /// create a new scope object
    pub fn new(para: *mut Option<A>, ret: *mut Option<T>) -> Self {
        Scope {
            para: para,
            ret: ret,
        }
    }

    /// set current generator return value
    #[inline]
    fn set_ret(&mut self, v: T) {
        let ret = unsafe { &mut *self.ret };
        *ret = Some(v);
    }

    /// get current generator send para
    #[inline]
    fn get_para(&mut self) -> Option<A> {
        let para = unsafe { &mut *self.para };
        para.take()
    }

    /// raw yiled without catch passed in para
    #[inline]
    fn raw_yield(&mut self, env: &ContextStack, context: &mut Context, v: T) {
        // check the context
        if !context.is_generator() {
            info!("yield from none generator context");
            // do nothing, just return
            return;
            // panic!(Error::ContextErr);
        }

        self.set_ret(v);
        context._ref -= 1;
        raw_yield_now(env, context);

        // here we just panic to exit the func
        if context._ref != 1 {
            panic!(Error::Cancel);
        }
    }

    /// yiled and get the send para
    // it's totally safe that we can refer to the function block
    // since we will come back later
    #[inline]
    pub fn yield_(&mut self, v: T) -> Option<A> {
        let env = ContextStack::current();
        let context = env.top();
        self.raw_yield(&env, context, v);
        self.get_para()
    }

    /// `yiled_from`
    /// the from generator must has the same type as itself
    pub fn yield_from(&mut self, mut g: Box<GeneratorImpl<A, T>>) -> Option<A> {
        let env = ContextStack::current();
        let context = env.top();
        let mut p = self.get_para();
        while !g.is_done() {
            match g.raw_send(p) {
                None => return None,
                Some(r) => self.raw_yield(&env, context, r),
            }
            p = self.get_para();
        }
        p
    }
}
<commit_msg>add yield_with for scope<commit_after>//! # yeild
//!
//! generator yield implmentation
//!

// use generator::Generator;
use gen_impl::GeneratorImpl;
use rt::{Error, Context, ContextStack};
use yield_::raw_yield_now;

/// passed in scope tpye
/// it not use the context to pass data, but keep it's own data ref
/// this struct provide both compile type info and runtime data
pub struct Scope<A, T> {
    para: *mut Option<A>,
    ret: *mut Option<T>,
}

impl<A, T> Scope<A, T> {
    /// create a new scope object
    pub fn new(para: *mut Option<A>, ret: *mut Option<T>) -> Self {
        Scope {
            para: para,
            ret: ret,
        }
    }

    /// set current generator return value
    #[inline]
    fn set_ret(&mut self, v: T) {
        let ret = unsafe { &mut *self.ret };
        *ret = Some(v);
    }

    /// get current generator send para
    #[inline]
    pub fn get_yield(&mut self) -> Option<A> {
        let para = unsafe { &mut *self.para };
        para.take()
    }

    /// raw yiled without catch passed in para
    #[inline]
    fn raw_yield(&mut self, env: &ContextStack, context: &mut Context, v: T) {
        // check the context
        if !context.is_generator() {
            info!("yield from none generator context");
            // do nothing, just return
            return;
            // panic!(Error::ContextErr);
        }

        self.set_ret(v);
        context._ref -= 1;
        raw_yield_now(env, context);

        // here we just panic to exit the func
        if context._ref != 1 {
            panic!(Error::Cancel);
        }
    }

    /// yiled something without catch passed in para
    #[inline]
    pub fn yield_with(&mut self, v: T) {
        let env = ContextStack::current();
        let context = env.top();
        self.raw_yield(&env, context, v);
    }

    /// yiled and get the send para
    // it's totally safe that we can refer to the function block
    // since we will come back later
    #[inline]
    pub fn yield_(&mut self, v: T) -> Option<A> {
        self.yield_with(v);
        self.get_yield()
    }

    /// `yiled_from`
    /// the from generator must has the same type as itself
    pub fn yield_from(&mut self, mut g: Box<GeneratorImpl<A, T>>) -> Option<A> {
        let env = ContextStack::current();
        let context = env.top();
        let mut p = self.get_yield();
        while !g.is_done() {
            match g.raw_send(p) {
                None => return None,
                Some(r) => self.raw_yield(&env, context, r),
            }
            p = self.get_yield();
        }
        p
    }
}
<|endoftext|>


<commit_before>extern crate serde;
extern crate std;

use self::std::prelude::v1::*;
use self::serde::{de, Deserialize, Deserializer, Serialize, Serializer};

use Uuid;

impl Serialize for Uuid {
    fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
        serializer.serialize_str(&self.hyphenated().to_string())
    }
}

impl Deserialize for Uuid {
    fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
        struct UuidVisitor;

        impl de::Visitor for UuidVisitor {
            type Value = Uuid;

            fn visit_str<E: de::Error>(&mut self, value: &str) -> Result<Uuid, E> {
                value.parse::<Uuid>().map_err(|e| E::custom(e.to_string()))
            }

            fn visit_bytes<E: de::Error>(&mut self, value: &[u8]) -> Result<Uuid, E> {
                Uuid::from_bytes(value).map_err(|e| E::custom(e.to_string()))
            }
        }

        deserializer.deserialize(UuidVisitor)
    }
}
<commit_msg>Fix for bincode deserialize<commit_after>extern crate serde;
extern crate std;

use self::std::prelude::v1::*;
use self::serde::{de, Deserialize, Deserializer, Serialize, Serializer};

use Uuid;

impl Serialize for Uuid {
    fn serialize<S: Serializer>(&self, serializer: &mut S) -> Result<(), S::Error> {
        serializer.serialize_str(&self.hyphenated().to_string())
    }
}

impl Deserialize for Uuid {
    fn deserialize<D: Deserializer>(deserializer: &mut D) -> Result<Self, D::Error> {
        struct UuidVisitor;

        impl de::Visitor for UuidVisitor {
            type Value = Uuid;

            fn visit_str<E: de::Error>(&mut self, value: &str) -> Result<Uuid, E> {
                value.parse::<Uuid>().map_err(|e| E::custom(e.to_string()))
            }

            fn visit_bytes<E: de::Error>(&mut self, value: &[u8]) -> Result<Uuid, E> {
                Uuid::from_bytes(value).map_err(|e| E::custom(e.to_string()))
            }
        }

        deserializer.deserialize(UuidVisitor)
                    .or_else(|_| deserializer.deserialize_string(UuidVisitor))
                    .or_else(|_| deserializer.deserialize_bytes(UuidVisitor))
    }
}
<|endoftext|>


<commit_before>use std::io;
use std::io::{ErrorKind, Result};
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{env, fs};

use yaml_rust::yaml::Hash;
use yaml_rust::{Yaml, YamlEmitter, YamlLoader};

pub struct Config {
    pub coordinator: Option<reqwest::Url>,
    pub helper_bind: SocketAddr,
    pub coordinator_bind: SocketAddr,

    pub process_limit: usize,
    pub cache_dir: PathBuf,
    pub cache_limit_mb: u32,
}

const CONFIG_FILE_NAME: &str = "octobuild.conf";

#[cfg(windows)]
const DEFAULT_CACHE_DIR: &str = "~/.octobuild";
#[cfg(unix)]
const DEFAULT_CACHE_DIR: &str = "~/.cache/octobuild";

const PARAM_HELPER_BIND: &str = "helper_bind";
const PARAM_COORDINATOR_BIND: &str = "coordinator_bind";
const PARAM_COORDINATOR: &str = "coordinator";
const PARAM_CACHE_LIMIT: &str = "cache_limit_mb";
const PARAM_CACHE_PATH: &str = "cache_path";
const PARAM_PROCESS_LIMIT: &str = "process_limit";

impl Config {
    pub fn new() -> Result<Self> {
        let local = get_local_config_path().and_then(|v| load_config(v).ok());
        let global = get_global_config_path().and_then(|v| load_config(v).ok());
        Config::load(&local, &global, false)
    }

    pub fn get_coordinator_addrs(&self) -> Result<Vec<SocketAddr>> {
        match self.coordinator {
            Some(ref url) => url.socket_addrs(|| match url.scheme() {
                "http" => Some(80),
                _ => None,
            }),
            None => Ok(Vec::new()),
        }
    }

    pub fn defaults() -> Result<Self> {
        Config::load(&None, &None, true)
    }

    fn load(local: &Option<Yaml>, global: &Option<Yaml>, defaults: bool) -> Result<Self> {
        let cache_limit_mb = get_config(local, global, PARAM_CACHE_LIMIT, |v| {
            v.as_i64().map(|v| v as u32)
        })
        .unwrap_or(16 * 1024);
        let cache_path = if defaults {
            None
        } else {
            env::var("OCTOBUILD_CACHE")
                .ok()
                .and_then(|v| if v == "" { None } else { Some(v) })
        }
        .or_else(|| {
            get_config(local, global, PARAM_CACHE_PATH, |v| {
                v.as_str().map(|v| v.to_string())
            })
        })
        .unwrap_or_else(|| DEFAULT_CACHE_DIR.to_string());
        let process_limit = get_config(local, global, PARAM_PROCESS_LIMIT, |v| {
            v.as_i64().map(|v| v as usize)
        })
        .unwrap_or_else(num_cpus::get);
        let coordinator = get_config(local, global, PARAM_COORDINATOR, |v| {
            if v.is_null() {
                None
            } else {
                v.as_str().and_then(|v| {
                    reqwest::Url::parse(v)
                        .map(|mut v| {
                            v.set_path("");
                            v
                        })
                        .ok()
                })
            }
        });
        let helper_bind = get_config(local, global, PARAM_HELPER_BIND, |v| {
            v.as_str().and_then(|v| FromStr::from_str(v).ok())
        })
        .unwrap_or_else(|| SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0)));
        let coordinator_bind = get_config(local, global, PARAM_COORDINATOR_BIND, |v| {
            v.as_str().and_then(|v| FromStr::from_str(v).ok())
        })
        .unwrap_or_else(|| SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 3000)));

        Ok(Config {
            process_limit,
            cache_dir: replace_home(&cache_path)?,
            cache_limit_mb,
            coordinator,
            helper_bind,
            coordinator_bind,
        })
    }

    fn show(&self) {
        let mut content = String::new();

        let mut y = Hash::new();
        y.insert(
            Yaml::String(PARAM_PROCESS_LIMIT.to_string()),
            Yaml::Integer(self.process_limit as i64),
        );
        y.insert(
            Yaml::String(PARAM_CACHE_LIMIT.to_string()),
            Yaml::Integer(i64::from(self.cache_limit_mb)),
        );
        y.insert(
            Yaml::String(PARAM_CACHE_PATH.to_string()),
            Yaml::String(self.cache_dir.to_str().unwrap().to_string()),
        );
        y.insert(
            Yaml::String(PARAM_COORDINATOR.to_string()),
            self.coordinator
                .as_ref()
                .map_or(Yaml::Null, |v| Yaml::String(v.as_str().to_string())),
        );
        y.insert(
            Yaml::String(PARAM_HELPER_BIND.to_string()),
            Yaml::String(self.helper_bind.to_string()),
        );
        y.insert(
            Yaml::String(PARAM_COORDINATOR_BIND.to_string()),
            Yaml::String(self.coordinator_bind.to_string()),
        );
        YamlEmitter::new(&mut content).dump(&Yaml::Hash(y)).unwrap();
        println!("{}", content);
    }

    pub fn help() {
        println!("Octobuild configuration:");
        println!(
            "  system config path: {}",
            get_global_config_path()
                .map(|v| v.to_str().unwrap().to_string())
                .unwrap_or_else(|| "none".to_string())
        );
        println!(
            "  user config path:   {}",
            get_local_config_path()
                .map(|v| v.to_str().unwrap().to_string())
                .unwrap_or_else(|| "none".to_string())
        );
        println!();
        println!("Actual configuration:");
        match Config::new() {
            Ok(c) => {
                c.show();
            }
            Err(e) => {
                println!("  ERROR: {}", e);
            }
        }
        println!();
        println!("Default configuration:");
        match Config::defaults() {
            Ok(c) => {
                c.show();
            }
            Err(e) => {
                println!("  ERROR: {}", e);
            }
        }
        println!();
    }
}

fn get_config<F, T>(local: &Option<Yaml>, global: &Option<Yaml>, param: &str, op: F) -> Option<T>
where
    F: Fn(&Yaml) -> Option<T>,
{
    None.or_else(|| local.as_ref().and_then(|i| op(&i[param])))
        .or_else(|| global.as_ref().and_then(|i| op(&i[param])))
}

fn load_config<P: AsRef<Path>>(path: P) -> Result<Yaml> {
    let content = fs::read_to_string(path)?;
    match YamlLoader::load_from_str(&content) {
        Ok(ref mut docs) => Ok(docs.pop().unwrap()),
        Err(e) => Err(io::Error::new(ErrorKind::InvalidInput, e)),
    }
}

fn get_local_config_path() -> Option<PathBuf> {
    dirs::home_dir().map(|v| v.join(&(".".to_string() + CONFIG_FILE_NAME)))
}

#[cfg(windows)]
fn get_global_config_path() -> Option<PathBuf> {
    env::var("ProgramData")
        .ok()
        .map(|v| Path::new(&v).join("octobuild").join(CONFIG_FILE_NAME))
}

#[cfg(unix)]
fn get_global_config_path() -> Option<PathBuf> {
    Some(
        Path::new("/etc/octobuild")
            .join(CONFIG_FILE_NAME)
            .to_path_buf(),
    )
}

fn replace_home(path: &str) -> Result<PathBuf> {
    if path.starts_with("~/") {
        dirs::home_dir()
            .map(|v| v.join(&path[2..]))
            .ok_or_else(|| io::Error::new(ErrorKind::NotFound, "Can't determinate user HOME path"))
    } else {
        Ok(Path::new(path).to_path_buf())
    }
}
<commit_msg>Fix clippy warning<commit_after>use std::io;
use std::io::{ErrorKind, Result};
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4};
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::{env, fs};

use yaml_rust::yaml::Hash;
use yaml_rust::{Yaml, YamlEmitter, YamlLoader};

pub struct Config {
    pub coordinator: Option<reqwest::Url>,
    pub helper_bind: SocketAddr,
    pub coordinator_bind: SocketAddr,

    pub process_limit: usize,
    pub cache_dir: PathBuf,
    pub cache_limit_mb: u32,
}

const CONFIG_FILE_NAME: &str = "octobuild.conf";

#[cfg(windows)]
const DEFAULT_CACHE_DIR: &str = "~/.octobuild";
#[cfg(unix)]
const DEFAULT_CACHE_DIR: &str = "~/.cache/octobuild";

const PARAM_HELPER_BIND: &str = "helper_bind";
const PARAM_COORDINATOR_BIND: &str = "coordinator_bind";
const PARAM_COORDINATOR: &str = "coordinator";
const PARAM_CACHE_LIMIT: &str = "cache_limit_mb";
const PARAM_CACHE_PATH: &str = "cache_path";
const PARAM_PROCESS_LIMIT: &str = "process_limit";

impl Config {
    pub fn new() -> Result<Self> {
        let local = get_local_config_path().and_then(|v| load_config(v).ok());
        let global = get_global_config_path().and_then(|v| load_config(v).ok());
        Config::load(&local, &global, false)
    }

    pub fn get_coordinator_addrs(&self) -> Result<Vec<SocketAddr>> {
        match self.coordinator {
            Some(ref url) => url.socket_addrs(|| match url.scheme() {
                "http" => Some(80),
                _ => None,
            }),
            None => Ok(Vec::new()),
        }
    }

    pub fn defaults() -> Result<Self> {
        Config::load(&None, &None, true)
    }

    fn load(local: &Option<Yaml>, global: &Option<Yaml>, defaults: bool) -> Result<Self> {
        let cache_limit_mb = get_config(local, global, PARAM_CACHE_LIMIT, |v| {
            v.as_i64().map(|v| v as u32)
        })
        .unwrap_or(16 * 1024);
        let cache_path = if defaults {
            None
        } else {
            env::var("OCTOBUILD_CACHE")
                .ok()
                .and_then(|v| if v == "" { None } else { Some(v) })
        }
        .or_else(|| {
            get_config(local, global, PARAM_CACHE_PATH, |v| {
                v.as_str().map(|v| v.to_string())
            })
        })
        .unwrap_or_else(|| DEFAULT_CACHE_DIR.to_string());
        let process_limit = get_config(local, global, PARAM_PROCESS_LIMIT, |v| {
            v.as_i64().map(|v| v as usize)
        })
        .unwrap_or_else(num_cpus::get);
        let coordinator = get_config(local, global, PARAM_COORDINATOR, |v| {
            if v.is_null() {
                None
            } else {
                v.as_str().and_then(|v| {
                    reqwest::Url::parse(v)
                        .map(|mut v| {
                            v.set_path("");
                            v
                        })
                        .ok()
                })
            }
        });
        let helper_bind = get_config(local, global, PARAM_HELPER_BIND, |v| {
            v.as_str().and_then(|v| FromStr::from_str(v).ok())
        })
        .unwrap_or_else(|| SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 0)));
        let coordinator_bind = get_config(local, global, PARAM_COORDINATOR_BIND, |v| {
            v.as_str().and_then(|v| FromStr::from_str(v).ok())
        })
        .unwrap_or_else(|| SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(0, 0, 0, 0), 3000)));

        Ok(Config {
            process_limit,
            cache_dir: replace_home(&cache_path)?,
            cache_limit_mb,
            coordinator,
            helper_bind,
            coordinator_bind,
        })
    }

    fn show(&self) {
        let mut content = String::new();

        let mut y = Hash::new();
        y.insert(
            Yaml::String(PARAM_PROCESS_LIMIT.to_string()),
            Yaml::Integer(self.process_limit as i64),
        );
        y.insert(
            Yaml::String(PARAM_CACHE_LIMIT.to_string()),
            Yaml::Integer(i64::from(self.cache_limit_mb)),
        );
        y.insert(
            Yaml::String(PARAM_CACHE_PATH.to_string()),
            Yaml::String(self.cache_dir.to_str().unwrap().to_string()),
        );
        y.insert(
            Yaml::String(PARAM_COORDINATOR.to_string()),
            self.coordinator
                .as_ref()
                .map_or(Yaml::Null, |v| Yaml::String(v.as_str().to_string())),
        );
        y.insert(
            Yaml::String(PARAM_HELPER_BIND.to_string()),
            Yaml::String(self.helper_bind.to_string()),
        );
        y.insert(
            Yaml::String(PARAM_COORDINATOR_BIND.to_string()),
            Yaml::String(self.coordinator_bind.to_string()),
        );
        YamlEmitter::new(&mut content).dump(&Yaml::Hash(y)).unwrap();
        println!("{}", content);
    }

    pub fn help() {
        println!("Octobuild configuration:");
        println!(
            "  system config path: {}",
            get_global_config_path()
                .map(|v| v.to_str().unwrap().to_string())
                .unwrap_or_else(|| "none".to_string())
        );
        println!(
            "  user config path:   {}",
            get_local_config_path()
                .map(|v| v.to_str().unwrap().to_string())
                .unwrap_or_else(|| "none".to_string())
        );
        println!();
        println!("Actual configuration:");
        match Config::new() {
            Ok(c) => {
                c.show();
            }
            Err(e) => {
                println!("  ERROR: {}", e);
            }
        }
        println!();
        println!("Default configuration:");
        match Config::defaults() {
            Ok(c) => {
                c.show();
            }
            Err(e) => {
                println!("  ERROR: {}", e);
            }
        }
        println!();
    }
}

fn get_config<F, T>(local: &Option<Yaml>, global: &Option<Yaml>, param: &str, op: F) -> Option<T>
where
    F: Fn(&Yaml) -> Option<T>,
{
    None.or_else(|| local.as_ref().and_then(|i| op(&i[param])))
        .or_else(|| global.as_ref().and_then(|i| op(&i[param])))
}

fn load_config<P: AsRef<Path>>(path: P) -> Result<Yaml> {
    let content = fs::read_to_string(path)?;
    match YamlLoader::load_from_str(&content) {
        Ok(ref mut docs) => Ok(docs.pop().unwrap()),
        Err(e) => Err(io::Error::new(ErrorKind::InvalidInput, e)),
    }
}

fn get_local_config_path() -> Option<PathBuf> {
    dirs::home_dir().map(|v| v.join(&(".".to_string() + CONFIG_FILE_NAME)))
}

#[cfg(windows)]
fn get_global_config_path() -> Option<PathBuf> {
    env::var("ProgramData")
        .ok()
        .map(|v| Path::new(&v).join("octobuild").join(CONFIG_FILE_NAME))
}

#[cfg(unix)]
fn get_global_config_path() -> Option<PathBuf> {
    Some(Path::new("/etc/octobuild").join(CONFIG_FILE_NAME))
}

fn replace_home(path: &str) -> Result<PathBuf> {
    if path.starts_with("~/") {
        dirs::home_dir()
            .map(|v| v.join(&path[2..]))
            .ok_or_else(|| io::Error::new(ErrorKind::NotFound, "Can't determinate user HOME path"))
    } else {
        Ok(Path::new(path).to_path_buf())
    }
}
<|endoftext|>