<commit_before>use std::fmt::{Show,Formatter};
use std::fmt;
use std::hash::Hasher;
use std::hash::sip::SipHasher;
use std::str;

use core::source::{Source, SourceId, GitKind, Location, Remote, Local};
use core::{Package, PackageId, Summary, Registry, Dependency};
use util::{CargoResult, Config, to_hex};
use sources::PathSource;
use sources::git::utils::{GitReference,GitRemote,Master,Other};

/* TODO: Refactor GitSource to delegate to a PathSource
 */
pub struct GitSource<'a, 'b> {
    remote: GitRemote,
    reference: GitReference,
    db_path: Path,
    checkout_path: Path,
    source_id: SourceId,
    path_source: Option<PathSource>,
    config: &'a mut Config<'b>
}

impl<'a, 'b> GitSource<'a, 'b> {
    pub fn new<'a, 'b>(source_id: &SourceId,
                       config: &'a mut Config<'b>) -> GitSource<'a, 'b> {
        assert!(source_id.is_git(), "id is not git, id={}", source_id);

        let reference = match source_id.kind {
            GitKind(ref reference) => reference,
            _ => fail!("Not a git source; id={}", source_id)
        };

        let remote = GitRemote::new(source_id.get_location());
        let ident = ident(source_id.get_location());

        let db_path = config.git_db_path()
            .join(ident.as_slice());

        let checkout_path = config.git_checkout_path()
            .join(ident.as_slice()).join(reference.as_slice());

        let reference = match source_id.precise {
            Some(ref s) => s,
            None => reference,
        };

        GitSource {
            remote: remote,
            reference: GitReference::for_str(reference.as_slice()),
            db_path: db_path,
            checkout_path: checkout_path,
            source_id: source_id.clone(),
            path_source: None,
            config: config
        }
    }

    pub fn get_location(&self) -> &Location {
        self.remote.get_location()
    }
}

fn ident(location: &Location) -> String {
    let hasher = SipHasher::new_with_keys(0,0);

    // FIXME: this really should be able to not use to_str() everywhere, but the
    //        compiler seems to currently ask for static lifetimes spuriously.
    //        Perhaps related to rust-lang/rust#15144
    let ident = match *location {
        Local(ref path) => {
            let last = path.components().last().unwrap();
            str::from_utf8(last).unwrap().to_string()
        }
        Remote(ref url) => {
            let path = url.path().unwrap().connect("/");
            let path = canonicalize_url(path.as_slice());
            path.as_slice().split('/').last().unwrap().to_string()
        }
    };

    let ident = if ident.as_slice() == "" {
        "_empty".to_string()
    } else {
        ident
    };

    let location = canonicalize_url(location.to_string().as_slice());

    format!("{}-{}", ident, to_hex(hasher.hash(&location.as_slice())))
}

fn strip_trailing_slash(path: &str) -> &str {
    // Remove the trailing '/' so that 'split' doesn't give us
    // an empty string, making '../foo/' and '../foo' both
    // result in the name 'foo' (#84)
    if path.as_bytes().last() != Some(&('/' as u8)) {
        path.clone()
    } else {
        path.slice(0, path.len() - 1)
    }
}

// Some hacks and heuristics for making equivalent URLs hash the same
pub fn canonicalize_url(url: &str) -> String {
    let url = strip_trailing_slash(url);

    // HACKHACK: For github URL's specifically just lowercase
    // everything.  GitHub traits both the same, but they hash
    // differently, and we're gonna be hashing them. This wants a more
    // general solution, and also we're almost certainly not using the
    // same case conversion rules that GitHub does. (#84)

    let lower_url = url.chars().map(|c|c.to_lowercase()).collect::<String>();
    let url = if lower_url.as_slice().contains("github.com") {
        if lower_url.as_slice().starts_with("https") {
            lower_url
        } else {
            let pos = lower_url.as_slice().find_str("://").unwrap_or(0);
            "https".to_string() + lower_url.as_slice().slice_from(pos)
        }
    } else {
        url.to_string()
    };

    // Repos generally can be accessed with or w/o '.git'
    let url = if !url.as_slice().ends_with(".git") {
        url
    } else {
        url.as_slice().slice(0, url.len() - 4).to_string()
    };

    return url;
}

impl<'a, 'b> Show for GitSource<'a, 'b> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "git repo at {}", self.remote.get_location()));

        match self.reference {
            Master => Ok(()),
            Other(ref reference) => write!(f, " ({})", reference)
        }
    }
}

impl<'a, 'b> Registry for GitSource<'a, 'b> {
    fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
        let src = self.path_source.as_mut()
                      .expect("BUG: update() must be called before query()");
        src.query(dep)
    }
}

impl<'a, 'b> Source for GitSource<'a, 'b> {
    fn update(&mut self) -> CargoResult<()> {
        let actual_rev = self.remote.rev_for(&self.db_path,
                                             self.reference.as_slice());
        let should_update = self.config.update_remotes() || actual_rev.is_err();

        let repo = if should_update {
            try!(self.config.shell().status("Updating",
                format!("git repository `{}`", self.remote.get_location())));

            log!(5, "updating git source `{}`", self.remote);
            try!(self.remote.checkout(&self.db_path))
        } else {
            self.remote.db_at(&self.db_path)
        };
        let actual_rev = match actual_rev {
            Ok(rev) => rev,
            Err(..) => try!(repo.rev_for(self.reference.as_slice())),
        };

        try!(repo.copy_to(actual_rev.clone(), &self.checkout_path));

        let source_id = self.source_id.with_precise(actual_rev.to_string());
        let path_source = PathSource::new(&self.checkout_path, &source_id);

        self.path_source = Some(path_source);
        self.path_source.as_mut().unwrap().update()
    }

    fn download(&self, _: &[PackageId]) -> CargoResult<()> {
        // TODO: assert! that the PackageId is contained by the source
        Ok(())
    }

    fn get(&self, ids: &[PackageId]) -> CargoResult<Vec<Package>> {
        log!(5, "getting packages for package ids `{}` from `{}`", ids, self.remote);
        self.path_source.as_ref().expect("BUG: update() must be called before get()").get(ids)
    }

    fn fingerprint(&self, _pkg: &Package) -> CargoResult<String> {
        let db = self.remote.db_at(&self.db_path);
        db.rev_for(self.reference.as_slice()).map(|r| r.to_string())
    }
}

#[cfg(test)]
mod test {
    use url::Url;
    use core::source::Remote;
    use super::ident;
    use util::ToUrl;

    #[test]
    pub fn test_url_to_path_ident_with_path() {
        let ident = ident(&Remote(url("https://github.com/carlhuda/cargo")));
        assert_eq!(ident.as_slice(), "cargo-0eed735c8ffd7c88");
    }

    #[test]
    pub fn test_url_to_path_ident_without_path() {
        let ident = ident(&Remote(url("https://github.com")));
        assert_eq!(ident.as_slice(), "_empty-fc065c9b6b16fc00");
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_trailing_url_slash() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston/")));
        let ident2 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_lowercasing_github_urls() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        let ident2 = ident(&Remote(url("https://github.com/pistondevelopers/piston")));
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_dot_git() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        let ident2 = ident(&Remote(url("https://github.com/PistonDevelopers/piston.git")));
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_different_protocls() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        let ident2 = ident(&Remote(url("git://github.com/PistonDevelopers/piston")));
        assert_eq!(ident1, ident2);
    }

    fn url(s: &str) -> Url {
        s.to_url().unwrap()
    }
}
<commit_msg>Avoid executing git in fingerprint()<commit_after>use std::fmt::{Show,Formatter};
use std::fmt;
use std::hash::Hasher;
use std::hash::sip::SipHasher;
use std::str;

use core::source::{Source, SourceId, GitKind, Location, Remote, Local};
use core::{Package, PackageId, Summary, Registry, Dependency};
use util::{CargoResult, Config, to_hex};
use sources::PathSource;
use sources::git::utils::{GitReference, GitRemote, Master, Other, GitRevision};

/* TODO: Refactor GitSource to delegate to a PathSource
 */
pub struct GitSource<'a, 'b> {
    remote: GitRemote,
    reference: GitReference,
    db_path: Path,
    checkout_path: Path,
    source_id: SourceId,
    path_source: Option<PathSource>,
    rev: Option<GitRevision>,
    config: &'a mut Config<'b>,
}

impl<'a, 'b> GitSource<'a, 'b> {
    pub fn new<'a, 'b>(source_id: &SourceId,
                       config: &'a mut Config<'b>) -> GitSource<'a, 'b> {
        assert!(source_id.is_git(), "id is not git, id={}", source_id);

        let reference = match source_id.kind {
            GitKind(ref reference) => reference,
            _ => fail!("Not a git source; id={}", source_id)
        };

        let remote = GitRemote::new(source_id.get_location());
        let ident = ident(source_id.get_location());

        let db_path = config.git_db_path()
            .join(ident.as_slice());

        let checkout_path = config.git_checkout_path()
            .join(ident.as_slice()).join(reference.as_slice());

        let reference = match source_id.precise {
            Some(ref s) => s,
            None => reference,
        };

        GitSource {
            remote: remote,
            reference: GitReference::for_str(reference.as_slice()),
            db_path: db_path,
            checkout_path: checkout_path,
            source_id: source_id.clone(),
            path_source: None,
            rev: None,
            config: config,
        }
    }

    pub fn get_location(&self) -> &Location {
        self.remote.get_location()
    }
}

fn ident(location: &Location) -> String {
    let hasher = SipHasher::new_with_keys(0,0);

    // FIXME: this really should be able to not use to_str() everywhere, but the
    //        compiler seems to currently ask for static lifetimes spuriously.
    //        Perhaps related to rust-lang/rust#15144
    let ident = match *location {
        Local(ref path) => {
            let last = path.components().last().unwrap();
            str::from_utf8(last).unwrap().to_string()
        }
        Remote(ref url) => {
            let path = url.path().unwrap().connect("/");
            let path = canonicalize_url(path.as_slice());
            path.as_slice().split('/').last().unwrap().to_string()
        }
    };

    let ident = if ident.as_slice() == "" {
        "_empty".to_string()
    } else {
        ident
    };

    let location = canonicalize_url(location.to_string().as_slice());

    format!("{}-{}", ident, to_hex(hasher.hash(&location.as_slice())))
}

