<commit_before>/*!
The gitignore module provides a way of reading a gitignore file and applying
it to a particular file name to determine whether it should be ignore or not.
The motivation for this submodule is performance and portability:

1. There is a gitignore crate on crates.io, but it uses the standard `glob`
   crate and checks patterns one-by-one. This is a reasonable implementation,
   but not suitable for the performance we need here.
2. We could shell out to a `git` sub-command like ls-files or status, but it
   seems better to not rely on the existence of external programs for a search
   tool. Besides, we need to implement this logic anyway to support things like
   an .rgignore file.

The key implementation detail here is that a single gitignore file is compiled
into a single RegexSet, which can be used to report which globs match a
particular file name. We can then do a quick post-processing step to implement
additional rules such as whitelists (prefix of `!`) or directory-only globs
(suffix of `/`).
*/

// TODO(burntsushi): Implement something similar, but for Mercurial. We can't
// use this exact implementation because hgignore files are different.

use std::cell::RefCell;
use std::error::Error as StdError;
use std::fmt;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::{Path, PathBuf};

use regex;

use glob;
use pathutil::strip_prefix;

/// Represents an error that can occur when parsing a gitignore file.
#[derive(Debug)]
pub enum Error {
    Glob(glob::Error),
    Regex(regex::Error),
    Io(io::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
            Error::Io(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
            Error::Io(ref err) => err.fmt(f),
        }
    }
}

impl From<glob::Error> for Error {
    fn from(err: glob::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

/// Gitignore is a matcher for the glob patterns in a single gitignore file.
#[derive(Clone, Debug)]
pub struct Gitignore {
    set: glob::Set,
    root: PathBuf,
    patterns: Vec<Pattern>,
    num_ignores: u64,
    num_whitelist: u64,
}

impl Gitignore {
    /// Create a new gitignore glob matcher from the given root directory and
    /// string containing the contents of a gitignore file.
    #[allow(dead_code)]
    fn from_str<P: AsRef<Path>>(
        root: P,
        gitignore: &str,
    ) -> Result<Gitignore, Error> {
        let mut builder = GitignoreBuilder::new(root);
        try!(builder.add_str(gitignore));
        builder.build()
    }

    /// Returns true if and only if the given file path should be ignored
    /// according to the globs in this gitignore. `is_dir` should be true if
    /// the path refers to a directory and false otherwise.
    ///
    /// Before matching path, its prefix (as determined by a common suffix
    /// of the directory containing this gitignore) is stripped. If there is
    /// no common suffix/prefix overlap, then path is assumed to reside in the
    /// same directory as this gitignore file.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        let mut path = path.as_ref();
        if let Some(p) = strip_prefix("./", path) {
            path = p;
        }
        if let Some(p) = strip_prefix(&self.root, path) {
            path = p;
        }
        self.matched_stripped(path, is_dir)
    }

    /// Like matched, but takes a path that has already been stripped.
    pub fn matched_stripped(&self, path: &Path, is_dir: bool) -> Match {
        thread_local! {
            static MATCHES: RefCell<Vec<usize>> = {
                RefCell::new(vec![])
            };
        };
        MATCHES.with(|matches| {
            let mut matches = matches.borrow_mut();
            self.set.matches_into(path, &mut *matches);
            for &i in matches.iter().rev() {
                let pat = &self.patterns[i];
                if !pat.only_dir || is_dir {
                    return if pat.whitelist {
                        Match::Whitelist(pat)
                    } else {
                        Match::Ignored(pat)
                    };
                }
            }
            Match::None
        })
    }

    /// Returns the total number of ignore patterns.
    pub fn num_ignores(&self) -> u64 {
        self.num_ignores
    }
}

/// The result of a glob match.
///
/// The lifetime `'a` refers to the lifetime of the pattern that resulted in
/// a match (whether ignored or whitelisted).
#[derive(Clone, Debug)]
pub enum Match<'a> {
    /// The path didn't match any glob in the gitignore file.
    None,
    /// The last glob matched indicates the path should be ignored.
    Ignored(&'a Pattern),
    /// The last glob matched indicates the path should be whitelisted.
    Whitelist(&'a Pattern),
}

impl<'a> Match<'a> {
    /// Returns true if the match result implies the path should be ignored.
    #[allow(dead_code)]
    pub fn is_ignored(&self) -> bool {
        match *self {
            Match::Ignored(_) => true,
            Match::None | Match::Whitelist(_) => false,
        }
    }

    /// Returns true if the match result didn't match any globs.
    pub fn is_none(&self) -> bool {
        match *self {
            Match::None => true,
            Match::Ignored(_) | Match::Whitelist(_) => false,
        }
    }

    /// Inverts the match so that Ignored becomes Whitelisted and Whitelisted
    /// becomes Ignored. A non-match remains the same.
    pub fn invert(self) -> Match<'a> {
        match self {
            Match::None => Match::None,
            Match::Ignored(pat) => Match::Whitelist(pat),
            Match::Whitelist(pat) => Match::Ignored(pat),
        }
    }
}

/// GitignoreBuilder constructs a matcher for a single set of globs from a
/// .gitignore file.
pub struct GitignoreBuilder {
    builder: glob::SetBuilder,
    root: PathBuf,
    patterns: Vec<Pattern>,
}

/// Pattern represents a single pattern in a gitignore file. It doesn't
/// know how to do glob matching directly, but it does store additional
/// options on a pattern, such as whether it's whitelisted.
#[derive(Clone, Debug)]
pub struct Pattern {
    /// The file path that this pattern was extracted from (may be empty).
    pub from: PathBuf,
    /// The original glob pattern string.
    pub original: String,
    /// The actual glob pattern string used to convert to a regex.
    pub pat: String,
    /// Whether this is a whitelisted pattern or not.
    pub whitelist: bool,
    /// Whether this pattern should only match directories or not.
    pub only_dir: bool,
}

impl GitignoreBuilder {
    /// Create a new builder for a gitignore file.
    ///
    /// The path given should be the path at which the globs for this gitignore
    /// file should be matched.
    pub fn new<P: AsRef<Path>>(root: P) -> GitignoreBuilder {
        GitignoreBuilder {
            builder: glob::SetBuilder::new(),
            root: root.as_ref().to_path_buf(),
            patterns: vec![],
        }
    }

    /// Builds a new matcher from the glob patterns added so far.
    ///
    /// Once a matcher is built, no new glob patterns can be added to it.
    pub fn build(self) -> Result<Gitignore, Error> {
        let nignores = self.patterns.iter().filter(|p| !p.whitelist).count();
        let nwhitelist = self.patterns.iter().filter(|p| p.whitelist).count();
        Ok(Gitignore {
            set: try!(self.builder.build()),
            root: self.root,
            patterns: self.patterns,
            num_ignores: nignores as u64,
            num_whitelist: nwhitelist as u64,
        })
    }

    /// Add each pattern line from the file path given.
    pub fn add_path<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
        let rdr = io::BufReader::new(try!(File::open(&path)));
        for line in rdr.lines() {
            try!(self.add(&path, &try!(line)));
        }
        Ok(())
    }

    /// Add each pattern line from the string given.
    pub fn add_str(&mut self, gitignore: &str) -> Result<(), Error> {
        for line in gitignore.lines() {
            try!(self.add("", line));
        }
        Ok(())
    }