fn strip_trailing_slash(path: &str) -> &str {
    // Remove the trailing '/' so that 'split' doesn't give us
    // an empty string, making '../foo/' and '../foo' both
    // result in the name 'foo' (#84)
    if path.as_bytes().last() != Some(&('/' as u8)) {
        path.clone()
    } else {
        path.slice(0, path.len() - 1)
    }
}

// Some hacks and heuristics for making equivalent URLs hash the same
pub fn canonicalize_url(url: &str) -> String {
    let url = strip_trailing_slash(url);

    // HACKHACK: For github URL's specifically just lowercase
    // everything.  GitHub traits both the same, but they hash
    // differently, and we're gonna be hashing them. This wants a more
    // general solution, and also we're almost certainly not using the
    // same case conversion rules that GitHub does. (#84)

    let lower_url = url.chars().map(|c|c.to_lowercase()).collect::<String>();
    let url = if lower_url.as_slice().contains("github.com") {
        if lower_url.as_slice().starts_with("https") {
            lower_url
        } else {
            let pos = lower_url.as_slice().find_str("://").unwrap_or(0);
            "https".to_string() + lower_url.as_slice().slice_from(pos)
        }
    } else {
        url.to_string()
    };

    // Repos generally can be accessed with or w/o '.git'
    let url = if !url.as_slice().ends_with(".git") {
        url
    } else {
        url.as_slice().slice(0, url.len() - 4).to_string()
    };

    return url;
}

impl<'a, 'b> Show for GitSource<'a, 'b> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        try!(write!(f, "git repo at {}", self.remote.get_location()));

        match self.reference {
            Master => Ok(()),
            Other(ref reference) => write!(f, " ({})", reference)
        }
    }
}

impl<'a, 'b> Registry for GitSource<'a, 'b> {
    fn query(&mut self, dep: &Dependency) -> CargoResult<Vec<Summary>> {
        let src = self.path_source.as_mut()
                      .expect("BUG: update() must be called before query()");
        src.query(dep)
    }
}

impl<'a, 'b> Source for GitSource<'a, 'b> {
    fn update(&mut self) -> CargoResult<()> {
        let actual_rev = self.remote.rev_for(&self.db_path,
                                             self.reference.as_slice());
        let should_update = self.config.update_remotes() || actual_rev.is_err();

        let repo = if should_update {
            try!(self.config.shell().status("Updating",
                format!("git repository `{}`", self.remote.get_location())));

            log!(5, "updating git source `{}`", self.remote);
            try!(self.remote.checkout(&self.db_path))
        } else {
            self.remote.db_at(&self.db_path)
        };
        let actual_rev = match actual_rev {
            Ok(rev) => rev,
            Err(..) => try!(repo.rev_for(self.reference.as_slice())),
        };

        try!(repo.copy_to(actual_rev.clone(), &self.checkout_path));

        let source_id = self.source_id.with_precise(actual_rev.to_string());
        let path_source = PathSource::new(&self.checkout_path, &source_id);

        self.path_source = Some(path_source);
        self.rev = Some(actual_rev);
        self.path_source.as_mut().unwrap().update()
    }

    fn download(&self, _: &[PackageId]) -> CargoResult<()> {
        // TODO: assert! that the PackageId is contained by the source
        Ok(())
    }

    fn get(&self, ids: &[PackageId]) -> CargoResult<Vec<Package>> {
        log!(5, "getting packages for package ids `{}` from `{}`", ids, self.remote);
        self.path_source.as_ref().expect("BUG: update() must be called before get()").get(ids)
    }

    fn fingerprint(&self, _pkg: &Package) -> CargoResult<String> {
        Ok(self.rev.get_ref().to_string())
    }
}

#[cfg(test)]
mod test {
    use url::Url;
    use core::source::Remote;
    use super::ident;
    use util::ToUrl;

    #[test]
    pub fn test_url_to_path_ident_with_path() {
        let ident = ident(&Remote(url("https://github.com/carlhuda/cargo")));
        assert_eq!(ident.as_slice(), "cargo-0eed735c8ffd7c88");
    }

    #[test]
    pub fn test_url_to_path_ident_without_path() {
        let ident = ident(&Remote(url("https://github.com")));
        assert_eq!(ident.as_slice(), "_empty-fc065c9b6b16fc00");
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_trailing_url_slash() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston/")));
        let ident2 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_lowercasing_github_urls() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        let ident2 = ident(&Remote(url("https://github.com/pistondevelopers/piston")));
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_by_stripping_dot_git() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        let ident2 = ident(&Remote(url("https://github.com/PistonDevelopers/piston.git")));
        assert_eq!(ident1, ident2);
    }

    #[test]
    fn test_canonicalize_idents_different_protocls() {
        let ident1 = ident(&Remote(url("https://github.com/PistonDevelopers/piston")));
        let ident2 = ident(&Remote(url("git://github.com/PistonDevelopers/piston")));
        assert_eq!(ident1, ident2);
    }

    fn url(s: &str) -> Url {
        s.to_url().unwrap()
    }
}
<|endoftext|>


<commit_before>use std::path::PathBuf;
use std::fs::File;
use std::ffi::OsString;
use std::io::Write;

use app::parser::Parser;
use shell::Shell;
use args::{ArgSettings, OptBuilder};

macro_rules! w {
    ($_self:ident, $f:ident, $to_w:expr) => {
        match $f.write_all($to_w) {
            Ok(..) => (),
            Err(..) => panic!(format!("Failed to write to file completions file")),
        }
    };
}

pub struct ComplGen<'a, 'b> where 'a: 'b {
    p: &'b Parser<'a, 'b>,
    out_dir: OsString,
}

impl<'a, 'b> ComplGen<'a, 'b> {
    pub fn new(p: &'b Parser<'a, 'b>, od: OsString) -> Self {
        ComplGen {
            p: p,
            out_dir: od,
        }
    }

    pub fn generate(&self, for_shell: Shell) {
        match for_shell {
            Shell::Bash => self.gen_bash(),
        }
    }

    fn gen_bash(&self) {
        use std::error::Error;
        let out_dir = PathBuf::from(&self.out_dir);

        let mut file = match File::create(out_dir.join("bash.sh")) {
            Err(why) => panic!("couldn't create bash completion file: {}",
                why.description()),
            Ok(file) => file,
        };
        w!(self, file, format!(
"_{name}() {{
    local i cur prev opts cmds
    COMPREPLY=()
    cur=\"${{COMP_WORDS[COMP_CWORD]}}\"
    prev=\"${{COMP_WORDS[COMP_CWORD-1]}}\"
    cmd=\"\"
    opts=\"\"

    for i in ${{COMP_WORDS[@]}}
    do
        case \"${{i}}\" in
            {name})
                cmd=\"{name}\"
                ;;
            {subcmds}
            *)
                ;;
        esac
    done

    case \"${{cmd}}\" in
        {name})
            opts=\"{name_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {name_opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            ;;
        {subcmd_details}
    esac
}}

complete -F _{name} {name}
",
            name=self.p.meta.bin_name.as_ref().unwrap(),
            name_opts=self.all_options_for_path(self.p.meta.bin_name.as_ref().unwrap()),
            name_opts_details=self.option_details_for_path(self.p.meta.bin_name.as_ref().unwrap()),
            subcmds=self.all_subcommands(),
            subcmd_details=self.subcommand_details()
        ).as_bytes());
    }

    fn all_subcommands(&self) -> String {
        let mut subcmds = String::new();
        let mut scs = get_all_subcommands(self.p);
        scs.sort();
        scs.dedup();

        for sc in &scs {
            subcmds = format!(
                "{}
            {name})
                cmd+=\"_{name}\"
                ;;",
                subcmds,
                name=sc.replace("-", "_"));
        }

        subcmds
    }

    fn subcommand_details(&self) -> String {
        let mut subcmd_dets = String::new();
        let mut scs = get_all_subcommand_paths(self.p, true);
        scs.sort();
        scs.dedup();

        for sc in &scs {
            subcmd_dets = format!(
                "{}
        {subcmd})
            opts=\"{sc_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            ;;",
                subcmd_dets,
                subcmd=sc.replace("-", "_"),
                sc_opts=self.all_options_for_path(&*sc),
                level=sc.split("_").map(|_|1).fold(0, |acc, n| acc + n),
                opts_details=self.option_details_for_path(&*sc)
            );
        }

        subcmd_dets
    }

    fn all_options_for_path(&self, path: &str) -> String {
        let mut p = self.p;
        for sc in path.split("_").skip(1) {
            debugln!("iter;sc={}", sc);
            p = &p.subcommands.iter().filter(|s| s.p.meta.name == sc).next().unwrap().p;
        }
        let mut opts = p.short_list.iter().fold(String::new(), |acc, s| format!("{} -{}", acc, s));
        opts = format!("{} {}", opts, p.long_list.iter()
                                                 .fold(String::new(), |acc, l| format!("{} --{}", acc, l)));
        opts = format!("{} {}", opts, p.positionals.values()
                                                   .fold(String::new(), |acc, p| format!("{} {}", acc, p)));
        opts = format!("{} {}", opts, p.subcommands.iter()
                                                   .fold(String::new(), |acc, s| format!("{} {}", acc, s.p.meta.name)));
        opts
    }

    fn option_details_for_path(&self, path: &str) -> String {
        let mut p = self.p;
        for sc in path.split("_").skip(1) {
            debugln!("iter;sc={}", sc);
            p = &p.subcommands.iter().filter(|s| s.p.meta.name == sc).next().unwrap().p;
        }
        let mut opts = String::new();
        for o in &p.opts {
            if let Some(l) = o.long {
                opts = format!("{}
                --{})
                    COMPREPLY=(\"{}\")
                    ;;", opts, l, vals_for(o));
            }
            if let Some(s) = o.short {
                opts = format!("{}
                    -{})
                    COMPREPLY=(\"{}\")
                    ;;", opts, s, vals_for(o));
            }
        }
        opts
    }
}

pub fn get_all_subcommands(p: &Parser) -> Vec<String> {
    let mut subcmds = vec![];
    if !p.has_subcommands() {
        return vec![p.meta.name.clone()]
    }
    for sc in p.subcommands.iter().map(|ref s| s.p.meta.name.clone()) {
        subcmds.push(sc);
    }
    for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommands(&s.p)) {
        subcmds.extend(sc_v);
    }
    subcmds
}

pub fn get_all_subcommand_paths(p: &Parser, first: bool) -> Vec<String> {
    let mut subcmds = vec![];
    if !p.has_subcommands() {
        if !first {
            return vec![p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_")]
        }
        return vec![];
    }
    for sc in p.subcommands.iter()
                           .map(|ref s| s.p.meta.bin_name.as_ref()
                                                         .unwrap()
                                                         .clone()
                                                         .replace(" ", "_")) {
        subcmds.push(sc);
    }
    for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommand_paths(&s.p, false)) {
        subcmds.extend(sc_v);
    }
    subcmds
}

fn vals_for(o: &OptBuilder) -> String {
    use args::AnyArg;
    let mut ret = String::new();
    if let Some(ref vec) = o.val_names() {
        let mut it = vec.iter().peekable();
        while let Some((_, val)) = it.next() {
            ret = format!("{}<{}>{}", ret, val,
                if it.peek().is_some() {
                    " "
                } else {
                    ""
                });
        }
        let num = vec.len();
        if o.is_set(ArgSettings::Multiple) && num == 1 {
            ret = format!("{}...", ret);
        }
    } else if let Some(num) = o.num_vals() {
        let mut it = (0..num).peekable();
        while let Some(_) = it.next() {
            ret = format!("{}<{}>{}", ret, o.name(),
            if it.peek().is_some() {
                " "
            } else {
                ""
            });
        }
        if o.is_set(ArgSettings::Multiple) && num == 1 {
            ret = format!("{}...", ret);
        }
    } else {
        ret = format!("<{}>", o.name());
        if o.is_set(ArgSettings::Multiple) {
            ret = format!("{}...", ret);
        }
    }
    ret
}
<commit_msg>imp(Completions): allows matching on possible values in options<commit_after>use std::path::PathBuf;
use std::fs::File;
use std::ffi::OsString;
use std::io::Write;

use app::parser::Parser;
use shell::Shell;
use args::{ArgSettings, OptBuilder};

macro_rules! w {
    ($_self:ident, $f:ident, $to_w:expr) => {
        match $f.write_all($to_w) {
            Ok(..) => (),
            Err(..) => panic!(format!("Failed to write to file completions file")),
        }
    };
}

pub struct ComplGen<'a, 'b> where 'a: 'b {
    p: &'b Parser<'a, 'b>,
    out_dir: OsString,
}

impl<'a, 'b> ComplGen<'a, 'b> {
    pub fn new(p: &'b Parser<'a, 'b>, od: OsString) -> Self {
        ComplGen {
            p: p,
            out_dir: od,
        }
    }

    pub fn generate(&self, for_shell: Shell) {
        match for_shell {
            Shell::Bash => self.gen_bash(),
        }
    }

    fn gen_bash(&self) {
        use std::error::Error;
        let out_dir = PathBuf::from(&self.out_dir);

        let mut file = match File::create(out_dir.join("bash.sh")) {
            Err(why) => panic!("couldn't create bash completion file: {}",
                why.description()),
            Ok(file) => file,
        };
        w!(self, file, format!(
"_{name}() {{
    local i cur prev opts cmds
    COMPREPLY=()
    cur=\"${{COMP_WORDS[COMP_CWORD]}}\"
    prev=\"${{COMP_WORDS[COMP_CWORD-1]}}\"
    cmd=\"\"
    opts=\"\"

    for i in ${{COMP_WORDS[@]}}
    do
        case \"${{i}}\" in
            {name})
                cmd=\"{name}\"
                ;;
            {subcmds}
            *)
                ;;
        esac
    done

    case \"${{cmd}}\" in
        {name})
            opts=\"{name_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {name_opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            ;;
        {subcmd_details}
    esac
}}

complete -F _{name} {name}
",
            name=self.p.meta.bin_name.as_ref().unwrap(),
            name_opts=self.all_options_for_path(self.p.meta.bin_name.as_ref().unwrap()),
            name_opts_details=self.option_details_for_path(self.p.meta.bin_name.as_ref().unwrap()),
            subcmds=self.all_subcommands(),
            subcmd_details=self.subcommand_details()
        ).as_bytes());
    }

    fn all_subcommands(&self) -> String {
        let mut subcmds = String::new();
        let mut scs = get_all_subcommands(self.p);
        scs.sort();
        scs.dedup();

        for sc in &scs {
            subcmds = format!(
                "{}
            {name})
                cmd+=\"_{name}\"
                ;;",
                subcmds,
                name=sc.replace("-", "_"));
        }

        subcmds
    }

    fn subcommand_details(&self) -> String {
        let mut subcmd_dets = String::new();
        let mut scs = get_all_subcommand_paths(self.p, true);
        scs.sort();
        scs.dedup();

        for sc in &scs {
            subcmd_dets = format!(
                "{}
        {subcmd})
            opts=\"{sc_opts}\"
            if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then
                COMPREPLY=( $(compgen -W \"${{opts}}\" -- ${{cur}}) )
                return 0
            fi
            case \"${{prev}}\" in
                {opts_details}
                *)
                    COMPREPLY=()
                    ;;
            esac
            ;;",
                subcmd_dets,
                subcmd=sc.replace("-", "_"),
                sc_opts=self.all_options_for_path(&*sc),
                level=sc.split("_").map(|_|1).fold(0, |acc, n| acc + n),
                opts_details=self.option_details_for_path(&*sc)
            );
        }

        subcmd_dets
    }

    fn all_options_for_path(&self, path: &str) -> String {
        let mut p = self.p;
        for sc in path.split("_").skip(1) {
            debugln!("iter;sc={}", sc);
            p = &p.subcommands.iter().filter(|s| s.p.meta.name == sc).next().unwrap().p;
        }
        let mut opts = p.short_list.iter().fold(String::new(), |acc, s| format!("{} -{}", acc, s));
        opts = format!("{} {}", opts, p.long_list.iter()
                                                 .fold(String::new(), |acc, l| format!("{} --{}", acc, l)));
        opts = format!("{} {}", opts, p.positionals.values()
                                                   .fold(String::new(), |acc, p| format!("{} {}", acc, p)));
        opts = format!("{} {}", opts, p.subcommands.iter()
                                                   .fold(String::new(), |acc, s| format!("{} {}", acc, s.p.meta.name)));
        opts
    }

    fn option_details_for_path(&self, path: &str) -> String {
        let mut p = self.p;
        for sc in path.split("_").skip(1) {
            debugln!("iter;sc={}", sc);
            p = &p.subcommands.iter().filter(|s| s.p.meta.name == sc).next().unwrap().p;
        }
        let mut opts = String::new();
        for o in &p.opts {
            if let Some(l) = o.long {
                opts = format!("{}
                --{})
                    COMPREPLY=({})
                    return 0
                    ;;", opts, l, vals_for(o));
            }
            if let Some(s) = o.short {
                opts = format!("{}
                    -{})
                    COMPREPLY=({})
                    return 0
                    ;;", opts, s, vals_for(o));
            }
        }
        opts
    }
}

pub fn get_all_subcommands(p: &Parser) -> Vec<String> {
    let mut subcmds = vec![];
    if !p.has_subcommands() {
        return vec![p.meta.name.clone()]
    }
    for sc in p.subcommands.iter().map(|ref s| s.p.meta.name.clone()) {
        subcmds.push(sc);
    }
    for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommands(&s.p)) {
        subcmds.extend(sc_v);
    }
    subcmds
}

pub fn get_all_subcommand_paths(p: &Parser, first: bool) -> Vec<String> {
    let mut subcmds = vec![];
    if !p.has_subcommands() {
        if !first {
            return vec![p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_")]
        }
        return vec![];
    }
    for sc in p.subcommands.iter()
                           .map(|ref s| s.p.meta.bin_name.as_ref()
                                                         .unwrap()
                                                         .clone()
                                                         .replace(" ", "_")) {
        subcmds.push(sc);
    }
    for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommand_paths(&s.p, false)) {
        subcmds.extend(sc_v);
    }
    subcmds
}

fn vals_for(o: &OptBuilder) -> String {
    use args::AnyArg;
    let mut ret = String::new();
    let mut needs_quotes = true;
    if let Some(ref vals) = o.possible_vals() {
        needs_quotes = false;
        ret = format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" "));
    } else if let Some(ref vec) = o.val_names() {
        let mut it = vec.iter().peekable();
        while let Some((_, val)) = it.next() {
            ret = format!("{}<{}>{}", ret, val,
                if it.peek().is_some() {
                    " "
                } else {
                    ""
                });
        }
        let num = vec.len();
        if o.is_set(ArgSettings::Multiple) && num == 1 {
            ret = format!("{}...", ret);
        }
    } else if let Some(num) = o.num_vals() {
        let mut it = (0..num).peekable();
        while let Some(_) = it.next() {
            ret = format!("{}<{}>{}", ret, o.name(),
            if it.peek().is_some() {
                " "
            } else {
                ""
            });
        }
        if o.is_set(ArgSettings::Multiple) && num == 1 {
            ret = format!("{}...", ret);
        }
    } else {
        ret = format!("<{}>", o.name());
        if o.is_set(ArgSettings::Multiple) {
            ret = format!("{}...", ret);
        }
    }
    if needs_quotes {
        ret = format!("\"{}\"", ret);
    }
    ret
}
<|endoftext|>


<commit_before>use fake_adapter::FakeBluetoothAdapter;
use std::cell::{Cell, RefCell};
use rustc_serialize::hex::FromHex;
use core::ops::Deref;
use std::error::Error;

#[derive(Clone, Debug)]
pub struct FakeBluetoothDevice {
    object_path: String,
	adapter: RefCell<Option<FakeBluetoothAdapter>>,
	appearance: Cell<u16>,
	connectionError: Cell<u32>,
	deviceClass: Cell<u32>,
	isPaired: Cell<bool>,
	isConnected: Cell<bool>,
	isTrusted: Cell<bool>,
	isBlocked: Cell<bool>,
	isLegacyPairing: Cell<bool>,
	uuids: RefCell<Vec<String>>,
	modalias: RefCell<String>,
	name: RefCell<String>,
	productId: Cell<u32>,
	productVersion: Cell<u32>,
	rssi: Cell<i16>,
	txPower: Cell<i16>,
	vendorId: Cell<u32>,
	vendorIdSource: RefCell<String>,
}