    /// Add a line from a gitignore file to this builder.
    ///
    /// If the line could not be parsed as a glob, then an error is returned.
    pub fn add<P: AsRef<Path>>(
        &mut self,
        from: P,
        mut line: &str,
    ) -> Result<(), Error> {
        if line.is_empty() {
            return Ok(());
        }
        let mut pat = Pattern {
            from: from.as_ref().to_path_buf(),
            original: line.to_string(),
            pat: String::new(),
            whitelist: false,
            only_dir: false,
        };
        let mut opts = glob::MatchOptions::default();
        let has_slash = line.chars().any(|c| c == '/');
        // If the line starts with an escaped '!', then remove the escape.
        // Otherwise, if it starts with an unescaped '!', then this is a
        // whitelist pattern.
        match line.chars().nth(0) {
            Some('#') => return Ok(()),
            Some('\\') => {
                match line.chars().nth(1) {
                    Some('!') | Some('#') => {
                        line = &line[1..];
                    }
                    _ => {}
                }
            }
            Some('!') => {
                pat.whitelist = true;
                line = &line[1..];
            }
            Some('/') => {
                // `man gitignore` says that if a glob starts with a slash,
                // then the glob can only match the beginning of a path
                // (relative to the location of gitignore). We achieve this by
                // simply banning wildcards from matching /.
                opts.require_literal_separator = true;
                line = &line[1..];
            }
            _ => {}
        }
        // If it ends with a slash, then this should only match directories,
        // but the slash should otherwise not be used while globbing.
        if let Some((i, c)) = line.char_indices().rev().nth(0) {
            if c == '/' {
                pat.only_dir = true;
                line = &line[..i];
            }
        }
        // If there is a literal slash, then we note that so that globbing
        // doesn't let wildcards match slashes. Otherwise, we need to let
        // the pattern match anywhere, so we add a `**/` prefix to achieve
        // that behavior.
        pat.pat = line.to_string();
        if has_slash {
            opts.require_literal_separator = true;
        } else {
            pat.pat = format!("**/{}", pat.pat);
        }
        try!(self.builder.add_with(&pat.pat, &opts));
        self.patterns.push(pat);
        Ok(())
    }
}

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

    macro_rules! ignored {
        ($name:ident, $root:expr, $gi:expr, $path:expr) => {
            ignored!($name, $root, $gi, $path, false);
        };
        ($name:ident, $root:expr, $gi:expr, $path:expr, $is_dir:expr) => {
            #[test]
            fn $name() {
                let gi = Gitignore::from_str($root, $gi).unwrap();
                assert!(gi.matched($path, $is_dir).is_ignored());
            }
        };
    }

    macro_rules! not_ignored {
        ($name:ident, $root:expr, $gi:expr, $path:expr) => {
            not_ignored!($name, $root, $gi, $path, false);
        };
        ($name:ident, $root:expr, $gi:expr, $path:expr, $is_dir:expr) => {
            #[test]
            fn $name() {
                let gi = Gitignore::from_str($root, $gi).unwrap();
                assert!(!gi.matched($path, $is_dir).is_ignored());
            }
        };
    }

    const ROOT: &'static str = "/home/foobar/rust/rg";

    ignored!(ig1, ROOT, "months", "months");
    ignored!(ig2, ROOT, "*.lock", "Cargo.lock");
    ignored!(ig3, ROOT, "*.rs", "src/main.rs");
    ignored!(ig4, ROOT, "src/*.rs", "src/main.rs");
    ignored!(ig5, ROOT, "/*.c", "cat-file.c");
    ignored!(ig6, ROOT, "/src/*.rs", "src/main.rs");
    ignored!(ig7, ROOT, "!src/main.rs\n*.rs", "src/main.rs");
    ignored!(ig8, ROOT, "foo/", "foo", true);
    ignored!(ig9, ROOT, "**/foo", "foo");
    ignored!(ig10, ROOT, "**/foo", "src/foo");
    ignored!(ig11, ROOT, "**/foo/**", "src/foo/bar");
    ignored!(ig12, ROOT, "**/foo/**", "wat/src/foo/bar/baz");
    ignored!(ig13, ROOT, "**/foo/bar", "foo/bar");
    ignored!(ig14, ROOT, "**/foo/bar", "src/foo/bar");
    ignored!(ig15, ROOT, "abc/**", "abc/x");
    ignored!(ig16, ROOT, "abc/**", "abc/x/y");
    ignored!(ig17, ROOT, "abc/**", "abc/x/y/z");
    ignored!(ig18, ROOT, "a/**/b", "a/b");
    ignored!(ig19, ROOT, "a/**/b", "a/x/b");
    ignored!(ig20, ROOT, "a/**/b", "a/x/y/b");
    ignored!(ig21, ROOT, r"\!xy", "!xy");
    ignored!(ig22, ROOT, r"\#foo", "#foo");
    ignored!(ig23, ROOT, "foo", "./foo");
    ignored!(ig24, ROOT, "target", "grep/target");
    ignored!(ig25, ROOT, "Cargo.lock", "./tabwriter-bin/Cargo.lock");
    ignored!(ig26, ROOT, "/foo/bar/baz", "./foo/bar/baz");

    not_ignored!(ignot1, ROOT, "amonths", "months");
    not_ignored!(ignot2, ROOT, "monthsa", "months");
    not_ignored!(ignot3, ROOT, "src/*.rs", "src/grep/src/main.rs");
    not_ignored!(ignot4, ROOT, "/*.c", "mozilla-sha1/sha1.c");
    not_ignored!(ignot5, ROOT, "/src/*.rs", "src/grep/src/main.rs");
    not_ignored!(ignot6, ROOT, "*.rs\n!src/main.rs", "src/main.rs");
    not_ignored!(ignot7, ROOT, "foo/", "foo", false);
    not_ignored!(ignot8, ROOT, "**/foo/**", "wat/src/afoo/bar/baz");
    not_ignored!(ignot9, ROOT, "**/foo/**", "wat/src/fooa/bar/baz");
    not_ignored!(ignot10, ROOT, "**/foo/bar", "foo/src/bar");
    not_ignored!(ignot11, ROOT, "#foo", "#foo");
    not_ignored!(ignot12, ROOT, "\n\n\n", "foo");
}
<commit_msg>try again...<commit_after>/*!
The gitignore module provides a way of reading a gitignore file and applying
it to a particular file name to determine whether it should be ignore or not.
The motivation for this submodule is performance and portability:

1. There is a gitignore crate on crates.io, but it uses the standard `glob`
   crate and checks patterns one-by-one. This is a reasonable implementation,
   but not suitable for the performance we need here.
2. We could shell out to a `git` sub-command like ls-files or status, but it
   seems better to not rely on the existence of external programs for a search
   tool. Besides, we need to implement this logic anyway to support things like
   an .rgignore file.

The key implementation detail here is that a single gitignore file is compiled
into a single RegexSet, which can be used to report which globs match a
particular file name. We can then do a quick post-processing step to implement
additional rules such as whitelists (prefix of `!`) or directory-only globs
(suffix of `/`).
*/

// TODO(burntsushi): Implement something similar, but for Mercurial. We can't
// use this exact implementation because hgignore files are different.

use std::cell::RefCell;
use std::error::Error as StdError;
use std::fmt;
use std::fs::File;
use std::io::{self, BufRead};
use std::path::{Path, PathBuf};

use regex;

use glob;
use pathutil::strip_prefix;

/// Represents an error that can occur when parsing a gitignore file.
#[derive(Debug)]
pub enum Error {
    Glob(glob::Error),
    Regex(regex::Error),
    Io(io::Error),
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Glob(ref err) => err.description(),
            Error::Regex(ref err) => err.description(),
            Error::Io(ref err) => err.description(),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Glob(ref err) => err.fmt(f),
            Error::Regex(ref err) => err.fmt(f),
            Error::Io(ref err) => err.fmt(f),
        }
    }
}

impl From<glob::Error> for Error {
    fn from(err: glob::Error) -> Error {
        Error::Glob(err)
    }
}

impl From<regex::Error> for Error {
    fn from(err: regex::Error) -> Error {
        Error::Regex(err)
    }
}

impl From<io::Error> for Error {
    fn from(err: io::Error) -> Error {
        Error::Io(err)
    }
}

/// Gitignore is a matcher for the glob patterns in a single gitignore file.
#[derive(Clone, Debug)]
pub struct Gitignore {
    set: glob::Set,
    root: PathBuf,
    patterns: Vec<Pattern>,
    num_ignores: u64,
    num_whitelist: u64,
}

impl Gitignore {
    /// Create a new gitignore glob matcher from the given root directory and
    /// string containing the contents of a gitignore file.
    #[allow(dead_code)]
    fn from_str<P: AsRef<Path>>(
        root: P,
        gitignore: &str,
    ) -> Result<Gitignore, Error> {
        let mut builder = GitignoreBuilder::new(root);
        try!(builder.add_str(gitignore));
        builder.build()
    }

    /// Returns true if and only if the given file path should be ignored
    /// according to the globs in this gitignore. `is_dir` should be true if
    /// the path refers to a directory and false otherwise.
    ///
    /// Before matching path, its prefix (as determined by a common suffix
    /// of the directory containing this gitignore) is stripped. If there is
    /// no common suffix/prefix overlap, then path is assumed to reside in the
    /// same directory as this gitignore file.
    pub fn matched<P: AsRef<Path>>(&self, path: P, is_dir: bool) -> Match {
        let mut path = path.as_ref();
        if let Some(p) = strip_prefix("./", path) {
            path = p;
        }
        if let Some(p) = strip_prefix(&self.root, path) {
            path = p;
        }
        self.matched_stripped(path, is_dir)
    }

    /// Like matched, but takes a path that has already been stripped.
    pub fn matched_stripped(&self, path: &Path, is_dir: bool) -> Match {
        thread_local! {
            static MATCHES: RefCell<Vec<usize>> = {
                RefCell::new(vec![])
            }
        };
        MATCHES.with(|matches| {
            let mut matches = matches.borrow_mut();
            self.set.matches_into(path, &mut *matches);
            for &i in matches.iter().rev() {
                let pat = &self.patterns[i];
                if !pat.only_dir || is_dir {
                    return if pat.whitelist {
                        Match::Whitelist(pat)
                    } else {
                        Match::Ignored(pat)
                    };
                }
            }
            Match::None
        })
    }

    /// Returns the total number of ignore patterns.
    pub fn num_ignores(&self) -> u64 {
        self.num_ignores
    }
}

/// The result of a glob match.
///
/// The lifetime `'a` refers to the lifetime of the pattern that resulted in
/// a match (whether ignored or whitelisted).
#[derive(Clone, Debug)]
pub enum Match<'a> {
    /// The path didn't match any glob in the gitignore file.
    None,
    /// The last glob matched indicates the path should be ignored.
    Ignored(&'a Pattern),
    /// The last glob matched indicates the path should be whitelisted.
    Whitelist(&'a Pattern),
}

impl<'a> Match<'a> {
    /// Returns true if the match result implies the path should be ignored.
    #[allow(dead_code)]
    pub fn is_ignored(&self) -> bool {
        match *self {
            Match::Ignored(_) => true,
            Match::None | Match::Whitelist(_) => false,
        }
    }

    /// Returns true if the match result didn't match any globs.
    pub fn is_none(&self) -> bool {
        match *self {
            Match::None => true,
            Match::Ignored(_) | Match::Whitelist(_) => false,
        }
    }

    /// Inverts the match so that Ignored becomes Whitelisted and Whitelisted
    /// becomes Ignored. A non-match remains the same.
    pub fn invert(self) -> Match<'a> {
        match self {
            Match::None => Match::None,
            Match::Ignored(pat) => Match::Whitelist(pat),
            Match::Whitelist(pat) => Match::Ignored(pat),
        }
    }
}

/// GitignoreBuilder constructs a matcher for a single set of globs from a
/// .gitignore file.
pub struct GitignoreBuilder {
    builder: glob::SetBuilder,
    root: PathBuf,
    patterns: Vec<Pattern>,
}

/// Pattern represents a single pattern in a gitignore file. It doesn't
/// know how to do glob matching directly, but it does store additional
/// options on a pattern, such as whether it's whitelisted.
#[derive(Clone, Debug)]
pub struct Pattern {
    /// The file path that this pattern was extracted from (may be empty).
    pub from: PathBuf,
    /// The original glob pattern string.
    pub original: String,
    /// The actual glob pattern string used to convert to a regex.
    pub pat: String,
    /// Whether this is a whitelisted pattern or not.
    pub whitelist: bool,
    /// Whether this pattern should only match directories or not.
    pub only_dir: bool,
}

impl GitignoreBuilder {
    /// Create a new builder for a gitignore file.
    ///
    /// The path given should be the path at which the globs for this gitignore
    /// file should be matched.
    pub fn new<P: AsRef<Path>>(root: P) -> GitignoreBuilder {
        GitignoreBuilder {
            builder: glob::SetBuilder::new(),
            root: root.as_ref().to_path_buf(),
            patterns: vec![],
        }
    }