impl FakeBluetoothDevice {
	pub fn new(object_path: String,
			   adapter: FakeBluetoothAdapter,
			   appearance: u16,
			   connectionError: u32,
			   deviceClass: u32,
			   isPaired: bool,
			   isConnected: bool,
			   isTrusted: bool,
			   isBlocked: bool,
			   isLegacyPairing: bool,
			   uuids: Vec<String>,
			   modalias: String,
			   name: String,
			   productId: u32,
			   productVersion: u32,
			   rssi: i16,
			   txPower: i16,
			   vendorId: u32,
			   vendorIdSource: String)
			   -> FakeBluetoothDevice {
		FakeBluetoothDevice{
		   	object_path: object_path,
			adapter: RefCell::new(Some(adapter)),
			appearance: Cell::new(appearance),
			connectionError: Cell::new(connectionError),
			deviceClass: Cell::new(deviceClass),
			isPaired: Cell::new(isPaired),
			isConnected: Cell::new(isConnected),
			isTrusted: Cell::new(isTrusted),
			isBlocked: Cell::new(isBlocked),
			isLegacyPairing: Cell::new(isLegacyPairing),
			uuids: RefCell::new(uuids),
			modalias: RefCell::new(modalias),
			name: RefCell::new(name),
			productId: Cell::new(productId),
			productVersion: Cell::new(productVersion),
			rssi: Cell::new(rssi),
			txPower: Cell::new(txPower),
			vendorId: Cell::new(vendorId),
			vendorIdSource: RefCell::new(vendorIdSource),
		}
	}

	pub fn new_empty(object_path: String) -> FakeBluetoothDevice {
		FakeBluetoothDevice{
		   	object_path: object_path,
			adapter: RefCell::new(None),
			appearance: Cell::new(0),
			connectionError: Cell::new(0),
			deviceClass: Cell::new(0),
			isPaired: Cell::new(false),
			isConnected: Cell::new(false),
			isTrusted: Cell::new(false),
			isBlocked: Cell::new(false),
			isLegacyPairing: Cell::new(false),
			uuids: RefCell::new(vec![]),
			modalias: RefCell::new(String::new()),
			name: RefCell::new(String::new()),
			productId: Cell::new(0),
			productVersion: Cell::new(0),
			rssi: Cell::new(0),
			txPower: Cell::new(0),
			vendorId: Cell::new(0),
			vendorIdSource: RefCell::new(String::new()),
		}
	}

    pub fn get_object_path(&self) -> String {
        self.object_path.clone()
    }

    pub fn get_adapter(&self) -> Result<FakeBluetoothAdapter, Box<Error>> {
        match self.adapter.borrow().clone() {
        	Some(adapter) => return Ok(adapter),
        	None => return Err(Box::from("Device has no adapter."))
        }
    }

    pub fn set_adapter(&mut self, adapter: FakeBluetoothAdapter) {
    	*self.adapter.borrow_mut() = Some(adapter);
    }

    pub fn get_appearance(&self) -> u16 {
        self.appearance.get()
    }

    pub fn set_appearance(&mut self, value: u16) {
        self.appearance.set(value);
    }

    pub fn get_connection_error(&self) -> u32 {
        self.connectionError.get()
    }

    pub fn set_connection_error(&mut self, value: u32) {
        self.connectionError.set(value);
    }

    pub fn get_device_class(&self) -> u32 {
        self.deviceClass.get()
    }

    pub fn set_device_class(&mut self, value: u32) {
        self.deviceClass.set(value);
    }

    pub fn is_paired(&self) -> bool {
        self.isPaired.get()
    }

    pub fn set_paired(&mut self, value: bool) {
    	self.isPaired.set(value);
    }

    pub fn is_connected(&self) -> bool {
        self.isConnected.get()
    }

    pub fn set_connected(&mut self, value: bool) {
    	self.isConnected.set(value);
    }

    pub fn is_trusted(&self) -> bool {
        self.isTrusted.get()
    }

    pub fn set_trusted(&mut self, value: bool) {
    	self.isTrusted.set(value);
    }

    pub fn is_blocked(&self) -> bool {
        self.isBlocked.get()
    }

    pub fn set_blocked(&mut self, value: bool) {
    	self.isBlocked.set(value);
    }

    pub fn is_legacy_pairing(&self) -> bool {
        self.isLegacyPairing.get()
    }

    pub fn set_legacy_pairing(&mut self, value: bool) {
    	self.isLegacyPairing.set(value);
    }

    pub fn get_uuids(&self) -> Vec<String> {
        self.uuids.borrow().clone()
    }

    pub fn set_uuids(&mut self, uuids: Vec<String>) {
    	for uuid in uuids {
    		self.uuids.borrow_mut().push(uuid);
    	}
    }

    pub fn get_name(&self) -> String {
        self.name.borrow().clone()
    }

    pub fn set_name(&mut self, name: String) {
    	*self.name.borrow_mut() = name;
    }

    pub fn get_product_id(&self) -> u32 {
        self.productId.get()
    }

    pub fn set_product_id(&mut self, value: u32) {
    	self.productId.set(value);
    }

    pub fn get_product_version(&self) -> u32 {
    	self.productVersion.get()
    }

    pub fn set_product_version(&mut self, value: u32) {
    	self.productVersion.set(value);
    }

    pub fn get_rssi(&self) -> i16 {
        self.rssi.get()
    }

    pub fn set_rssi(&mut self, value: i16) {
    	self.rssi.set(value);
    }

    pub fn get_tx_power(&self) -> i16 {
        self.txPower.get()
    }

    pub fn set_tx_power(&mut self, value: i16) {
    	self.txPower.set(value);
    }

    pub fn get_vendor_id(&self) -> u32 {
        self.vendorId.get()
    }

    pub fn set_vendor_id(&mut self, value: u32) {
    	self.vendorId.set(value);
    }

    pub fn get_vendor_id_source(&self) -> String {
        self.vendorIdSource.borrow().clone()
    }

    pub fn set_vendor_id_source(&mut self, value: String) {
    	*self.vendorIdSource.borrow_mut() = value;
    }
}
<commit_msg>Further Device implementation<commit_after>use fake_adapter::FakeBluetoothAdapter;
use std::cell::{Cell, RefCell};
use rustc_serialize::hex::FromHex;
use core::ops::Deref;
use std::error::Error;

#[derive(Clone, Debug)]
pub struct FakeBluetoothDevice {
    object_path: String,
    adapter: RefCell<Option<FakeBluetoothAdapter>>,
    address: RefCell<String>,
    appearance: Cell<u16>,
    //connectionError: Cell<u32>,
    //deviceClass: Cell<u32>,
    gattServices: RefCell<Vec<String>>,
    //isPaired: Cell<bool>,
    isConnectable: Cell<bool>,
    isConnected: Cell<bool>,
    //isTrusted: Cell<bool>,
    isBlocked: Cell<bool>,
    //isLegacyPairing: Cell<bool>,
    uuids: RefCell<Vec<String>>,
    name: RefCell<String>,
    //productId: Cell<u32>,
    //productVersion: Cell<u32>,
    rssi: Cell<i16>,
    txPower: Cell<i16>,
    //vendorId: Cell<u32>,
    //vendorIdSource: RefCell<String>,
}

impl FakeBluetoothDevice {
    pub fn new(object_path: String,
               adapter: FakeBluetoothAdapter,
               address: String,
               appearance: u16,
               //connectionError: u32,
               //deviceClass: u32,
               gattServices: Vec<String>,
               //isPaired: bool,
               isConnectable: bool,
               isConnected: bool,
               //isTrusted: bool,
               isBlocked: bool,
               //isLegacyPairing: bool,
               uuids: Vec<String>,
               name: String,
               //productId: u32,
               //productVersion: u32,
               rssi: i16,
               txPower: i16,
               //vendorId: u32,
               //vendorIdSource: String
               )
               -> FakeBluetoothDevice {
        FakeBluetoothDevice{
            object_path: object_path,
            adapter: RefCell::new(Some(adapter)),
            address: RefCell::new(address),
            appearance: Cell::new(appearance),
            //connectionError: Cell::new(connectionError),
            //deviceClass: Cell::new(deviceClass),
            gattServices: RefCell::new(gattServices),
            //isPaired: Cell::new(isPaired),
            isConnectable: Cell::new(isConnectable),
            isConnected: Cell::new(isConnected),
            //isTrusted: Cell::new(isTrusted),
            isBlocked: Cell::new(isBlocked),
            //isLegacyPairing: Cell::new(isLegacyPairing),
            uuids: RefCell::new(uuids),
            name: RefCell::new(name),
            //productId: Cell::new(productId),
            //productVersion: Cell::new(productVersion),
            rssi: Cell::new(rssi),
            txPower: Cell::new(txPower),
            //vendorId: Cell::new(vendorId),
            //vendorIdSource: RefCell::new(vendorIdSource),
        }
    }

    pub fn new_empty(object_path: String) -> FakeBluetoothDevice {
        FakeBluetoothDevice{
            object_path: object_path,
            adapter: RefCell::new(None),
            address: RefCell::new(String::new()),
            appearance: Cell::new(0),
            //connectionError: Cell::new(0),
            //deviceClass: Cell::new(0),
            gattServices: RefCell::new(vec![]),
            //isPaired: Cell::new(false),
            isConnectable: Cell::new(false),
            isConnected: Cell::new(false),
            //isTrusted: Cell::new(false),
            isBlocked: Cell::new(false),
            //isLegacyPairing: Cell::new(false),
            uuids: RefCell::new(vec![]),
            name: RefCell::new(String::new()),
            //productId: Cell::new(0),
            //productVersion: Cell::new(0),
            rssi: Cell::new(0),
            txPower: Cell::new(0),
            //vendorId: Cell::new(0),
            //vendorIdSource: RefCell::new(String::new()),
        }
    }

    pub fn get_object_path(&self) -> String {
        self.object_path.clone()
    }

    pub fn get_adapter(&self) -> Result<FakeBluetoothAdapter, Box<Error>> {
        match self.adapter.borrow().clone() {
            Some(adapter) => return Ok(adapter),
            None => return Err(Box::from("Device has no adapter."))
        }
    }

    pub fn set_adapter(&mut self, adapter: FakeBluetoothAdapter) {
        *self.adapter.borrow_mut() = Some(adapter);
    }

    pub fn get_address(&self) -> String {
        self.address.borrow().clone()
    }

    pub fn set_address(&mut self, address: String) {
        *self.address.borrow_mut() = address;
    }