    /// Builds a new matcher from the glob patterns added so far.
    ///
    /// Once a matcher is built, no new glob patterns can be added to it.
    pub fn build(self) -> Result<Gitignore, Error> {
        let nignores = self.patterns.iter().filter(|p| !p.whitelist).count();
        let nwhitelist = self.patterns.iter().filter(|p| p.whitelist).count();
        Ok(Gitignore {
            set: try!(self.builder.build()),
            root: self.root,
            patterns: self.patterns,
            num_ignores: nignores as u64,
            num_whitelist: nwhitelist as u64,
        })
    }

    /// Add each pattern line from the file path given.
    pub fn add_path<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
        let rdr = io::BufReader::new(try!(File::open(&path)));
        for line in rdr.lines() {
            try!(self.add(&path, &try!(line)));
        }
        Ok(())
    }

    /// Add each pattern line from the string given.
    pub fn add_str(&mut self, gitignore: &str) -> Result<(), Error> {
        for line in gitignore.lines() {
            try!(self.add("", line));
        }
        Ok(())
    }

    /// Add a line from a gitignore file to this builder.
    ///
    /// If the line could not be parsed as a glob, then an error is returned.
    pub fn add<P: AsRef<Path>>(
        &mut self,
        from: P,
        mut line: &str,
    ) -> Result<(), Error> {
        if line.is_empty() {
            return Ok(());
        }
        let mut pat = Pattern {
            from: from.as_ref().to_path_buf(),
            original: line.to_string(),
            pat: String::new(),
            whitelist: false,
            only_dir: false,
        };
        let mut opts = glob::MatchOptions::default();
        let has_slash = line.chars().any(|c| c == '/');
        // If the line starts with an escaped '!', then remove the escape.
        // Otherwise, if it starts with an unescaped '!', then this is a
        // whitelist pattern.
        match line.chars().nth(0) {
            Some('#') => return Ok(()),
            Some('\\') => {
                match line.chars().nth(1) {
                    Some('!') | Some('#') => {
                        line = &line[1..];
                    }
                    _ => {}
                }
            }
            Some('!') => {
                pat.whitelist = true;
                line = &line[1..];
            }
            Some('/') => {
                // `man gitignore` says that if a glob starts with a slash,
                // then the glob can only match the beginning of a path
                // (relative to the location of gitignore). We achieve this by
                // simply banning wildcards from matching /.
                opts.require_literal_separator = true;
                line = &line[1..];
            }
            _ => {}
        }
        // If it ends with a slash, then this should only match directories,
        // but the slash should otherwise not be used while globbing.
        if let Some((i, c)) = line.char_indices().rev().nth(0) {
            if c == '/' {
                pat.only_dir = true;
                line = &line[..i];
            }
        }
        // If there is a literal slash, then we note that so that globbing
        // doesn't let wildcards match slashes. Otherwise, we need to let
        // the pattern match anywhere, so we add a `**/` prefix to achieve
        // that behavior.
        pat.pat = line.to_string();
        if has_slash {
            opts.require_literal_separator = true;
        } else {
            pat.pat = format!("**/{}", pat.pat);
        }
        try!(self.builder.add_with(&pat.pat, &opts));
        self.patterns.push(pat);
        Ok(())
    }
}

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

    macro_rules! ignored {
        ($name:ident, $root:expr, $gi:expr, $path:expr) => {
            ignored!($name, $root, $gi, $path, false);
        };
        ($name:ident, $root:expr, $gi:expr, $path:expr, $is_dir:expr) => {
            #[test]
            fn $name() {
                let gi = Gitignore::from_str($root, $gi).unwrap();
                assert!(gi.matched($path, $is_dir).is_ignored());
            }
        };
    }

    macro_rules! not_ignored {
        ($name:ident, $root:expr, $gi:expr, $path:expr) => {
            not_ignored!($name, $root, $gi, $path, false);
        };
        ($name:ident, $root:expr, $gi:expr, $path:expr, $is_dir:expr) => {
            #[test]
            fn $name() {
                let gi = Gitignore::from_str($root, $gi).unwrap();
                assert!(!gi.matched($path, $is_dir).is_ignored());
            }
        };
    }

    const ROOT: &'static str = "/home/foobar/rust/rg";

    ignored!(ig1, ROOT, "months", "months");
    ignored!(ig2, ROOT, "*.lock", "Cargo.lock");
    ignored!(ig3, ROOT, "*.rs", "src/main.rs");
    ignored!(ig4, ROOT, "src/*.rs", "src/main.rs");
    ignored!(ig5, ROOT, "/*.c", "cat-file.c");
    ignored!(ig6, ROOT, "/src/*.rs", "src/main.rs");
    ignored!(ig7, ROOT, "!src/main.rs\n*.rs", "src/main.rs");
    ignored!(ig8, ROOT, "foo/", "foo", true);
    ignored!(ig9, ROOT, "**/foo", "foo");
    ignored!(ig10, ROOT, "**/foo", "src/foo");
    ignored!(ig11, ROOT, "**/foo/**", "src/foo/bar");
    ignored!(ig12, ROOT, "**/foo/**", "wat/src/foo/bar/baz");
    ignored!(ig13, ROOT, "**/foo/bar", "foo/bar");
    ignored!(ig14, ROOT, "**/foo/bar", "src/foo/bar");
    ignored!(ig15, ROOT, "abc/**", "abc/x");
    ignored!(ig16, ROOT, "abc/**", "abc/x/y");
    ignored!(ig17, ROOT, "abc/**", "abc/x/y/z");
    ignored!(ig18, ROOT, "a/**/b", "a/b");
    ignored!(ig19, ROOT, "a/**/b", "a/x/b");
    ignored!(ig20, ROOT, "a/**/b", "a/x/y/b");
    ignored!(ig21, ROOT, r"\!xy", "!xy");
    ignored!(ig22, ROOT, r"\#foo", "#foo");
    ignored!(ig23, ROOT, "foo", "./foo");
    ignored!(ig24, ROOT, "target", "grep/target");
    ignored!(ig25, ROOT, "Cargo.lock", "./tabwriter-bin/Cargo.lock");
    ignored!(ig26, ROOT, "/foo/bar/baz", "./foo/bar/baz");

    not_ignored!(ignot1, ROOT, "amonths", "months");
    not_ignored!(ignot2, ROOT, "monthsa", "months");
    not_ignored!(ignot3, ROOT, "src/*.rs", "src/grep/src/main.rs");
    not_ignored!(ignot4, ROOT, "/*.c", "mozilla-sha1/sha1.c");
    not_ignored!(ignot5, ROOT, "/src/*.rs", "src/grep/src/main.rs");
    not_ignored!(ignot6, ROOT, "*.rs\n!src/main.rs", "src/main.rs");
    not_ignored!(ignot7, ROOT, "foo/", "foo", false);
    not_ignored!(ignot8, ROOT, "**/foo/**", "wat/src/afoo/bar/baz");
    not_ignored!(ignot9, ROOT, "**/foo/**", "wat/src/fooa/bar/baz");
    not_ignored!(ignot10, ROOT, "**/foo/bar", "foo/src/bar");
    not_ignored!(ignot11, ROOT, "#foo", "#foo");
    not_ignored!(ignot12, ROOT, "\n\n\n", "foo");
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
use super::{ElfResult, ElfWord, Section, section};
use super::ValidatesWord;
use core::{fmt, mem};

pub trait Header {
    type Word: ElfWord;

    /// Attempt to extract an ELF file header from a slice of bytes.
    /// TODO: should this be a From impl maybe?
    //          - eliza, 03/08/2017
    fn from_slice<'a>(input: &'a [u8]) -> ElfResult<&'a Self>;

    /// Attempt to extract a section header from a slice of bytes.
    /// TODO: can/should the index be `usize`?
    //          - eliza, 03/08/2017
    fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16)
                            -> ElfResult<&'a Section>;

    // Field accessors -------------------------------------------
    fn ident(&self) -> Ident;
    fn get_type(&self) -> Type;
    fn machine(&self) -> Machine;
    /// Offset of the program entry point
    fn entry_point(&self) -> usize;
    /// Offset of the start of program headers
    fn ph_offset(&self) -> usize;
    /// Number of program headers.
    fn ph_count(&self) -> usize;
    /// Size of a program header.
    /// TODO: add function for using this and `ph_count` to make a memrange
    ///       representing the program header entries?
    //          - eliza, 03/08/2017
    fn ph_entry_size(&self) -> usize;
    /// Offset of the start of [section header]s.
    ///
    /// [section header]: ../section/struct.Header.html
    fn sh_offset(&self) -> usize;
    /// Number of [section header]s.
    ///
    /// [section header]: ../section/struct.Header.html
    fn sh_count(&self) -> usize;
    /// Size of a [section header].
    ///
    /// [section header]: ../section/struct.Header.html
    /// TODO: add function for using this and `sh_count` to make a memrange
    ///       representing the section header entries?
    //          - eliza, 03/08/2017
    fn sh_entry_size(&self) -> usize;
    /// TODO: can this return the flags type?
    //          - eliza, 03/08/2017
    fn flags(&self) -> u32;
    /// Index of the section header [string table].
    ///
    /// [string table]: ../section/struct.StrTable.html"]
    fn sh_str_idx(&self) -> usize;

}

/// FIXME(style): generate more stuff with macros/use `macro_attr` to derive
///               these...
//                  - eliza, 03/08/2017
macro_rules! impl_getters {
    ($(#[$attr:meta])* pub fn $name:ident(&self) -> $ty:ident; $($rest:tt)*) => {
        $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty }
        impl_getters!{ $( $rest )* }
    };
    ($(#[$attr:meta])* fn $name:ident(&self) -> $ty:ident; $($rest:tt)*) => {
        $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty }
        impl_getters!{ $( $rest )* }
    };
    ( $(#[$attr:meta])* pub fn $name: ident (&self)-> $ty: ident; ) => {
        $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty }
    };
    ( $(#[$attr:meta])* fn $name: ident (&self)-> $ty: ident; ) => {
        $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty }
    };
    () => {};
}

macro_rules! Header {
    (($($size:ty),+) $(pub)* enum $name:ident $($tail:tt)* ) => {
        Header! { @impl $name, $($size)+ }
    };
    (($($size:ty),+) $(pub)* struct $name:ident $($tail:tt)*) => {
        Header! { @impl $name, $($size)+ }
    };
    (@impl $name:ident, $($size:ty)+) => {
        $(impl Header for $name<$size> {
            type Word = $size;
            /// Attempt to extract an ELF file header from a slice of bytes.
            fn from_slice<'a>(input: &'a [u8]) -> ElfResult<&'a Self> {
                if input.len() < mem::size_of::<Self>() {
                    Err("Input too short to extract ELF header")
                } else {
                    unsafe { Ok(&super::extract_from_slice::<Self>(input, 0, 1)[0]) }
                }
            }

            /// Attempt to extract a section header from a slice of bytes.
            fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16)
                                    -> ElfResult<&'a Section>
            {
                if idx < section::SHN_LORESERVE {
                    Err("Cannot parse reserved section.")
                } else {
                    // use ValidatesWord to check if this section's Class field
                    // will let us interpret the section with the requested
                    // word length. this is a bit of a hack around the type
                    // system not letting me do this the way I wanted to to....
                    let validator: &ValidatesWord<Self::Word>
                        = &self.ident.class;
                    validator.check()?;

                    // start offset for section
                    let start = self.sh_offset() + idx as usize *
                                self.sh_entry_size();
                    // end offset for section
                    let end = start + self.sh_entry_size();
                    let raw = &input[start .. end];

                    match self.ident.class {
                        // Class::None is always invalid ----------------------
                        Class::None =>
                            Err("Invalid ELF class (ELFCLASSNONE).")
                        // Class and header types mismatch --------------------
                      , Class::Elf64 =>
                            Err("Cannot parse 64-bit section from 32-bit ELF.")
                        // class and header types match -----------------------
                      , Class::Elf32 => unsafe {
                            Ok(&*(raw as *const [u8] as *const u8 as *const Section))
                        }
                    }
                }
            }

            #[inline] fn get_type(&self) -> Type { self.elftype.as_type() }

            impl_getters! {
                #[doc = "Index for the start of [section header]s. \
                         [section header]: ../section/struct.Header.html"]
                fn sh_offset(&self) -> usize;
                fn sh_entry_size(&self) -> usize;
                fn sh_count(&self) -> usize;

                #[doc = "Index for the start of program headers"]
                fn ph_offset(&self) -> usize;
                fn ph_entry_size(&self) -> usize;
                fn ph_count(&self) -> usize;

                #[doc = "Index for the program entry point"]
                fn entry_point(&self) -> usize;
                #[doc = "Index of the section header [string table] \
                         [string table]: ../section/struct.StrTable.html"]
                fn sh_str_idx(&self) -> usize;
                fn flags(&self) -> u32;
                fn ident(&self) -> Ident;
                fn machine(&self) -> Machine;
            }
        })+
    }
}

macro_attr! {
    /// An ELF file header
    #[derive(Copy, Clone, Debug, Header!(u32, u64))]
    #[repr(C, packed)]
    pub struct HeaderRepr<W: ElfWord> {
        /// the ELF [file identifier](struct.Ident.html)
        pub ident: Ident
      , elftype: TypeRepr
      , pub machine: Machine
      , /// Program entry point
        /// TODO: getters for turning these into `usize`?
        //          - eliza, 03/08/2017
        entry_point: W
      , /// Offset for start of program headers
        ph_offset: W
      , /// Offset for start of [section header]s.
        /// [section header]: ../section/struct.Header.html
        sh_offset: W
      , pub flags: u32
      , pub header_size: u16
      , pub ph_entry_size: u16
      , pub ph_count: u16
      , pub sh_entry_size: u16
      , pub sh_count: u16
      , /// Index of the section header string table
        sh_str_idx: u16
    }
}

/// ELF header magic
pub const MAGIC: Magic = [0x7f, b'E', b'L', b'F'];

/// Type of header magic
pub type Magic = [u8; 4];

/// ELF identifier (`e_ident` in the ELF standard)
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Ident {
    /// ELF magic numbers. Must be equal to the [ELF magic], `[0x7, E, L, F]`.
    ///
    /// [ELF magic]: constant.MAGIC.html
    pub magic: Magic
  , /// ELF [file class](enum.Class.html) (32- or 64-bit)
    pub class: Class
  , /// ELF [data encoding](enum.DataEncoding.html) (big- or little-endian)
    pub encoding: DataEncoding
  , /// ELF file [version](enum.Version.html)
    pub version: Version
  , /// What [operating system ABI] this file was compiled for.
    ///
    /// [operating system ABI]: enum.OsAbi.html
    pub abi: OsAbi
  , /// ABI version (often this is just padding)
    pub abi_version: u8
  , _padding: [u8; 7]
}

impl Ident {
    #[inline] pub fn check_magic(&self) -> bool { self.magic == MAGIC }

    /// Returns true if the identifier section identifies a valid ELF file.
    #[inline] pub fn is_valid(&self) -> bool {
        // the ELF magic number is correct
        self.check_magic() &&
        // the file class is either 32- or 64-bits
        self.class.is_valid() &&
        // the data encoding is either big- or little-endian
        self.encoding.is_valid()
    }
}

/// Identifies the class of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum Class {
    /// Invalid ELF class file (`ELFCLASSNONE` in the standard)
    None  = 0
  , /// 32-bit ELF file (`ELFCLASS32` in the standard)
    Elf32 = 1
  , /// 64-bit ELF file (`ELFCLASS64` in the standard)
    Elf64 = 2
}

impl Class {
    /// Returns true if the class field for this file is valid.
    #[inline]
    pub fn is_valid(&self) -> bool {
        match *self { Class::None => false
                    , _ => true
                    }
    }

}

impl ValidatesWord<u64> for Class {
    #[inline]
    fn check(&self) -> ElfResult<()> {
        use self::Class::*;
        match *self {
            None => Err("Invalid ELF type ELFCLASSNONE!")
          , Elf32 => Err("Cannot extract 64-bit section from 32-bit ELF")
          , Elf64 => Ok(())
        }
    }
}

impl ValidatesWord<u32> for Class {
    #[inline]
    fn check(&self) -> ElfResult<()> {
        use self::Class::*;
        match *self {
            None => Err("Invalid ELF type ELFCLASSNONE!")
          , Elf64 =>
                // TODO: is this actually true?
                //          - eliza, 03/08/2017
                Err("Cannot extract 32-bit section from 64-bit ELF")
          , Elf32 => Ok(())
        }
    }
}

/// Identifies the data encoding of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum DataEncoding {
    /// Invalid data encoding (`ELFDATANONE` in the standard)
    None  = 0
  , /// Twos-complement little-endian data encoding
    /// (`ELFDATA2LSB` in the standard)
    LittleEndian = 1
  , /// Twos-complement big-endian data encoding
    /// (`ELFDATA2MSB` in the standard)
    BigEndian = 2
}

impl DataEncoding {
    /// Returns true if the data encoding field for this file is valid.
    #[inline]
    pub fn is_valid(&self) -> bool {
        match *self { DataEncoding::None => false
                    , _ => true
                    }
    }
}

/// Operating system ABI
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum OsAbi { /// Ox00 also represents "none"
                 SystemV = 0x00
               , HpUx    = 0x01
               , NetBsd  = 0x02
               , Linux   = 0x03
               , Solaris = 0x06
               , Aix     = 0x07
               , Irix    = 0x08
               , FreeBsd = 0x09
               , OpenBsd = 0x0C
               , OpenVms = 0x0D
               }

/// Identifies the version of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum Version { None = 0, Current = 1 }

#[derive(Clone, Copy, PartialEq)]
struct TypeRepr(u16);

impl TypeRepr {
    pub fn as_type(&self) -> Type {
        match self.0 {
            0 => Type::None
          , 1 => Type::Relocatable
          , 2 => Type::Executable
          , 3 => Type::SharedObject
          , 4 => Type::Core
          , anything => Type::Other(anything)
        }
    }
}

impl fmt::Debug for TypeRepr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_type().fmt(f)
    }
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Type { None
              , Relocatable
              , Executable
              , SharedObject
              , Core
              , Other(u16)
              }

#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u16)]
pub enum Machine { None    = 0
                 , Sparc   = 0x02
                 , X86     = 0x03
                 , Mips    = 0x08
                 , PowerPc = 0x14
                 , Arm     = 0x28
                 , SuperH  = 0x2A
                 , Ia64    = 0x32
                 , X86_64  = 0x3E
                 , AArch64 = 0xB7
                 }
<commit_msg>style(elf): make some ugly code less ugly<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015-2017 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
use super::{ElfResult, ElfWord, Section, section};
use super::ValidatesWord;
use core::{fmt, mem};

pub trait Header {
    type Word: ElfWord;

    /// Attempt to extract an ELF file header from a slice of bytes.
    /// TODO: should this be a From impl maybe?
    //          - eliza, 03/08/2017
    fn from_slice<'a>(input: &'a [u8]) -> ElfResult<&'a Self>;

    /// Attempt to extract a section header from a slice of bytes.
    /// TODO: can/should the index be `usize`?
    //          - eliza, 03/08/2017
    fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16)
                            -> ElfResult<&'a Section>;

    // Field accessors -------------------------------------------
    fn ident(&self) -> Ident;
    fn get_type(&self) -> Type;
    fn machine(&self) -> Machine;
    /// Offset of the program entry point
    fn entry_point(&self) -> usize;
    /// Offset of the start of program headers
    fn ph_offset(&self) -> usize;
    /// Number of program headers.
    fn ph_count(&self) -> usize;
    /// Size of a program header.
    /// TODO: add function for using this and `ph_count` to make a memrange
    ///       representing the program header entries?
    //          - eliza, 03/08/2017
    fn ph_entry_size(&self) -> usize;
    /// Offset of the start of [section header]s.
    ///
    /// [section header]: ../section/struct.Header.html
    fn sh_offset(&self) -> usize;
    /// Number of [section header]s.
    ///
    /// [section header]: ../section/struct.Header.html
    fn sh_count(&self) -> usize;
    /// Size of a [section header].
    ///
    /// [section header]: ../section/struct.Header.html
    /// TODO: add function for using this and `sh_count` to make a memrange
    ///       representing the section header entries?
    //          - eliza, 03/08/2017
    fn sh_entry_size(&self) -> usize;
    /// TODO: can this return the flags type?
    //          - eliza, 03/08/2017
    fn flags(&self) -> u32;
    /// Index of the section header [string table].
    ///
    /// [string table]: ../section/struct.StrTable.html"]
    fn sh_str_idx(&self) -> usize;

}