    pub fn get_appearance(&self) -> u16 {
        self.appearance.get()
    }

    pub fn set_appearance(&mut self, value: u16) {
        self.appearance.set(value);
    }

    /*pub fn get_connection_error(&self) -> u32 {
        self.connectionError.get()
    }

    pub fn set_connection_error(&mut self, value: u32) {
        self.connectionError.set(value);
    }*/

    /*pub fn get_device_class(&self) -> u32 {
        self.deviceClass.get()
    }

    pub fn set_device_class(&mut self, value: u32) {
        self.deviceClass.set(value);
    }*/

    pub fn get_gatt_services(&self) -> Vec<String> {
        self.gattServices.borrow().clone()
    }

    pub fn push_gatt_service(&mut self, services: Vec<String>) {
        for service in services {
            self.gattServices.borrow_mut().push(service);
        }
    }

    /*pub fn is_paired(&self) -> bool {
        self.isPaired.get()
    }

    pub fn set_paired(&mut self, value: bool) {
        self.isPaired.set(value);
    }*/

    pub fn is_connected(&self) -> bool {
        self.isConnected.get()
    }

    pub fn set_connected(&mut self, value: bool) {
        self.isConnected.set(value);
    }

    /*pub fn is_trusted(&self) -> bool {
        self.isTrusted.get()
    }

    pub fn set_trusted(&mut self, value: bool) {
        self.isTrusted.set(value);
    }*/

    pub fn is_blocked(&self) -> bool {
        self.isBlocked.get()
    }

    pub fn set_blocked(&mut self, value: bool) {
        self.isBlocked.set(value);
    }

    /*pub fn is_legacy_pairing(&self) -> bool {
        self.isLegacyPairing.get()
    }

    pub fn set_legacy_pairing(&mut self, value: bool) {
        self.isLegacyPairing.set(value);
    }*/

    pub fn get_uuids(&self) -> Vec<String> {
        self.uuids.borrow().clone()
    }

    pub fn set_uuids(&mut self, uuids: Vec<String>) {
        for uuid in uuids {
            self.uuids.borrow_mut().push(uuid);
        }
    }

    pub fn get_name(&self) -> String {
        self.name.borrow().clone()
    }

    pub fn set_name(&mut self, name: String) {
        *self.name.borrow_mut() = name;
    }

    /*pub fn get_product_id(&self) -> u32 {
        self.productId.get()
    }

    pub fn set_product_id(&mut self, value: u32) {
        self.productId.set(value);
    }

    pub fn get_product_version(&self) -> u32 {
        self.productVersion.get()
    }

    pub fn set_product_version(&mut self, value: u32) {
        self.productVersion.set(value);
    }*/

    pub fn get_rssi(&self) -> i16 {
        self.rssi.get()
    }

    pub fn set_rssi(&mut self, value: i16) {
        self.rssi.set(value);
    }

    pub fn get_tx_power(&self) -> i16 {
        self.txPower.get()
    }

    pub fn set_tx_power(&mut self, value: i16) {
        self.txPower.set(value);
    }

    /*pub fn get_vendor_id(&self) -> u32 {
        self.vendorId.get()
    }

    pub fn set_vendor_id(&mut self, value: u32) {
        self.vendorId.set(value);
    }

    pub fn get_vendor_id_source(&self) -> String {
        self.vendorIdSource.borrow().clone()
    }

    pub fn set_vendor_id_source(&mut self, value: String) {
        *self.vendorIdSource.borrow_mut() = value;
    }*/

    pub fn connect(&mut self) -> Result<(), Box<Error>> {
        // TODO: result-al térjen vissza, ha
        if self.isConnectable.get() && !self.isConnected.get() {
            self.isConnected.set(true);
            return Ok(());
        } else {
            return Err(Box::from("Could not connect to the device."));
        }
    }

    pub fn disconnect(&mut self) -> Result<(), Box<Error>>{
        if self.isConnected.get() {
            self.isConnected.set(false);
            return Ok(());
        } else {
            return Err(Box::from("The device is not connected."));
        }
    }
}
<|endoftext|>


<commit_before>use character::controls::CharacterControl;
use game::constants::{RESOLUTION_X, RESOLUTION_Y};
use gfx;
use gfx_app::controls::{Control, TilemapControls};
use gfx_device_gl;
use gfx_window_glutin;
use glutin;
use glutin::{GlContext, KeyboardInput, MouseButton};
use glutin::ElementState::{Pressed, Released};
use glutin::VirtualKeyCode::{Escape, Z, X, W, A, S, D, R};

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct WindowContext {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl WindowContext {
  pub fn new() -> WindowContext {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Hinterland");

    let builder = if cfg!(feature = "windowed") {
      window_title
        .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_min_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_max_dimensions(RESOLUTION_X, RESOLUTION_Y)
    } else {
      let monitor = {
        events_loop.get_available_monitors().nth(0).expect("No monitor found")
      };
      let monitor_resolution = monitor.get_dimensions();

      let resolution = ((monitor_resolution.1 as f32 * 16.0 / 9.0) as u32, monitor_resolution.1);
      window_title.with_fullscreen(Some(monitor))
                  .with_dimensions(resolution.0, resolution.1)
                  .with_min_dimensions(resolution.0, resolution.1)
                  .with_max_dimensions(resolution.0, resolution.1)
    };

    let context = glutin::ContextBuilder::new()
      .with_vsync(true)
      .with_pixel_format(24, 8)
      .with_gl(glutin::GlRequest::GlThenGles {
        opengles_version: (3, 0),
        opengl_version: (4, 1),
      });

    let (window, device, factory, rtv, dsv) =
      gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop);

    WindowContext {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0),
    }
  }
}

#[derive(Debug, PartialEq, Eq)]
pub enum WindowStatus {
  Open,
  Close,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);
  fn get_viewport_size(&mut self) -> (f32, f32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_hidpi_factor(&mut self) -> f32;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
  fn poll_events(&mut self) -> WindowStatus;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for WindowContext {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
        .swap_buffers()
        .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (f32, f32) {
    if cfg!(feature = "windowed") {
      (RESOLUTION_X as f32, RESOLUTION_Y as f32)
    } else {
      let monitor = self.events_loop.get_available_monitors().nth(0).expect("No monitor found");
      let monitor_resolution = monitor.get_dimensions();
      ((monitor_resolution.1 as f32 * 16.0 / 9.0) as f32, monitor_resolution.1 as f32)
    }
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_hidpi_factor(&mut self) -> f32 {
    self.window.hidpi_factor() as f32
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }

  fn poll_events(&mut self) -> WindowStatus {
    use glutin::WindowEvent::{CursorMoved, CloseRequested, MouseInput};

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };

    let m_pos = &mut self.mouse_pos;

    let mut game_status = WindowStatus::Open;

    self.events_loop.poll_events(|event| {
      game_status = if let glutin::Event::WindowEvent { event, .. } = event {
        match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => {process_keyboard_input(input, controls)},
          MouseInput { state: Pressed, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(None);
            WindowStatus::Open
          }
          MouseInput { state: Pressed, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(None);
            WindowStatus::Open
          }
          CursorMoved { position, .. } => {
            *m_pos = position;
            WindowStatus::Open
          }
          CloseRequested => WindowStatus::Close,
          _ => WindowStatus::Open,
        }
      } else {
        WindowStatus::Open
      };
    });
    game_status
  }
}

fn process_keyboard_input(input: glutin::KeyboardInput, controls: &mut TilemapControls) -> WindowStatus {
  match input {
    KeyboardInput { virtual_keycode: Some(Escape), .. } => WindowStatus::Close,
    KeyboardInput { state: Pressed, virtual_keycode: Some(Z), .. } => {
      controls.zoom(&Control::Negative);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(X), .. } => {
      controls.zoom(&Control::Plus);
      WindowStatus::Open
    }
    KeyboardInput { state: Released, virtual_keycode: Some(Z), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(X), .. } => {
      controls.zoom(&Control::Released);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(W), .. } => {
      controls.move_character(CharacterControl::Up);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(S), .. } => {
      controls.move_character(CharacterControl::Down);
      WindowStatus::Open
    }
    KeyboardInput { state: Released, virtual_keycode: Some(W), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(S), .. } => {
      controls.move_character(CharacterControl::YMoveStop);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(A), .. } => {
      controls.move_character(CharacterControl::Left);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(D), .. } => {
      controls.move_character(CharacterControl::Right);
      WindowStatus::Open
    }
    KeyboardInput { state: Released, virtual_keycode: Some(A), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(D), .. } => {
      controls.move_character(CharacterControl::XMoveStop);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(R), .. } => {
      controls.reload_weapon(true);
      WindowStatus::Open
    },
    KeyboardInput { state: Released, virtual_keycode: Some(R), .. } => {
      controls.reload_weapon(false);
      WindowStatus::Open
    },
    KeyboardInput { state: Pressed, modifiers, .. } => {
      if modifiers.ctrl {
        controls.ctrl_pressed(true);
      }
      WindowStatus::Open
    }
    KeyboardInput { state: Released, modifiers, .. } => {
      if !modifiers.ctrl {
        controls.ctrl_pressed(false);
      }
      WindowStatus::Open
    }
  }
}

<commit_msg>Update preferred opengl version<commit_after>use character::controls::CharacterControl;
use game::constants::{RESOLUTION_X, RESOLUTION_Y};
use gfx;
use gfx_app::controls::{Control, TilemapControls};
use gfx_device_gl;
use gfx_window_glutin;
use glutin;
use glutin::{GlContext, KeyboardInput, MouseButton};
use glutin::ElementState::{Pressed, Released};
use glutin::VirtualKeyCode::{Escape, Z, X, W, A, S, D, R};

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct WindowContext {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl WindowContext {
  pub fn new() -> WindowContext {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Hinterland");

    let builder = if cfg!(feature = "windowed") {
      window_title
        .with_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_min_dimensions(RESOLUTION_X, RESOLUTION_Y)
        .with_max_dimensions(RESOLUTION_X, RESOLUTION_Y)
    } else {
      let monitor = {
        events_loop.get_available_monitors().nth(0).expect("No monitor found")
      };
      let monitor_resolution = monitor.get_dimensions();

      let resolution = ((monitor_resolution.1 as f32 * 16.0 / 9.0) as u32, monitor_resolution.1);
      window_title.with_fullscreen(Some(monitor))
                  .with_dimensions(resolution.0, resolution.1)
                  .with_min_dimensions(resolution.0, resolution.1)
                  .with_max_dimensions(resolution.0, resolution.1)
    };

    let context = glutin::ContextBuilder::new()
      .with_vsync(true)
      .with_pixel_format(24, 8)
      .with_gl(glutin::GlRequest::GlThenGles {
        opengles_version: (3, 0),
        opengl_version: (3, 3),
      });

    let (window, device, factory, rtv, dsv) =
      gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop);

    WindowContext {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0),
    }
  }
}