/// FIXME(style): generate more stuff with macros/use `macro_attr` to derive
///               these...
//                  - eliza, 03/08/2017
macro_rules! impl_getters {
    ($(#[$attr:meta])* pub fn $name:ident(&self) -> $ty:ident; $($rest:tt)*) => {
        $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty }
        impl_getters!{ $( $rest )* }
    };
    ($(#[$attr:meta])* fn $name:ident(&self) -> $ty:ident; $($rest:tt)*) => {
        $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty }
        impl_getters!{ $( $rest )* }
    };
    ( $(#[$attr:meta])* pub fn $name: ident (&self)-> $ty: ident; ) => {
        $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty }
    };
    ( $(#[$attr:meta])* fn $name: ident (&self)-> $ty: ident; ) => {
        $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty }
    };
    () => {};
}

macro_rules! Header {
    (($($size:ty),+) $(pub)* enum $name:ident $($tail:tt)* ) => {
        Header! { @impl $name, $($size)+ }
    };
    (($($size:ty),+) $(pub)* struct $name:ident $($tail:tt)*) => {
        Header! { @impl $name, $($size)+ }
    };
    (@impl $name:ident, $($size:ty)+) => {
        $(impl Header for $name<$size> {
            type Word = $size;
            /// Attempt to extract an ELF file header from a slice of bytes.
            fn from_slice<'a>(input: &'a [u8]) -> ElfResult<&'a Self> {
                if input.len() < mem::size_of::<Self>() {
                    Err("Input too short to extract ELF header")
                } else {
                    unsafe { Ok(&super::extract_from_slice::<Self>(input, 0, 1)[0]) }
                }
            }

            /// Attempt to extract a section header from a slice of bytes.
            fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16)
                                    -> ElfResult<&'a Section>
            {
                if idx < section::SHN_LORESERVE {
                    Err("Cannot parse reserved section.")
                } else {
                    // use ValidatesWord to check if this section's Class field
                    // will let us interpret the section with the requested
                    // word length. this is a bit of a hack around the type
                    // system not letting me do this the way I wanted to to....
                    let validator: &ValidatesWord<Self::Word>
                        = &self.ident.class;
                    validator.check()?;

                    // start offset for section
                    let start = self.sh_offset() + idx as usize *
                                self.sh_entry_size();
                    // end offset for section
                    let end = start + self.sh_entry_size();
                    let raw = &input[start .. end];

                    unsafe {
                        Ok(&*(raw as *const [u8]
                                  as *const _
                                  as *const Section))
                    }
                }
            }

            #[inline] fn get_type(&self) -> Type { self.elftype.as_type() }

            impl_getters! {
                #[doc = "Index for the start of [section header]s. \
                         [section header]: ../section/struct.Header.html"]
                fn sh_offset(&self) -> usize;
                fn sh_entry_size(&self) -> usize;
                fn sh_count(&self) -> usize;

                #[doc = "Index for the start of program headers"]
                fn ph_offset(&self) -> usize;
                fn ph_entry_size(&self) -> usize;
                fn ph_count(&self) -> usize;

                #[doc = "Index for the program entry point"]
                fn entry_point(&self) -> usize;
                #[doc = "Index of the section header [string table] \
                         [string table]: ../section/struct.StrTable.html"]
                fn sh_str_idx(&self) -> usize;
                fn flags(&self) -> u32;
                fn ident(&self) -> Ident;
                fn machine(&self) -> Machine;
            }
        })+
    }
}

macro_attr! {
    /// An ELF file header
    #[derive(Copy, Clone, Debug, Header!(u32, u64))]
    #[repr(C, packed)]
    pub struct HeaderRepr<W: ElfWord> {
        /// the ELF [file identifier](struct.Ident.html)
        pub ident: Ident
      , elftype: TypeRepr
      , pub machine: Machine
      , /// Program entry point
        entry_point: W
      , /// Offset for start of program headers
        ph_offset: W
      , /// Offset for start of [section header]s.
        /// [section header]: ../section/struct.Header.html
        sh_offset: W
      , pub flags: u32
      , pub header_size: u16
      , pub ph_entry_size: u16
      , pub ph_count: u16
      , pub sh_entry_size: u16
      , pub sh_count: u16
      , /// Index of the section header string table
        sh_str_idx: u16
    }
}

/// ELF header magic
pub const MAGIC: Magic = [0x7f, b'E', b'L', b'F'];

/// Type of header magic
pub type Magic = [u8; 4];

/// ELF identifier (`e_ident` in the ELF standard)
#[derive(Copy, Clone, Debug)]
#[repr(C, packed)]
pub struct Ident {
    /// ELF magic numbers. Must be equal to the [ELF magic], `[0x7, E, L, F]`.
    ///
    /// [ELF magic]: constant.MAGIC.html
    pub magic: Magic
  , /// ELF [file class](enum.Class.html) (32- or 64-bit)
    pub class: Class
  , /// ELF [data encoding](enum.DataEncoding.html) (big- or little-endian)
    pub encoding: DataEncoding
  , /// ELF file [version](enum.Version.html)
    pub version: Version
  , /// What [operating system ABI] this file was compiled for.
    ///
    /// [operating system ABI]: enum.OsAbi.html
    pub abi: OsAbi
  , /// ABI version (often this is just padding)
    pub abi_version: u8
  , _padding: [u8; 7]
}

impl Ident {
    #[inline] pub fn check_magic(&self) -> bool { self.magic == MAGIC }

    /// Returns true if the identifier section identifies a valid ELF file.
    #[inline] pub fn is_valid(&self) -> bool {
        // the ELF magic number is correct
        self.check_magic() &&
        // the file class is either 32- or 64-bits
        self.class.is_valid() &&
        // the data encoding is either big- or little-endian
        self.encoding.is_valid()
    }
}

/// Identifies the class of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum Class {
    /// Invalid ELF class file (`ELFCLASSNONE` in the standard)
    None  = 0
  , /// 32-bit ELF file (`ELFCLASS32` in the standard)
    Elf32 = 1
  , /// 64-bit ELF file (`ELFCLASS64` in the standard)
    Elf64 = 2
}

impl Class {
    /// Returns true if the class field for this file is valid.
    #[inline]
    pub fn is_valid(&self) -> bool {
        match *self { Class::None => false
                    , _ => true
                    }
    }

}

impl ValidatesWord<u64> for Class {
    #[inline]
    fn check(&self) -> ElfResult<()> {
        use self::Class::*;
        match *self {
            None => Err("Invalid ELF type ELFCLASSNONE!")
          , Elf32 => Err("Cannot extract 64-bit section from 32-bit ELF")
          , Elf64 => Ok(())
        }
    }
}

impl ValidatesWord<u32> for Class {
    #[inline]
    fn check(&self) -> ElfResult<()> {
        use self::Class::*;
        match *self {
            None => Err("Invalid ELF type ELFCLASSNONE!")
          , Elf64 =>
                // TODO: is this actually true?
                //          - eliza, 03/08/2017
                Err("Cannot extract 32-bit section from 64-bit ELF")
          , Elf32 => Ok(())
        }
    }
}

/// Identifies the data encoding of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum DataEncoding {
    /// Invalid data encoding (`ELFDATANONE` in the standard)
    None  = 0
  , /// Twos-complement little-endian data encoding
    /// (`ELFDATA2LSB` in the standard)
    LittleEndian = 1
  , /// Twos-complement big-endian data encoding
    /// (`ELFDATA2MSB` in the standard)
    BigEndian = 2
}

impl DataEncoding {
    /// Returns true if the data encoding field for this file is valid.
    #[inline]
    pub fn is_valid(&self) -> bool {
        match *self { DataEncoding::None => false
                    , _ => true
                    }
    }
}

/// Operating system ABI
#[derive(Copy, Clone, Debug)]
#[repr(u8)]
pub enum OsAbi { /// Ox00 also represents "none"
                 SystemV = 0x00
               , HpUx    = 0x01
               , NetBsd  = 0x02
               , Linux   = 0x03
               , Solaris = 0x06
               , Aix     = 0x07
               , Irix    = 0x08
               , FreeBsd = 0x09
               , OpenBsd = 0x0C
               , OpenVms = 0x0D
               }

/// Identifies the version of the ELF file
#[derive(Copy, Clone, PartialEq, Debug)]
#[repr(u8)]
pub enum Version { None = 0, Current = 1 }

#[derive(Clone, Copy, PartialEq)]
struct TypeRepr(u16);

impl TypeRepr {
    pub fn as_type(&self) -> Type {
        match self.0 {
            0 => Type::None
          , 1 => Type::Relocatable
          , 2 => Type::Executable
          , 3 => Type::SharedObject
          , 4 => Type::Core
          , anything => Type::Other(anything)
        }
    }
}

impl fmt::Debug for TypeRepr {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_type().fmt(f)
    }
}

#[derive(Clone, Copy, PartialEq, Debug)]
pub enum Type { None
              , Relocatable
              , Executable
              , SharedObject
              , Core
              , Other(u16)
              }

#[allow(non_camel_case_types)]
#[derive(Clone, Copy, PartialEq, Debug)]
#[repr(u16)]
pub enum Machine { None    = 0
                 , Sparc   = 0x02
                 , X86     = 0x03
                 , Mips    = 0x08
                 , PowerPc = 0x14
                 , Arm     = 0x28
                 , SuperH  = 0x2A
                 , Ia64    = 0x32
                 , X86_64  = 0x3E
                 , AArch64 = 0xB7
                 }
<|endoftext|>


<commit_before>#[macro_use]
extern crate glium;
extern crate isosurface;
extern crate ndarray;

use ndarray::Array;
use isosurface::marching_tetrahedra;
use glium::glutin;

mod support;

mod surface {
    use glium;
    use glium::backend::Facade;
    use support;
    use std;

    #[derive(Copy, Clone)]
    struct Vertex {
        position: [f32; 3],
        color: [f32; 3],
        normal: [f32; 3],
    }

    implement_vertex!(Vertex, position, color, normal);

    pub struct Surface {
        verts: glium::VertexBuffer<Vertex>,
        indices: glium::IndexBuffer<u32>,
        program: glium::Program,
        normal_program: glium::Program,
        model: [[f32; 4]; 4],
    }

    impl Surface {
        pub fn new<F: Facade>(facade: &F,
                              verts: &[[f64; 3]],
                              faces: &[[u32; 3]],
                              normals: &[[f64; 3]])
                              -> Surface {

            // compute the bounding box
            let mut xmin = std::f64::MAX;
            let mut ymin = std::f64::MAX;
            let mut zmin = std::f64::MAX;
            let mut xmax = std::f64::MIN;
            let mut ymax = std::f64::MIN;
            let mut zmax = std::f64::MIN;

            for v in verts {
                xmin = xmin.min(v[0]);
                ymin = ymin.min(v[1]);
                zmin = zmin.min(v[2]);
                xmax = xmax.max(v[0]);
                ymax = ymax.max(v[1]);
                zmax = zmax.max(v[2]);
            }

            let cx = ((xmax + xmin) / 2.) as f32;
            let cy = ((ymax + ymin) / 2.) as f32;
            let cz = ((zmax + zmin) / 2.) as f32;
            let sx = ((xmax - xmin) / 2.) as f32;
            let sy = ((ymax - ymin) / 2.) as f32;
            let sz = ((zmax - zmin) / 2.) as f32;

            // center the object and scale to max size 1 in any direction
            let scale = 1. / sx.max(sy).max(sz);
            let model = [[scale, 0.0, 0.0, 0.0],
                         [0.0, scale, 0.0, 0.0],
                         [0.0, 0.0, scale, 0.0],
                         [-scale * cx, -scale * cy, -scale * cz, 1.0f32]];

            let verts = {

                let verts: Vec<_> = verts.iter()
                    .zip(normals.iter())
                    .map(|(v, n)| {
                        Vertex {
                            position: [v[0] as f32, v[1] as f32, v[2] as f32],
                            color: [1.0, 0.0, 0.0],
                            normal: [n[0] as f32, n[1] as f32, n[2] as f32],
                        }
                    })
                    .collect();
                glium::VertexBuffer::new(facade, &verts).unwrap()
            };

            let indices = {
                let mut buf = Vec::with_capacity(faces.len() * 3);
                for i in faces {
                    buf.extend_from_slice(i);
                }
                glium::IndexBuffer::new(facade, glium::index::PrimitiveType::TrianglesList, &buf)
                    .unwrap()
            };

            let vertex_shader_src = r#"
        #version 150

        in vec3 position;
        in vec3 normal;

        out vec3 v_normal;
        out vec3 v_position;

        uniform mat4 perspective;
        uniform mat4 view;
        uniform mat4 model;

        void main() {
            mat4 modelview = view * model;
            v_normal = transpose(inverse(mat3(modelview))) * normal;
            gl_Position = perspective * modelview * vec4(position, 1.0);
            v_position = gl_Position.xyz / gl_Position.w;
        }
    "#;

            let fragment_shader_src = r#"
        #version 150

        in vec3 v_normal;
        in vec3 v_position;

        out vec4 color;

        uniform vec3 u_light;

        const vec3 ambient_color = vec3(0.2, 0.0, 0.0);
        const vec3 diffuse_color = vec3(0.6, 0.0, 0.0);
        const vec3 specular_color = vec3(1.0, 1.0, 1.0);

        void main() {
            float diffuse = max(dot(normalize(v_normal), normalize(u_light)), 0.0);

            vec3 camera_dir = normalize(-v_position);
            vec3 half_direction = normalize(normalize(u_light) + camera_dir);
            float specular = pow(max(dot(half_direction, normalize(v_normal)), 0.0), 16.0);

            color = vec4(ambient_color + diffuse * diffuse_color + specular * specular_color, 1.0);
        }
    "#;

            let program =
                glium::Program::from_source(facade, vertex_shader_src, fragment_shader_src, None)
                    .unwrap();

            let normal_vertex_shader_src = r#"
    #version 150
    in vec3 position;
    in vec3 normal;

    out Vertex
    {
      vec3 normal;
    } vertex;

    void main()
    {
      gl_Position = vec4(position, 1.0);
      vertex.normal = normal;
    }
    "#;

            let normal_geometry_shader_src = r#"
    #version 150
    layout(triangles) in;

    // Three lines will be generated: 6 vertices
    layout(line_strip, max_vertices=6) out;

    uniform float normal_length;
    uniform mat4 perspective;
    uniform mat4 view;
    uniform mat4 model;

    in Vertex
    {
      vec3 normal;
    } vertex[];

    out vec4 vertex_color;

    void main()
    {
      int i;
      mat4 matrix = perspective * view * model;
      for(i=0; i < gl_in.length(); i++)
      {
        vec3 p = gl_in[i].gl_Position.xyz;
        vec3 n = vertex[i].normal;

        gl_Position =  matrix * vec4(p, 1.0);
        EmitVertex();

        gl_Position = matrix * vec4(p + n * normal_length, 1.0);
        EmitVertex();

        EndPrimitive();
      }
    }
            "#;

            let normal_fragment_shader_src = r#"
        #version 150

        const vec4 normal_color = vec4(0.0, 1.0, 0.0, 1.0);
        out vec4 color;

        void main() {
            color = normal_color;
        }
    "#;

            let normal_program = glium::Program::from_source(facade,
                                                             normal_vertex_shader_src,
                                                             normal_fragment_shader_src,
                                                             Some(normal_geometry_shader_src))
                .unwrap();
            Surface {
                verts: verts,
                indices: indices,
                program: program,
                normal_program: normal_program,
                model: model,
            }
        }

        pub fn draw<S: glium::Surface>(&self,
                                       target: &mut S,
                                       camera: &support::camera::CameraState,
                                       wireframe: bool,
                                       normals: bool)
                                       -> Result<(), glium::DrawError> {
            let light = [1.4, 0.4, -0.7f32];

            let params = glium::DrawParameters {
                depth: glium::Depth {
                    test: glium::draw_parameters::DepthTest::IfLess,
                    write: true,
                    ..Default::default()
                },
                polygon_mode: if wireframe {
                    glium::draw_parameters::PolygonMode::Line
                } else {
                    glium::draw_parameters::PolygonMode::Fill
                },
                line_width: Some(3.),
                // backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockWise,
                ..Default::default()
            };

            try!(target.draw(&self.verts,
                             &self.indices,
                             &self.program,
                             &uniform! { model: self.model,
                                view: camera.get_view(),
                                perspective: camera.get_perspective(),
                                u_light: light },
                             &params));
            if normals {

                try!(target.draw(&self.verts,
                                 &self.indices,
                                 &self.normal_program,
                                 &uniform! { model: self.model,
                                view: camera.get_view(),
                                perspective: camera.get_perspective(),
                                normal_length: 1f32 },
                                 &params));
            }

            Ok(())

        }
    }

}

fn main() {
    let res = 3;
    let xs = Array::linspace(-0.5, 0.5, res);
    let ys = Array::linspace(-0.5, 0.5, res);
    let zs = Array::linspace(-0.5, 0.5, res);

    let dim = (xs.len(), ys.len(), zs.len());

    let u = {
        let mut u = Array::from_elem(dim, 0.);

        let r = 0.4;
        for ((i, j, k), u) in u.indexed_iter_mut() {
            let (x, y, z) = (xs[i], ys[j], zs[k]);
            *u = x * x + y * y + z * z - r * r;
        }
        u
    };

    let level = 0.0;
    let (verts, faces, normals) = marching_tetrahedra(u.as_slice().unwrap(), dim, level);

    println!("{:?}", verts);
    println!("{:?}", normals);

    use glium::{DisplayBuild, Surface};
    let display = glium::glutin::WindowBuilder::new()
        .with_depth_buffer(24)
        .build_glium()
        .unwrap();

    let surface = surface::Surface::new(&display, &verts, &faces, &normals);

    let mut camera = support::camera::CameraState::new();
    let mut wireframe = false;
    let mut show_normals = false;

    loop {
        camera.update();

        let mut target = display.draw();
        target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);

        surface.draw(&mut target, &camera, wireframe, show_normals).unwrap();
        target.finish().unwrap();

        for ev in display.poll_events() {
            match ev {
                glium::glutin::Event::Closed => return,
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _,
                                             Some(glutin::VirtualKeyCode::F)) => {
                    wireframe = !wireframe
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _,
                                             Some(glutin::VirtualKeyCode::N)) => {
                    show_normals = !show_normals
                }
                ev => camera.process_input(&ev),
            }
        }
    }
}
<commit_msg>Fix shading<commit_after>#[macro_use]
extern crate glium;
extern crate isosurface;
extern crate ndarray;

use ndarray::Array;
use isosurface::marching_tetrahedra;
use glium::glutin;

mod support;

mod surface {
    use glium;
    use glium::backend::Facade;
    use support;
    use std;

    #[derive(Copy, Clone)]
    struct Vertex {
        position: [f32; 3],
        color: [f32; 3],
        normal: [f32; 3],
    }

    implement_vertex!(Vertex, position, color, normal);

    pub struct Surface {
        verts: glium::VertexBuffer<Vertex>,
        indices: glium::IndexBuffer<u32>,
        program: glium::Program,
        normal_program: glium::Program,
        model: [[f32; 4]; 4],
    }