#[derive(Debug, PartialEq, Eq)]
pub enum WindowStatus {
  Open,
  Close,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);
  fn get_viewport_size(&mut self) -> (f32, f32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_hidpi_factor(&mut self) -> f32;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
  fn poll_events(&mut self) -> WindowStatus;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for WindowContext {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
        .swap_buffers()
        .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (f32, f32) {
    if cfg!(feature = "windowed") {
      (RESOLUTION_X as f32, RESOLUTION_Y as f32)
    } else {
      let monitor = self.events_loop.get_available_monitors().nth(0).expect("No monitor found");
      let monitor_resolution = monitor.get_dimensions();
      ((monitor_resolution.1 as f32 * 16.0 / 9.0) as f32, monitor_resolution.1 as f32)
    }
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_hidpi_factor(&mut self) -> f32 {
    self.window.hidpi_factor() as f32
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }

  fn poll_events(&mut self) -> WindowStatus {
    use glutin::WindowEvent::{CursorMoved, CloseRequested, MouseInput};

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };

    let m_pos = &mut self.mouse_pos;

    let mut game_status = WindowStatus::Open;

    self.events_loop.poll_events(|event| {
      game_status = if let glutin::Event::WindowEvent { event, .. } = event {
        match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => {process_keyboard_input(input, controls)},
          MouseInput { state: Pressed, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(None);
            WindowStatus::Open
          }
          MouseInput { state: Pressed, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(None);
            WindowStatus::Open
          }
          CursorMoved { position, .. } => {
            *m_pos = position;
            WindowStatus::Open
          }
          CloseRequested => WindowStatus::Close,
          _ => WindowStatus::Open,
        }
      } else {
        WindowStatus::Open
      };
    });
    game_status
  }
}

fn process_keyboard_input(input: glutin::KeyboardInput, controls: &mut TilemapControls) -> WindowStatus {
  match input {
    KeyboardInput { virtual_keycode: Some(Escape), .. } => WindowStatus::Close,
    KeyboardInput { state: Pressed, virtual_keycode: Some(Z), .. } => {
      controls.zoom(&Control::Negative);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(X), .. } => {
      controls.zoom(&Control::Plus);
      WindowStatus::Open
    }
    KeyboardInput { state: Released, virtual_keycode: Some(Z), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(X), .. } => {
      controls.zoom(&Control::Released);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(W), .. } => {
      controls.move_character(CharacterControl::Up);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(S), .. } => {
      controls.move_character(CharacterControl::Down);
      WindowStatus::Open
    }
    KeyboardInput { state: Released, virtual_keycode: Some(W), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(S), .. } => {
      controls.move_character(CharacterControl::YMoveStop);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(A), .. } => {
      controls.move_character(CharacterControl::Left);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(D), .. } => {
      controls.move_character(CharacterControl::Right);
      WindowStatus::Open
    }
    KeyboardInput { state: Released, virtual_keycode: Some(A), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(D), .. } => {
      controls.move_character(CharacterControl::XMoveStop);
      WindowStatus::Open
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(R), .. } => {
      controls.reload_weapon(true);
      WindowStatus::Open
    },
    KeyboardInput { state: Released, virtual_keycode: Some(R), .. } => {
      controls.reload_weapon(false);
      WindowStatus::Open
    },
    KeyboardInput { state: Pressed, modifiers, .. } => {
      if modifiers.ctrl {
        controls.ctrl_pressed(true);
      }
      WindowStatus::Open
    }
    KeyboardInput { state: Released, modifiers, .. } => {
      if !modifiers.ctrl {
        controls.ctrl_pressed(false);
      }
      WindowStatus::Open
    }
  }
}

<|endoftext|>


<commit_before>//! Implementation of Xen's network driver

use core::mem;
use alloc_uni::__rust_allocate;

use sync::{Arc, Weak};
use boxed::Box;

use vec::Vec;
use string::ToString;

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

use ffi::CString;

use net::{Interface, Packet};
use net::defs::{Device, HwAddr};

use hal::mmu::{Vaddr, Mfn};

use hal::xen::store::{XenStore, XenbusState};
use hal::xen::grant::{Table as GrantTable, Ref as GrantRef};
use hal::xen::ring::{SharedRing, FrontRing, Idx as RingIdx};

use hal::xen::defs::EvtchnPort;

use hal::arch::defs::PAGE_SIZE;

use hal::xen::event;

#[repr(i16)]
#[derive(Debug, PartialEq)]
#[allow(dead_code)]
/// XEN_NETIF_RSP_*
enum NetifRsp {
    /// XEN_NETIF_RSP_OKAY
    Okay = 0,
    /// XEN_NETIF_RSP_ERROR
    Error = -1,
    /// XEN_NETIF_RSP_DROPPED
    Dropped = -2,
    /// XEN_NETIF_RSP_NULL
    Null = 1,
}

#[repr(u16)]
#[derive(Debug)]
#[allow(dead_code)]
/// XEN_NETTXF_*
enum NetTxFlags {
    CsumBlank = 1 << 0,
    DataValidated = 1 << 1,
    MoreData = 1 << 2,
    ExtraInfo = 1 << 3,
}

#[repr(u16)]
#[derive(Debug)]
#[allow(dead_code)]
/// XEN_NETRXF_*
enum NetRxFlags {
    DataValidated = 1 << 0,
    CsumBlank = 1 << 1,
    MoreData = 1 << 2,
    ExtraInfo = 1 << 3,
    GsoPrefix = 1 << 4,
}

#[repr(C)]
#[derive(Debug)]
/// struct xen_netif_tx_request
struct NetifTxRequest {
    gref: GrantRef,
    offset: u16,
    flags: NetTxFlags,
    id: u16,
    size: u16,
}

#[repr(C)]
#[derive(Debug)]
/// struct xen_netif_tx_response
struct NetifTxResponse {
    id: u16,
    status: NetifRsp,
}

#[repr(C)]
#[derive(Debug)]
/// struct xen_netif_rx_request
struct NetifRxRequest {
    id: u16,
    gref: GrantRef,
}

#[repr(C)]
#[derive(Debug)]
/// struct xen_netif_rx_response
struct NetifRxResponse {
    id: u16,
    offset: u16,
    flags: NetRxFlags,
    status: i16,
}

type TxSharedRing = SharedRing<NetifTxRequest, NetifTxResponse>;
type RxSharedRing = SharedRing<NetifRxRequest, NetifRxResponse>;

type TxFrontRing = FrontRing<NetifTxRequest, NetifTxResponse>;
type RxFrontRing = FrontRing<NetifRxRequest, NetifRxResponse>;

/// Verify if a vif (virtual interface) with id `id` exists
fn vif_id_exists(id: u32) -> bool {
    let mut t = XenStore::start_transaction().unwrap();
    let path = CString::new(format!("device/vif/{}", id)).unwrap();

    let res = match t.read(path) {
        Ok(..) => true,
        Err(..) => false,
    };

    t.end().unwrap();

    res
}

/// Returns a list of interfaces that have a xen backend
pub fn discover() -> Vec<Arc<RwLock<Interface>>> {
    let mut id = 0;
    let mut v = Vec::new();

    // Create interface for every `id` valid
    while vif_id_exists(id) {
        let interface = Arc::new(RwLock::new(Interface::new()));
        let interface_weak = Arc::downgrade(&interface);

        v.push(interface);

        // Instantiate the Xen backend
        match XenNetDevice::new(id, interface_weak) {
            Ok(i) => {
                // Set it as pv_device of the interface
                v.last().unwrap().write().pv_device_set(i);
            }
            Err(..) => {
                v.pop();
                println!("Warning: Impossible to initialize xen network interface {}",
                         id);
            }
        }

        id += 1;
    }

    v
}

/// Buffer allocated for packet reception
struct RxBuffer {
    pub id: u16,
    pub page: *mut u8,
    pub grant_ref: GrantRef,
}

/// Buffer allocated for packet transmission
struct TxBuffer {
    pub id: u16,
    pub pkt: Option<Packet>,
    pub grant_ref: GrantRef,
}

/// A Xen vif (virtual interface)
pub struct XenNetDevice {
    evtchn: EvtchnPort,
    backend_id: u16,
    tx_ring: TxFrontRing,
    rx_ring: RxFrontRing,
    tx_buffer: SpinLock<Vec<TxBuffer>>,
    rx_buffer: InterruptSpinLock<Vec<RxBuffer>>,
    intf: Weak<RwLock<Interface>>,
}

impl Device for XenNetDevice {
    fn refresh(&mut self) {
        unimplemented!();
    }

    fn tx_packet(&mut self, pkt: Packet) {
        unimplemented!();
    }
}

impl XenNetDevice {
    fn device_callback(_: EvtchnPort, data: *mut u8) {
        let xen_dev = unsafe { &mut (*(data as *mut XenNetDevice)) };

        xen_dev.rx_packet();
    }