    impl Surface {
        pub fn new<F: Facade>(facade: &F,
                              verts: &[[f64; 3]],
                              faces: &[[u32; 3]],
                              normals: &[[f64; 3]])
                              -> Surface {

            // compute the bounding box
            let mut xmin = std::f64::MAX;
            let mut ymin = std::f64::MAX;
            let mut zmin = std::f64::MAX;
            let mut xmax = std::f64::MIN;
            let mut ymax = std::f64::MIN;
            let mut zmax = std::f64::MIN;

            for v in verts {
                xmin = xmin.min(v[0]);
                ymin = ymin.min(v[1]);
                zmin = zmin.min(v[2]);
                xmax = xmax.max(v[0]);
                ymax = ymax.max(v[1]);
                zmax = zmax.max(v[2]);
            }

            let cx = ((xmax + xmin) / 2.) as f32;
            let cy = ((ymax + ymin) / 2.) as f32;
            let cz = ((zmax + zmin) / 2.) as f32;
            let sx = ((xmax - xmin) / 2.) as f32;
            let sy = ((ymax - ymin) / 2.) as f32;
            let sz = ((zmax - zmin) / 2.) as f32;

            // center the object and scale to max size 1 in any direction
            let scale = 1. / sx.max(sy).max(sz);
            let model = [[scale, 0.0, 0.0, 0.0],
                         [0.0, scale, 0.0, 0.0],
                         [0.0, 0.0, scale, 0.0],
                         [-scale * cx, -scale * cy, -scale * cz, 1.0f32]];

            let verts = {

                let verts: Vec<_> = verts.iter()
                    .zip(normals.iter())
                    .map(|(v, n)| {
                        Vertex {
                            position: [v[0] as f32, v[1] as f32, v[2] as f32],
                            color: [1.0, 0.0, 0.0],
                            normal: [n[0] as f32, n[1] as f32, n[2] as f32],
                        }
                    })
                    .collect();
                glium::VertexBuffer::new(facade, &verts).unwrap()
            };

            let indices = {
                let mut buf = Vec::with_capacity(faces.len() * 3);
                for i in faces {
                    buf.extend_from_slice(i);
                }
                glium::IndexBuffer::new(facade, glium::index::PrimitiveType::TrianglesList, &buf)
                    .unwrap()
            };

            let vertex_shader_src = r#"
        #version 150

        in vec3 position;
        in vec3 normal;

        out vec3 v_normal;
        out vec3 v_position;

        uniform mat4 perspective;
        uniform mat4 view;
        uniform mat4 model;

        void main() {
            mat4 modelview = view * model;
            v_normal = transpose(inverse(mat3(modelview))) * normal;
            gl_Position = perspective * modelview * vec4(position, 1.0);
            v_position = gl_Position.xyz / gl_Position.w;
        }
    "#;

            let fragment_shader_src = r#"
        #version 150

        in vec3 v_normal;
        in vec3 v_position;

        out vec4 color;

        uniform vec3 u_light;

        const vec3 ambient_color = vec3(0.2, 0.0, 0.0);
        const vec3 diffuse_color = vec3(0.6, 0.0, 0.0);
        const vec3 specular_color = vec3(1.0, 1.0, 1.0);

        void main() {
            float diffuse = max(dot(normalize(v_normal), normalize(u_light)), 0.0);

            vec3 camera_dir = normalize(v_position);
            vec3 half_direction = normalize(normalize(u_light) + camera_dir);
            float specular = pow(max(dot(half_direction, normalize(v_normal)), 0.0), 16.0);

            color = vec4(ambient_color + diffuse * diffuse_color + specular * specular_color, 1.0);
        }
    "#;

            let program =
                glium::Program::from_source(facade, vertex_shader_src, fragment_shader_src, None)
                    .unwrap();

            let normal_vertex_shader_src = r#"
    #version 150
    in vec3 position;
    in vec3 normal;

    out Vertex
    {
      vec3 normal;
    } vertex;

    void main()
    {
      gl_Position = vec4(position, 1.0);
      vertex.normal = normal;
    }
    "#;

            let normal_geometry_shader_src = r#"
    #version 150
    layout(triangles) in;

    // Three lines will be generated: 6 vertices
    layout(line_strip, max_vertices=6) out;

    uniform float normal_length;
    uniform mat4 perspective;
    uniform mat4 view;
    uniform mat4 model;

    in Vertex
    {
      vec3 normal;
    } vertex[];

    out vec4 vertex_color;

    void main()
    {
      int i;
      mat4 matrix = perspective * view * model;
      for(i=0; i < gl_in.length(); i++)
      {
        vec3 p = gl_in[i].gl_Position.xyz;
        vec3 n = vertex[i].normal;

        gl_Position =  matrix * vec4(p, 1.0);
        EmitVertex();

        gl_Position = matrix * vec4(p + n * normal_length, 1.0);
        EmitVertex();

        EndPrimitive();
      }
    }
            "#;

            let normal_fragment_shader_src = r#"
        #version 150

        const vec4 normal_color = vec4(0.0, 1.0, 0.0, 1.0);
        out vec4 color;

        void main() {
            color = normal_color;
        }
    "#;

            let normal_program = glium::Program::from_source(facade,
                                                             normal_vertex_shader_src,
                                                             normal_fragment_shader_src,
                                                             Some(normal_geometry_shader_src))
                .unwrap();
            Surface {
                verts: verts,
                indices: indices,
                program: program,
                normal_program: normal_program,
                model: model,
            }
        }

        pub fn draw<S: glium::Surface>(&self,
                                       target: &mut S,
                                       camera: &support::camera::CameraState,
                                       wireframe: bool,
                                       normals: bool)
                                       -> Result<(), glium::DrawError> {
            let light = [1., 1., 1.0f32];
            // let light = [1.4, 0.4, -0.7f32];

            let params = glium::DrawParameters {
                depth: glium::Depth {
                    test: glium::draw_parameters::DepthTest::IfLess,
                    write: true,
                    ..Default::default()
                },
                polygon_mode: if wireframe {
                    glium::draw_parameters::PolygonMode::Line
                } else {
                    glium::draw_parameters::PolygonMode::Fill
                },
                line_width: Some(3.),
                // backface_culling: glium::draw_parameters::BackfaceCullingMode::CullClockWise,
                ..Default::default()
            };

            try!(target.draw(&self.verts,
                             &self.indices,
                             &self.program,
                             &uniform! { model: self.model,
                                view: camera.get_view(),
                                perspective: camera.get_perspective(),
                                u_light: light },
                             &params));
            if normals {

                try!(target.draw(&self.verts,
                                 &self.indices,
                                 &self.normal_program,
                                 &uniform! { model: self.model,
                                view: camera.get_view(),
                                perspective: camera.get_perspective(),
                                normal_length: 1f32 },
                                 &params));
            }

            Ok(())

        }
    }

}

fn main() {
    let res = 10;
    let xs = Array::linspace(-0.5, 0.5, res);
    let ys = Array::linspace(-0.5, 0.5, res);
    let zs = Array::linspace(-0.5, 0.5, res);

    let dim = (xs.len(), ys.len(), zs.len());

    let u = {
        let mut u = Array::from_elem(dim, 0.);

        let r = 0.4;
        for ((i, j, k), u) in u.indexed_iter_mut() {
            let (x, y, z) = (xs[i], ys[j], zs[k]);
            *u = x * x + y * y + z * z - r * r;
        }
        u
    };

    let level = 0.0;
    let (verts, faces, normals) = marching_tetrahedra(u.as_slice().unwrap(), dim, level);

    use glium::{DisplayBuild, Surface};
    let display = glium::glutin::WindowBuilder::new()
        .with_depth_buffer(24)
        .build_glium()
        .unwrap();

    let surface = surface::Surface::new(&display, &verts, &faces, &normals);

    let mut camera = support::camera::CameraState::new();
    let mut wireframe = false;
    let mut show_normals = false;

    loop {
        camera.update();

        let mut target = display.draw();
        target.clear_color_and_depth((0.0, 0.0, 0.0, 1.0), 1.0);

        surface.draw(&mut target, &camera, wireframe, show_normals).unwrap();
        target.finish().unwrap();

        for ev in display.poll_events() {
            match ev {
                glium::glutin::Event::Closed => return,
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _,
                                             Some(glutin::VirtualKeyCode::F)) => {
                    wireframe = !wireframe
                }
                glutin::Event::KeyboardInput(glutin::ElementState::Pressed,
                                             _,
                                             Some(glutin::VirtualKeyCode::N)) => {
                    show_normals = !show_normals
                }
                ev => camera.process_input(&ev),
            }
        }
    }
}
<|endoftext|>


<commit_before>//! OAuth 1
//!
//!# Example
//!
//! TODO
use std::default::Default;
use std::fmt;
use super::{SignatureMethod, AuthorizationHeader, generate_nonce, generate_timestamp};

impl Default for SignatureMethod {
    fn default() -> SignatureMethod {
        SignatureMethod::HMAC_SHA1}
}

impl fmt::String for SignatureMethod {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result{
        let out = match *self {
            SignatureMethod::HMAC_SHA1 => {"HMAC-SHA1"},
            SignatureMethod::RSA_SHA1  => {"RSA-SHA1"},
            SignatureMethod::PLAINTEXT => {"PLAINTEXT"}
        };
        write!(f, "{}", out)
    }
}

#[unstable]
pub struct Session<'a> {
    oauth_consumer_key : &'a str,
    oauth_token : &'a str,
    oauth_token_secret : &'a str,
    oauth_signature_method : SignatureMethod,
    oauth_signature : String,
    oauth_timestamp : String,
    oauth_nonce : String,
}

impl<'a> Session<'a> {
    // Creates a Session Object, which contains all reused parameters
    // for OAuth 1.0A. This is the Struct used to communicate with a server
    // TODO: Should we use options for oauth_nonce and oauth_timestamp?
    pub fn new (consumer_key: &'a str, token: &'a str, secret: &'a str,
                signature_method: SignatureMethod) -> Session<'a> {
        Session {
            oauth_consumer_key: consumer_key,
            oauth_token: token,
            oauth_token_secret : secret,
            oauth_signature_method: signature_method,
            oauth_signature: Default::default(),
            oauth_timestamp: Default::default(),
            oauth_nonce: Default::default(),
        }
    }
    // Returns a base string URI, ecnoded with [RFC3986]. This gets used to
    // generate the `oauth_signature`. I takes a different path dependent
    // on the signature type
    fn get_base_string(mut self, base_url: &'a str) -> String {
        if (self.oauth_signature_method == SignatureMethod::PLAINTEXT) {
            format!("{}&oauth_consumer_key={}&\
                    oauth_signature={}&oauth_signature_method={}&\
                    oauth_token={}&oauth_version=1.0",
                    base_url, self.oauth_consumer_key, self.oauth_signature,
                    self.oauth_signature_method, self.oauth_token)
        } else {
            self.oauth_nonce = generate_nonce();
            self.oauth_timestamp = generate_timestamp();
            format!("{}&oauth_consumer_key={}&oauth_nonce={}&\
                    oauth_signature={}&oauth_signature_method={}&\
                    oauth_timestamp={}&oauth_token={}&oauth_version=1.0",
                    base_url, self.oauth_consumer_key, self.oauth_nonce,
                    self.oauth_signature, self.oauth_signature_method,
                    self.oauth_timestamp, self.oauth_token)
        }
    }
    #[unimplemented]
    // this function will take API url and data and use that to send
    // an Oauth request. Data shouldn't need to be in alphabetical order
    // but will be in it's own data structure.
    fn request(&self, base_url: String) {

    }
}