    /// Creates a new Xen network device with id `id`.
    ///
    /// This interface will be the backend of the interface `intf`
    pub fn new(id: u32, intf: Weak<RwLock<Interface>>) -> Result<Box<Self>, ()> {
        // Compute the root path that contains all the information for the
        // network device with id "id"
        let vif_root = format!("device/vif/{}", id);

        // Get the backend id. This is basically the domain id of the backend
        // It will be useful for event allocation and page grant
        let backend_path = try!(CString::new(format!("{}/backend-id", vif_root)));
        let backend_id = try!(XenStore::read_value::<u16>(backend_path));

        // Create RX and TX shared ring
        let mut tx_sring = try!(TxSharedRing::new(PAGE_SIZE).ok_or(()));
        let mut rx_sring = try!(RxSharedRing::new(PAGE_SIZE).ok_or(()));

        // Grant access to the rings to the backend
        let tx_ref = try!(tx_sring.grant_access(backend_id).ok_or(()));
        let rx_ref = try!(rx_sring.grant_access(backend_id).ok_or(()));

        let mut xen_dev = Box::new(XenNetDevice {
            evtchn: 0,
            backend_id: backend_id,
            tx_ring: TxFrontRing::new(tx_sring),
            rx_ring: RxFrontRing::new(rx_sring),
            tx_buffer: SpinLock::new(Vec::new()),
            rx_buffer: InterruptSpinLock::new(Vec::new()),
            intf: intf,
        });

        // This is legit as the event will be gone before the XenNetDevice
        let xen_dev_ptr = Box::into_raw(xen_dev);

        unsafe {
            xen_dev = Box::from_raw(xen_dev_ptr);
        }

        // Create a new event to receive interruptions about this device
        let evtchn = {
            try!(event::dispatcher().alloc_unbound(
                    backend_id, Self::device_callback,
                    xen_dev_ptr as *mut _).map_err(|_| ()
                 ))
        };

        xen_dev.evtchn = evtchn;

        // Start a transaction with the Xen store to finalize the device's
        // configuration
        let mut t = try!(XenStore::start_transaction());

        // Get the mac address as a String from the Xen Store
        let mac = try!(t.read(try!(CString::new(format!("{}/mac", vif_root)))));

        // Convert it to HwAddr
        let hw_addr = try!(HwAddr::from_str(mac));

        // Set tx and rx grant references
        try!(t.write(try!(CString::new(format!("{}/tx-ring-ref", vif_root))),
                     try!(CString::new(tx_ref.to_string()))));
        try!(t.write(try!(CString::new(format!("{}/rx-ring-ref", vif_root))),
                     try!(CString::new(rx_ref.to_string()))));

        // Set the event channel
        try!(t.write(try!(CString::new(format!("{}/event-channel", vif_root))),
                     try!(CString::new(evtchn.to_string()))));

        // Request packet transfer via flipping rather than copy. When using
        // flipping Xen backend driver will use paging to give us the packet
        // rather than copying the packet in the granted page
        try!(t.write(try!(CString::new(format!("{}/request-rx-copy", vif_root))),
                     CString::new("0").unwrap()));

        // Set the device as connected
        let state_path = try!(CString::new(format!("{}/state", vif_root)));

        try!(t.switch_state(state_path.clone(), XenbusState::Connected));

        try!(t.end());

        // Wait for the device to be connected
        // XXX: Add xen store watch implementation to avoid busy wait
        loop {
            let state: XenbusState = unsafe {
                mem::transmute(try!(XenStore::read_value::<u8>(state_path.clone())))
            };

            if state == XenbusState::Connected {
                break;
            } else if state < XenbusState::Connected {
                ::hal::xen::sched::yield_cpu();
                continue;
            } else {
                return Err(());
            }
        }

        // Init buffers
        try!(xen_dev.init_rx());
        try!(xen_dev.init_tx());

        // Unmask the event
        event::dispatcher().unmask_event(evtchn);

        let parent = xen_dev.intf.upgrade().unwrap();

        // Set interface info
        *parent.write().name_mut() = format!("xen{}", id);
        *parent.write().hw_addr_mut() = hw_addr;

        Ok(xen_dev)
    }

    fn init_rx(&mut self) -> Result<(), ()> {
        let mut v: Vec<RxBuffer> = Vec::with_capacity(self.rx_ring.size());

        // We populate the rx ring with RxRequest. These requests give to the
        // backend some pages to work with in order to send us some packets
        //
        // The way TX works with Xen is as follow:
        //
        // 1. We put RxRequests in the ring. These requests contain an
        // allocated and granted page for the backend to work with
        //
        // 2. The backend receives a packet for us
        //
        // 3. The backend pops a RxRequest and use the granted page to transfer
        // use the package
        //
        // 4. The backend pushes a RxResponse in the ring
        //
        // 5. We process this RxResponse, ungrant the page and enqueue the
        // packet in the Rx queue to be treated by the network stack
        //
        // This is why we need to keep trap of the granted page
        for i in 0..self.rx_ring.size() {
            let page = __rust_allocate(PAGE_SIZE, PAGE_SIZE);

            if page.is_null() {
                return Err(());
            }

            // Grant access to the page to the backend
            let grant = try!(GrantTable::alloc_ref().ok_or(()));

            grant.grant_access(self.backend_id,
                               Mfn::from(Vaddr::from_ptr(page)), false);

            // Tell the backend about the new buffer via a RxRequest
            let req = unsafe { self.rx_ring.sring_mut().request_from_index(i) };

            req.id = i as u16;
            req.gref = grant.clone();

            // Keep track of buffers internally
            v.push(RxBuffer {
                id: i as u16,
                page: page,
                grant_ref: grant,
            });
        }

        // Update ring's request production index
        unsafe {
            *self.rx_ring.req_prod_mut() += self.rx_ring.size() as RingIdx;
        }

        // Notify the backend
        if self.rx_ring.push_requests() {
            event::send(self.evtchn);
        }

        unsafe {
            // Set notification index so that the backend notifies us when it
            // pushes new responses in the ring
            let rsp_event = self.rx_ring.rsp_cons() + 1;

            self.rx_ring.sring_mut().rsp_event_set(rsp_event);
        }

        self.rx_buffer = InterruptSpinLock::new(v);

        Ok(())
    }

    fn init_tx(&mut self) -> Result<(), ()> {
        let mut v: Vec<TxBuffer> = Vec::with_capacity(self.tx_ring.size());

        // We internally initialize empty buffers.
        // The way TX works with Xen is as follow:
        //
        // 1. We sent a TxRequest with a page that contains the packet to
        // transmit. The backend has granted access to the page.
        //
        // 2. The backend will transmit our packet and push in the ring a
        // TxResponse
        //
        // 3. refresh_tx() is called
        //
        // 4. refresh_tx() will consume TxResponse(s), ungrant access to the
        // page and free the packet.
        //
        // This is why we need to keep track of the grant reference and the
        // packet so that refresh_tx() can do its job.
        for i in 0..self.tx_ring.size() {
            let grant = try!(GrantTable::alloc_ref().ok_or(()));

            let buffer = TxBuffer {
                id: i as u16,
                pkt: None,
                grant_ref: grant,
            };

            v.push(buffer);
        }

        self.tx_buffer = SpinLock::new(v);

        Ok(())
    }

    // Check for received packet. If there are packets enqueue them in
    // the network stack's rx queue to be processed.
    fn rx_packet(&mut self) {
    }
}
<commit_msg>xen/net: Add implementation of XenNetDevice::rx_packet()<commit_after>//! Implementation of Xen's network driver

use core::{mem, ptr};

use alloc_uni::{__rust_allocate, __rust_deallocate};

use sync::{Arc, Weak};
use boxed::Box;

use vec::Vec;
use string::ToString;

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

use ffi::CString;

use net::{Interface, Packet, Stack as NetStack};
use net::defs::{Device, HwAddr};

use hal::mmu::{Vaddr, Mfn};

use hal::xen::store::{XenStore, XenbusState};
use hal::xen::grant::{Table as GrantTable, Ref as GrantRef};
use hal::xen::ring::{SharedRing, FrontRing, Idx as RingIdx};

use hal::xen::defs::EvtchnPort;

use hal::arch::defs::PAGE_SIZE;
use hal::arch::utils::rmb;

use hal::xen::event;

#[repr(i16)]
#[derive(Debug, PartialEq)]
#[allow(dead_code)]
/// XEN_NETIF_RSP_*
enum NetifRsp {
    /// XEN_NETIF_RSP_OKAY
    Okay = 0,
    /// XEN_NETIF_RSP_ERROR
    Error = -1,
    /// XEN_NETIF_RSP_DROPPED
    Dropped = -2,
    /// XEN_NETIF_RSP_NULL
    Null = 1,
}

#[repr(u16)]
#[derive(Debug)]
#[allow(dead_code)]
/// XEN_NETTXF_*
enum NetTxFlags {
    CsumBlank = 1 << 0,
    DataValidated = 1 << 1,
    MoreData = 1 << 2,
    ExtraInfo = 1 << 3,
}

#[repr(u16)]
#[derive(Debug)]
#[allow(dead_code)]
/// XEN_NETRXF_*
enum NetRxFlags {
    DataValidated = 1 << 0,
    CsumBlank = 1 << 1,
    MoreData = 1 << 2,
    ExtraInfo = 1 << 3,
    GsoPrefix = 1 << 4,
}

#[repr(C)]
#[derive(Debug)]
/// struct xen_netif_tx_request
struct NetifTxRequest {
    gref: GrantRef,
    offset: u16,
    flags: NetTxFlags,
    id: u16,
    size: u16,
}

#[repr(C)]
#[derive(Debug)]
/// struct xen_netif_tx_response
struct NetifTxResponse {
    id: u16,
    status: NetifRsp,
}

#[repr(C)]
#[derive(Debug)]
/// struct xen_netif_rx_request
struct NetifRxRequest {
    id: u16,
    gref: GrantRef,
}

#[repr(C)]
#[derive(Debug)]
/// struct xen_netif_rx_response
struct NetifRxResponse {
    id: u16,
    offset: u16,
    flags: NetRxFlags,
    status: i16,
}

type TxSharedRing = SharedRing<NetifTxRequest, NetifTxResponse>;
type RxSharedRing = SharedRing<NetifRxRequest, NetifRxResponse>;

type TxFrontRing = FrontRing<NetifTxRequest, NetifTxResponse>;
type RxFrontRing = FrontRing<NetifRxRequest, NetifRxResponse>;

/// Verify if a vif (virtual interface) with id `id` exists
fn vif_id_exists(id: u32) -> bool {
    let mut t = XenStore::start_transaction().unwrap();
    let path = CString::new(format!("device/vif/{}", id)).unwrap();

    let res = match t.read(path) {
        Ok(..) => true,
        Err(..) => false,
    };

    t.end().unwrap();

    res
}

/// Returns a list of interfaces that have a xen backend
pub fn discover() -> Vec<Arc<RwLock<Interface>>> {
    let mut id = 0;
    let mut v = Vec::new();

    // Create interface for every `id` valid
    while vif_id_exists(id) {
        let interface = Arc::new(RwLock::new(Interface::new()));
        let interface_weak = Arc::downgrade(&interface);

        v.push(interface);

        // Instantiate the Xen backend
        match XenNetDevice::new(id, interface_weak) {
            Ok(i) => {
                // Set it as pv_device of the interface
                v.last().unwrap().write().pv_device_set(i);
            }
            Err(..) => {
                v.pop();
                println!("Warning: Impossible to initialize xen network interface {}",
                         id);
            }
        }

        id += 1;
    }

    v
}

/// Buffer allocated for packet reception
struct RxBuffer {
    pub id: u16,
    pub page: *mut u8,
    pub grant_ref: GrantRef,
}

/// Buffer allocated for packet transmission
struct TxBuffer {
    pub id: u16,
    pub pkt: Option<Packet>,
    pub grant_ref: GrantRef,
}

/// A Xen vif (virtual interface)
pub struct XenNetDevice {
    evtchn: EvtchnPort,
    backend_id: u16,
    tx_ring: TxFrontRing,
    rx_ring: RxFrontRing,
    tx_buffer: SpinLock<Vec<TxBuffer>>,
    rx_buffer: InterruptSpinLock<Vec<RxBuffer>>,
    intf: Weak<RwLock<Interface>>,
}

impl Device for XenNetDevice {
    fn refresh(&mut self) {
        unimplemented!();
    }

    fn tx_packet(&mut self, pkt: Packet) {
        unimplemented!();
    }
}

impl XenNetDevice {
    /// Callback handling Xen network events
    fn device_callback(_: EvtchnPort, data: *mut u8) {
        let xen_dev = unsafe { &mut (*(data as *mut XenNetDevice)) };

        xen_dev.rx_packet();
    }

    /// Creates a new Xen network device with id `id`.
    ///
    /// This interface will be the backend of the interface `intf`
    pub fn new(id: u32, intf: Weak<RwLock<Interface>>) -> Result<Box<Self>, ()> {
        // Compute the root path that contains all the information for the
        // network device with id "id"
        let vif_root = format!("device/vif/{}", id);

        // Get the backend id. This is basically the domain id of the backend
        // It will be useful for event allocation and page grant
        let backend_path = try!(CString::new(format!("{}/backend-id", vif_root)));
        let backend_id = try!(XenStore::read_value::<u16>(backend_path));

        // Create RX and TX shared ring
        let mut tx_sring = try!(TxSharedRing::new(PAGE_SIZE).ok_or(()));
        let mut rx_sring = try!(RxSharedRing::new(PAGE_SIZE).ok_or(()));

        // Grant access to the rings to the backend
        let tx_ref = try!(tx_sring.grant_access(backend_id).ok_or(()));
        let rx_ref = try!(rx_sring.grant_access(backend_id).ok_or(()));

        let mut xen_dev = Box::new(XenNetDevice {
            evtchn: 0,
            backend_id: backend_id,
            tx_ring: TxFrontRing::new(tx_sring),
            rx_ring: RxFrontRing::new(rx_sring),
            tx_buffer: SpinLock::new(Vec::new()),
            rx_buffer: InterruptSpinLock::new(Vec::new()),
            intf: intf,
        });

        // This is legit as the event will be gone before the XenNetDevice
        let xen_dev_ptr = Box::into_raw(xen_dev);

        unsafe {
            xen_dev = Box::from_raw(xen_dev_ptr);
        }

        // Create a new event to receive interruptions about this device
        let evtchn = {
            try!(event::dispatcher().alloc_unbound(
                    backend_id, Self::device_callback,
                    xen_dev_ptr as *mut _).map_err(|_| ()
                 ))
        };

        xen_dev.evtchn = evtchn;

        // Start a transaction with the Xen store to finalize the device's
        // configuration
        let mut t = try!(XenStore::start_transaction());

        // Get the mac address as a String from the Xen Store
        let mac = try!(t.read(try!(CString::new(format!("{}/mac", vif_root)))));

        // Convert it to HwAddr
        let hw_addr = try!(HwAddr::from_str(mac));

        // Set tx and rx grant references
        try!(t.write(try!(CString::new(format!("{}/tx-ring-ref", vif_root))),
                     try!(CString::new(tx_ref.to_string()))));
        try!(t.write(try!(CString::new(format!("{}/rx-ring-ref", vif_root))),
                     try!(CString::new(rx_ref.to_string()))));

        // Set the event channel
        try!(t.write(try!(CString::new(format!("{}/event-channel", vif_root))),
                     try!(CString::new(evtchn.to_string()))));

        // Request packet transfer via flipping rather than copy. When using
        // flipping Xen backend driver will use paging to give us the packet
        // rather than copying the packet in the granted page
        try!(t.write(try!(CString::new(format!("{}/request-rx-copy", vif_root))),
                     CString::new("0").unwrap()));

        // Set the device as connected
        let state_path = try!(CString::new(format!("{}/state", vif_root)));

        try!(t.switch_state(state_path.clone(), XenbusState::Connected));

        try!(t.end());

        // Wait for the device to be connected
        // XXX: Add xen store watch implementation to avoid busy wait
        loop {
            let state: XenbusState = unsafe {
                mem::transmute(try!(XenStore::read_value::<u8>(state_path.clone())))
            };

            if state == XenbusState::Connected {
                break;
            } else if state < XenbusState::Connected {
                ::hal::xen::sched::yield_cpu();
                continue;
            } else {
                return Err(());
            }
        }

        // Init buffers
        try!(xen_dev.init_rx());
        try!(xen_dev.init_tx());

        // Unmask the event
        event::dispatcher().unmask_event(evtchn);

        let parent = xen_dev.intf.upgrade().unwrap();

        // Set interface info
        *parent.write().name_mut() = format!("xen{}", id);
        *parent.write().hw_addr_mut() = hw_addr;

        Ok(xen_dev)
    }

    fn init_rx(&mut self) -> Result<(), ()> {
        let mut v: Vec<RxBuffer> = Vec::with_capacity(self.rx_ring.size());

        // We populate the rx ring with RxRequest. These requests give to the
        // backend some pages to work with in order to send us some packets
        //
        // The way TX works with Xen is as follow:
        //
        // 1. We put RxRequests in the ring. These requests contain an
        // allocated and granted page for the backend to work with
        //
        // 2. The backend receives a packet for us
        //
        // 3. The backend pops a RxRequest and use the granted page to transfer
        // use the package
        //
        // 4. The backend pushes a RxResponse in the ring
        //
        // 5. We process this RxResponse, ungrant the page and enqueue the
        // packet in the Rx queue to be treated by the network stack
        //
        // This is why we need to keep trap of the granted page
        for i in 0..self.rx_ring.size() {
            let page = __rust_allocate(PAGE_SIZE, PAGE_SIZE);

            if page.is_null() {
                return Err(());
            }

            // Grant access to the page to the backend
            let grant = try!(GrantTable::alloc_ref().ok_or(()));

            grant.grant_access(self.backend_id,
                               Mfn::from(Vaddr::from_ptr(page)), false);

            // Tell the backend about the new buffer via a RxRequest
            let req = unsafe { self.rx_ring.sring_mut().request_from_index(i) };

            req.id = i as u16;
            req.gref = grant.clone();

            // Keep track of buffers internally
            v.push(RxBuffer {
                id: i as u16,
                page: page,
                grant_ref: grant,
            });
        }

        // Update ring's request production index
        unsafe {
            *self.rx_ring.req_prod_mut() += self.rx_ring.size() as RingIdx;
        }

        // Notify the backend
        if self.rx_ring.push_requests() {
            event::send(self.evtchn);
        }

        unsafe {
            // Set notification index so that the backend notifies us when it
            // pushes new responses in the ring
            let rsp_event = self.rx_ring.rsp_cons() + 1;

            self.rx_ring.sring_mut().rsp_event_set(rsp_event);
        }

        self.rx_buffer = InterruptSpinLock::new(v);

        Ok(())
    }

    fn init_tx(&mut self) -> Result<(), ()> {
        let mut v: Vec<TxBuffer> = Vec::with_capacity(self.tx_ring.size());

        // We internally initialize empty buffers.
        // The way TX works with Xen is as follow:
        //
        // 1. We sent a TxRequest with a page that contains the packet to
        // transmit. The backend has granted access to the page.
        //
        // 2. The backend will transmit our packet and push in the ring a
        // TxResponse
        //
        // 3. refresh_tx() is called
        //
        // 4. refresh_tx() will consume TxResponse(s), ungrant access to the
        // page and free the packet.
        //
        // This is why we need to keep track of the grant reference and the
        // packet so that refresh_tx() can do its job.
        for i in 0..self.tx_ring.size() {
            let grant = try!(GrantTable::alloc_ref().ok_or(()));

            let buffer = TxBuffer {
                id: i as u16,
                pkt: None,
                grant_ref: grant,
            };

            v.push(buffer);
        }

        self.tx_buffer = SpinLock::new(v);

        Ok(())
    }

    /// Checks for received packet
    ///
    /// If there are packets, they are enqueued in the network stack's rx queue
    /// to be processed by the network thread
    fn rx_packet(&mut self) {
        loop {
            let prod = unsafe { self.rx_ring.sring_mut().rsp_prod() };
            let mut cons = self.rx_ring.rsp_cons();

            rmb();

            // Iterate through the RxResponses inside the ring
            while cons != prod {
                let resp = unsafe {
                    self.rx_ring.sring_mut()
                                   .response_from_index(cons as usize)
                };

                let id = resp.id as usize;

                {
                    let mut rx_buffer_locked = self.rx_buffer.lock();

                    // End access to the page now that we have the packet
                    rx_buffer_locked[id].grant_ref.end_access();

                    // If everything is good, enqueue the packet in the rx
                    // queue of the network stack
                    if resp.status > NetifRsp::Null as i16 {
                        let mut pkt = unsafe {
                            Packet::new(rx_buffer_locked[id].page,
                                        resp.offset as usize,
                                        resp.status as usize)
                        };

                        pkt.set_interface(self.intf.clone());

                        NetStack::enqueue_rx_packet(pkt);
                    } else {
                        // Deallocate the page if an error occurred
                        __rust_deallocate(rx_buffer_locked[id].page, PAGE_SIZE,
                                          PAGE_SIZE);
                    }

                    // Set the page as null so that we now that this buffer
                    // entry is free to re-use when refresh_rx_buffers() is
                    // called
                    rx_buffer_locked[id].page = ptr::null_mut();
                }

                cons += 1;
            }

            unsafe {
                *self.rx_ring.rsp_cons_mut() = cons;
            }

            if !self.rx_ring.final_check_for_responses() {
                break;
            }
        }
    }
}
<|endoftext|>