// Creates a URL encoded String containing headers
// This should be called everytime you make a request, since the
// `oauth_timestamp` and `oauth_nonce` need to freshly made
impl<'a> AuthorizationHeader for Session<'a>{
    fn get_header(&self) -> String {
            let header = format!("Authorization: OAuth oauth_consumer_key=\"{}\", \
                            oauth_signature=\"{}\", oauth_signature_method=\"{}\", \
                            oauth_token=\"{}\", oauth_version=\"1.0\"",
                            self.oauth_consumer_key, self.oauth_signature,
                            self.oauth_signature_method, self.oauth_token);

            match self.oauth_signature_method {
                SignatureMethod::PLAINTEXT => header,
                _ => format!("{}, oauth_timestamp=\"{}\", oauth_nonce=\"{}\"",
                            header, generate_timestamp(), generate_nonce())
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate url;
    use super::Session;
    use super::super::{SignatureMethod, AuthorizationHeader};
    use self::url::percent_encoding::{utf8_percent_encode, FORM_URLENCODED_ENCODE_SET};

    // Session initialization and setup test
    #[test]
    fn hw() {
        let s = Session::new("k0azC44q2c0DgF7ua9YZ6Q",
                            "119544186-6YZKqkECA9Z0bxq9bA1vzzG7tfPotCml4oTySkzj",
                            "zvNmU9daj9V00118H9KQBozQQsZt4pyLQcZdc",
                            SignatureMethod::HMAC_SHA1);
        let base_string = s.get_base_string("https://api.twitter.com/1.1/statuses/user_timeline.json");
        println!("\n\n{}\n\n{}\n\n", base_string, utf8_percent_encode(base_string.as_slice(), FORM_URLENCODED_ENCODE_SET));
    }
}
<commit_msg>added a mutable data vector containing arguments for the OAuth request<commit_after>//! OAuth 1
//!
//!# Example
//!
//! TODO
use std::default::Default;
use std::fmt;
use super::{SignatureMethod, AuthorizationHeader, generate_nonce, generate_timestamp};

impl Default for SignatureMethod {
    fn default() -> SignatureMethod {
        SignatureMethod::HMAC_SHA1}
}

impl fmt::String for SignatureMethod {
    fn fmt(&self, f : &mut fmt::Formatter) -> fmt::Result{
        let out = match *self {
            SignatureMethod::HMAC_SHA1 => {"HMAC-SHA1"},
            SignatureMethod::RSA_SHA1  => {"RSA-SHA1"},
            SignatureMethod::PLAINTEXT => {"PLAINTEXT"}
        };
        write!(f, "{}", out)
    }
}

#[unstable]
pub struct Session<'a> {
    oauth_consumer_key : &'a str,
    oauth_token : &'a str,
    oauth_token_secret : &'a str,
    oauth_signature_method : SignatureMethod,
    oauth_signature : String,
    oauth_timestamp : String,
    oauth_nonce : String,
}

impl<'a> Session<'a> {
    // Creates a Session Object, which contains all reused parameters
    // for OAuth 1.0A. This is the Struct used to communicate with a server
    // TODO: Should we use options for oauth_nonce and oauth_timestamp?
    pub fn new (consumer_key: &'a str, token: &'a str, secret: &'a str,
                signature_method: SignatureMethod) -> Session<'a> {
        Session {
            oauth_consumer_key: consumer_key,
            oauth_token: token,
            oauth_token_secret : secret,
            oauth_signature_method: signature_method,
            oauth_signature: Default::default(),
            oauth_timestamp: Default::default(),
            oauth_nonce: Default::default(),
        }
    }
    // Returns a base string URI, ecnoded with [RFC3986]. This gets used to
    // generate the `oauth_signature`. I takes a different path dependent
    // on the signature type
    fn get_base_string(mut self, base_url: &'a str, mut data: Vec<(&str, &str)>) -> String {
        data.sort();
        println!("1: {} 2: {} 3: {}", data[0].1, data[1].1, data[2].1);
        if (self.oauth_signature_method == SignatureMethod::PLAINTEXT) {
            format!("{}&oauth_consumer_key={}&\
                    oauth_signature={}&oauth_signature_method={}&\
                    oauth_token={}&oauth_version=1.0",
                    base_url, self.oauth_consumer_key, self.oauth_signature,
                    self.oauth_signature_method, self.oauth_token)
        } else {
            self.oauth_nonce = generate_nonce();
            self.oauth_timestamp = generate_timestamp();
            format!("{}&oauth_consumer_key={}&oauth_nonce={}&\
                    oauth_signature={}&oauth_signature_method={}&\
                    oauth_timestamp={}&oauth_token={}&oauth_version=1.0",
                    base_url, self.oauth_consumer_key, self.oauth_nonce,
                    self.oauth_signature, self.oauth_signature_method,
                    self.oauth_timestamp, self.oauth_token)
        }
    }
    #[unimplemented]
    // this function will take API url and data and use that to send
    // an Oauth request. Data shouldn't need to be in alphabetical order
    // but will be in it's own data structure.
    fn request(&self, base_url: String) {

    }
}

// Creates a URL encoded String containing headers
// This should be called everytime you make a request, since the
// `oauth_timestamp` and `oauth_nonce` need to freshly made
impl<'a> AuthorizationHeader for Session<'a>{
    fn get_header(&self) -> String {
            let header = format!("Authorization: OAuth oauth_consumer_key=\"{}\", \
                            oauth_signature=\"{}\", oauth_signature_method=\"{}\", \
                            oauth_token=\"{}\", oauth_version=\"1.0\"",
                            self.oauth_consumer_key, self.oauth_signature,
                            self.oauth_signature_method, self.oauth_token);

            match self.oauth_signature_method {
                SignatureMethod::PLAINTEXT => header,
                _ => format!("{}, oauth_timestamp=\"{}\", oauth_nonce=\"{}\"",
                            header, self.oauth_timestamp, self.oauth_nonce)
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate url;
    use super::Session;
    use super::super::{SignatureMethod, AuthorizationHeader};
    use self::url::percent_encoding::{utf8_percent_encode, FORM_URLENCODED_ENCODE_SET};

    // Session initialization and setup test
    #[test]
    fn hw() {
        let s = Session::new("k0azC44q2c0DgF7ua9YZ6Q",
                            "119544186-6YZKqkECA9Z0bxq9bA1vzzG7tfPotCml4oTySkzj",
                            "zvNmU9daj9V00118H9KQBozQQsZt4pyLQcZdc",
                            SignatureMethod::HMAC_SHA1);
        let base_string = s.get_base_string("https://api.twitter.com/1.1/statuses/user_timeline.json",
                                            vec![("zzz", "third"), ("qqq", "second"), ("aaa", "first")]);
        println!("{}", utf8_percent_encode(base_string.as_slice(), FORM_URLENCODED_ENCODE_SET));
    }
}
<|endoftext|>


<commit_before>extern crate grains;

#[test]
fn square_one() {
    assert_eq!(grains::square(1), 1);
}

#[test]
#[ignore]
fn square_two() {
    assert_eq!(grains::square(2), 2);
}

#[test]
#[ignore]
fn square_three() {
    assert_eq!(grains::square(3), 4);
}

#[test]
#[ignore]
fn square_four() {
    assert_eq!(grains::square(4), 8);
}

#[test]
#[ignore]
fn square_sixteen() {
    assert_eq!(grains::square(16), 32_768);
}

#[test]
#[ignore]
fn square_thirty_two() {
    assert_eq!(grains::square(32), 2_147_483_648);
}

#[test]
#[ignore]
fn square_sixty_four() {
    assert_eq!(grains::square(64), 9_223_372_036_854_775_808);
}

#[test]
#[ignore]
#[should_panic(expected = "Square must be between 1 and 64")]
fn square_zero_panics() {
    grains::square(0);
}

#[test]
#[ignore]
#[should_panic(expected = "Square must be between 1 and 64")]
fn square_sixty_five_panics() {
    grains::square(65);
}

#[test]
#[ignore]
fn total_sums_all_squares() {
    assert_eq!(grains::total(), 18_446_744_073_709_551_615);
    grains::total();
}
<commit_msg>grains: Remove extra call to total in test (#320)<commit_after>extern crate grains;

#[test]
fn square_one() {
    assert_eq!(grains::square(1), 1);
}

#[test]
#[ignore]
fn square_two() {
    assert_eq!(grains::square(2), 2);
}

#[test]
#[ignore]
fn square_three() {
    assert_eq!(grains::square(3), 4);
}

#[test]
#[ignore]
fn square_four() {
    assert_eq!(grains::square(4), 8);
}

#[test]
#[ignore]
fn square_sixteen() {
    assert_eq!(grains::square(16), 32_768);
}

#[test]
#[ignore]
fn square_thirty_two() {
    assert_eq!(grains::square(32), 2_147_483_648);
}

#[test]
#[ignore]
fn square_sixty_four() {
    assert_eq!(grains::square(64), 9_223_372_036_854_775_808);
}

#[test]
#[ignore]
#[should_panic(expected = "Square must be between 1 and 64")]
fn square_zero_panics() {
    grains::square(0);
}

#[test]
#[ignore]
#[should_panic(expected = "Square must be between 1 and 64")]
fn square_sixty_five_panics() {
    grains::square(65);
}

#[test]
#[ignore]
fn total_sums_all_squares() {
    assert_eq!(grains::total(), 18_446_744_073_709_551_615);
}
<|endoftext|>