<commit_before>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::{fmt, num::NonZeroU32};

/// A small and `no_std` compatible error type
///
/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
/// if so, which error code the OS gave the application. If such an error is
/// encountered, please consult with your system documentation.
///
/// Internally this type is a NonZeroU32, with certain values reserved for
/// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
///
/// *If this crate's `"std"` Cargo feature is enabled*, then:
/// - [`getrandom::Error`][Error] implements
///   [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
///   [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html).
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(NonZeroU32);

const fn internal_error(n: u16) -> Error {
    // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
    let code = Error::INTERNAL_START + (n as u32);
    Error(unsafe { NonZeroU32::new_unchecked(code) })
}

impl Error {
    /// This target/platform is not supported by `getrandom`.
    pub const UNSUPPORTED: Error = internal_error(0);
    /// The platform-specific `errno` returned a non-positive value.
    pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
    /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed.
    pub const IOS_SEC_RANDOM: Error = internal_error(3);
    /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
    pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4);
    /// RDRAND instruction failed due to a hardware issue.
    pub const FAILED_RDRAND: Error = internal_error(5);
    /// RDRAND instruction unsupported on this target.
    pub const NO_RDRAND: Error = internal_error(6);
    /// The environment does not support the Web Crypto API.
    pub const WEB_CRYPTO: Error = internal_error(7);
    /// Calling Web Crypto API `crypto.getRandomValues` failed.
    pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8);
    /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
    pub const VXWORKS_RAND_SECURE: Error = internal_error(11);
    /// Node.js does not have the `crypto` CommonJS module.
    pub const NODE_CRYPTO: Error = internal_error(12);
    /// Calling Node.js function `crypto.randomFillSync` failed.
    pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
    /// Called from an ES module on Node.js. This is unsupported, see:
    /// https://docs.rs/getrandom#nodejs-es6-module-support.
    pub const NODE_ES_MODULE: Error = internal_error(14);

    /// Codes below this point represent OS Errors (i.e. positive i32 values).
    /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
    /// reserved for use by the `rand` and `getrandom` crates.
    pub const INTERNAL_START: u32 = 1 << 31;

    /// Codes at or above this point can be used by users to define their own
    /// custom errors.
    pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);

    /// Extract the raw OS error code (if this error came from the OS)
    ///
    /// This method is identical to [`std::io::Error::raw_os_error()`][1], except
    /// that it works in `no_std` contexts. If this method returns `None`, the
    /// error value can still be formatted via the `Display` implementation.
    ///
    /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
    #[inline]
    pub fn raw_os_error(self) -> Option<i32> {
        if self.0.get() < Self::INTERNAL_START {
            match () {
                #[cfg(target_os = "solid_asp3")]
                // On SOLID, negate the error code again to obtain the original
                // error code.
                () => Some(-(self.0.get() as i32)),
                #[cfg(not(target_os = "solid_asp3"))]
                () => Some(self.0.get() as i32),
            }
        } else {
            None
        }
    }

    /// Extract the bare error code.
    ///
    /// This code can either come from the underlying OS, or be a custom error.
    /// Use [`Error::raw_os_error()`] to disambiguate.
    #[inline]
    pub const fn code(self) -> NonZeroU32 {
        self.0
    }
}

cfg_if! {
    if #[cfg(unix)] {
        fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> {
            let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
            if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
                return None;
            }

            // Take up to trailing null byte
            let n = buf.len();
            let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
            core::str::from_utf8(&buf[..idx]).ok()
        }
    } else {
        fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
            None
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut dbg = f.debug_struct("Error");
        if let Some(errno) = self.raw_os_error() {
            dbg.field("os_error", &errno);
            let mut buf = [0u8; 128];
            if let Some(err) = os_err(errno, &mut buf) {
                dbg.field("description", &err);
            }
        } else if let Some(desc) = internal_desc(*self) {
            dbg.field("internal_code", &self.0.get());
            dbg.field("description", &desc);
        } else {
            dbg.field("unknown_code", &self.0.get());
        }
        dbg.finish()
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(errno) = self.raw_os_error() {
            let mut buf = [0u8; 128];
            match os_err(errno, &mut buf) {
                Some(err) => err.fmt(f),
                None => write!(f, "OS Error: {}", errno),
            }
        } else if let Some(desc) = internal_desc(*self) {
            f.write_str(desc)
        } else {
            write!(f, "Unknown Error: {}", self.0.get())
        }
    }
}

impl From<NonZeroU32> for Error {
    fn from(code: NonZeroU32) -> Self {
        Self(code)
    }
}

fn internal_desc(error: Error) -> Option<&'static str> {
    match error {
        Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
        Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
        Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
        Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
        Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
        Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
        Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"),
        Error::WEB_GET_RANDOM_VALUES => Some("Calling Web API crypto.getRandomValues failed"),
        Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
        Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"),
        Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"),
        Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es6-module-support"),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use super::Error;
    use core::mem::size_of;

    #[test]
    fn test_size() {
        assert_eq!(size_of::<Error>(), 4);
        assert_eq!(size_of::<Result<(), Error>>(), 4);
    }
}
<commit_msg>Fix link typo<commit_after>// Copyright 2018 Developers of the Rand project.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use core::{fmt, num::NonZeroU32};

/// A small and `no_std` compatible error type
///
/// The [`Error::raw_os_error()`] will indicate if the error is from the OS, and
/// if so, which error code the OS gave the application. If such an error is
/// encountered, please consult with your system documentation.
///
/// Internally this type is a NonZeroU32, with certain values reserved for
/// certain purposes, see [`Error::INTERNAL_START`] and [`Error::CUSTOM_START`].
///
/// *If this crate's `"std"` Cargo feature is enabled*, then:
/// - [`getrandom::Error`][Error] implements
///   [`std::error::Error`](https://doc.rust-lang.org/std/error/trait.Error.html)
/// - [`std::io::Error`](https://doc.rust-lang.org/std/io/struct.Error.html) implements
///   [`From<getrandom::Error>`](https://doc.rust-lang.org/std/convert/trait.From.html).
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Error(NonZeroU32);

const fn internal_error(n: u16) -> Error {
    // SAFETY: code > 0 as INTERNAL_START > 0 and adding n won't overflow a u32.
    let code = Error::INTERNAL_START + (n as u32);
    Error(unsafe { NonZeroU32::new_unchecked(code) })
}

impl Error {
    /// This target/platform is not supported by `getrandom`.
    pub const UNSUPPORTED: Error = internal_error(0);
    /// The platform-specific `errno` returned a non-positive value.
    pub const ERRNO_NOT_POSITIVE: Error = internal_error(1);
    /// Call to iOS [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes) failed.
    pub const IOS_SEC_RANDOM: Error = internal_error(3);
    /// Call to Windows [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom) failed.
    pub const WINDOWS_RTL_GEN_RANDOM: Error = internal_error(4);
    /// RDRAND instruction failed due to a hardware issue.
    pub const FAILED_RDRAND: Error = internal_error(5);
    /// RDRAND instruction unsupported on this target.
    pub const NO_RDRAND: Error = internal_error(6);
    /// The environment does not support the Web Crypto API.
    pub const WEB_CRYPTO: Error = internal_error(7);
    /// Calling Web Crypto API `crypto.getRandomValues` failed.
    pub const WEB_GET_RANDOM_VALUES: Error = internal_error(8);
    /// On VxWorks, call to `randSecure` failed (random number generator is not yet initialized).
    pub const VXWORKS_RAND_SECURE: Error = internal_error(11);
    /// Node.js does not have the `crypto` CommonJS module.
    pub const NODE_CRYPTO: Error = internal_error(12);
    /// Calling Node.js function `crypto.randomFillSync` failed.
    pub const NODE_RANDOM_FILL_SYNC: Error = internal_error(13);
    /// Called from an ES module on Node.js. This is unsupported, see:
    /// <https://docs.rs/getrandom#nodejs-es-module-support>.
    pub const NODE_ES_MODULE: Error = internal_error(14);

    /// Codes below this point represent OS Errors (i.e. positive i32 values).
    /// Codes at or above this point, but below [`Error::CUSTOM_START`] are
    /// reserved for use by the `rand` and `getrandom` crates.
    pub const INTERNAL_START: u32 = 1 << 31;

    /// Codes at or above this point can be used by users to define their own
    /// custom errors.
    pub const CUSTOM_START: u32 = (1 << 31) + (1 << 30);

    /// Extract the raw OS error code (if this error came from the OS)
    ///
    /// This method is identical to [`std::io::Error::raw_os_error()`][1], except
    /// that it works in `no_std` contexts. If this method returns `None`, the
    /// error value can still be formatted via the `Display` implementation.
    ///
    /// [1]: https://doc.rust-lang.org/std/io/struct.Error.html#method.raw_os_error
    #[inline]
    pub fn raw_os_error(self) -> Option<i32> {
        if self.0.get() < Self::INTERNAL_START {
            match () {
                #[cfg(target_os = "solid_asp3")]
                // On SOLID, negate the error code again to obtain the original
                // error code.
                () => Some(-(self.0.get() as i32)),
                #[cfg(not(target_os = "solid_asp3"))]
                () => Some(self.0.get() as i32),
            }
        } else {
            None
        }
    }

    /// Extract the bare error code.
    ///
    /// This code can either come from the underlying OS, or be a custom error.
    /// Use [`Error::raw_os_error()`] to disambiguate.
    #[inline]
    pub const fn code(self) -> NonZeroU32 {
        self.0
    }
}

cfg_if! {
    if #[cfg(unix)] {
        fn os_err(errno: i32, buf: &mut [u8]) -> Option<&str> {
            let buf_ptr = buf.as_mut_ptr() as *mut libc::c_char;
            if unsafe { libc::strerror_r(errno, buf_ptr, buf.len()) } != 0 {
                return None;
            }

            // Take up to trailing null byte
            let n = buf.len();
            let idx = buf.iter().position(|&b| b == 0).unwrap_or(n);
            core::str::from_utf8(&buf[..idx]).ok()
        }
    } else {
        fn os_err(_errno: i32, _buf: &mut [u8]) -> Option<&str> {
            None
        }
    }
}

impl fmt::Debug for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut dbg = f.debug_struct("Error");
        if let Some(errno) = self.raw_os_error() {
            dbg.field("os_error", &errno);
            let mut buf = [0u8; 128];
            if let Some(err) = os_err(errno, &mut buf) {
                dbg.field("description", &err);
            }
        } else if let Some(desc) = internal_desc(*self) {
            dbg.field("internal_code", &self.0.get());
            dbg.field("description", &desc);
        } else {
            dbg.field("unknown_code", &self.0.get());
        }
        dbg.finish()
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        if let Some(errno) = self.raw_os_error() {
            let mut buf = [0u8; 128];
            match os_err(errno, &mut buf) {
                Some(err) => err.fmt(f),
                None => write!(f, "OS Error: {}", errno),
            }
        } else if let Some(desc) = internal_desc(*self) {
            f.write_str(desc)
        } else {
            write!(f, "Unknown Error: {}", self.0.get())
        }
    }
}

impl From<NonZeroU32> for Error {
    fn from(code: NonZeroU32) -> Self {
        Self(code)
    }
}

fn internal_desc(error: Error) -> Option<&'static str> {
    match error {
        Error::UNSUPPORTED => Some("getrandom: this target is not supported"),
        Error::ERRNO_NOT_POSITIVE => Some("errno: did not return a positive value"),
        Error::IOS_SEC_RANDOM => Some("SecRandomCopyBytes: iOS Security framework failure"),
        Error::WINDOWS_RTL_GEN_RANDOM => Some("RtlGenRandom: Windows system function failure"),
        Error::FAILED_RDRAND => Some("RDRAND: failed multiple times: CPU issue likely"),
        Error::NO_RDRAND => Some("RDRAND: instruction not supported"),
        Error::WEB_CRYPTO => Some("Web Crypto API is unavailable"),
        Error::WEB_GET_RANDOM_VALUES => Some("Calling Web API crypto.getRandomValues failed"),
        Error::VXWORKS_RAND_SECURE => Some("randSecure: VxWorks RNG module is not initialized"),
        Error::NODE_CRYPTO => Some("Node.js crypto CommonJS module is unavailable"),
        Error::NODE_RANDOM_FILL_SYNC => Some("Calling Node.js API crypto.randomFillSync failed"),
        Error::NODE_ES_MODULE => Some("Node.js ES modules are not directly supported, see https://docs.rs/getrandom#nodejs-es-module-support"),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use super::Error;
    use core::mem::size_of;

    #[test]
    fn test_size() {
        assert_eq!(size_of::<Error>(), 4);
        assert_eq!(size_of::<Result<(), Error>>(), 4);
    }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};

use ignore::Match;
use ignore::gitignore::{Gitignore, GitignoreBuilder};
use walkdir::{WalkDir, DirEntry, WalkDirIterator};

use error::Result;

pub struct FilesBuilder {
    root_dir: PathBuf,
    ignore: GitignoreBuilder,
}

impl FilesBuilder {
    pub fn new(root_dir: &Path) -> Result<FilesBuilder> {
        let mut ignore = GitignoreBuilder::new(root_dir);
        ignore.add_line(None, "**/.*")?;
        ignore.add_line(None, "**/.*/**")?;
        ignore.add_line(None, "**/_*")?;
        ignore.add_line(None, "**/_*/**")?;
        let builder = FilesBuilder {
            root_dir: root_dir.to_path_buf(),
            ignore: ignore,
        };

        Ok(builder)
    }

    pub fn add_ignore(&mut self, line: &str) -> Result<&mut FilesBuilder> {
        debug!("{:?}: adding '{}' ignore pattern", self.root_dir, line);
        self.ignore.add_line(None, line)?;
        Ok(self)
    }

    pub fn build(&self) -> Result<Files> {
        let files = Files::new(self.root_dir.as_path(), self.ignore.build()?);
        Ok(files)
    }
}

pub struct FilesIterator<'a> {
    inner: Box<Iterator<Item = PathBuf> + 'a>,
}

impl<'a> FilesIterator<'a> {
    fn new(files: &'a Files) -> FilesIterator<'a> {
        let walker = WalkDir::new(files.root_dir.as_path())
            .min_depth(1)
            .follow_links(false)
            .into_iter()
            .filter_entry(move |e| files.includes_entry(e))
            .filter_map(|e| e.ok())
            .filter(|e| e.file_type().is_file())
            .filter_map(move |e| {
                            e.path()
                                .strip_prefix(files.root_dir.as_path())
                                .ok()
                                .map(|p| p.to_path_buf())
                        });
        FilesIterator { inner: Box::new(walker) }
    }
}

impl<'a> Iterator for FilesIterator<'a> {
    type Item = PathBuf;

    fn next(&mut self) -> Option<PathBuf> {
        self.inner.next()
    }
}

pub struct Files {
    root_dir: PathBuf,
    ignore: Gitignore,
}

impl Files {
    fn new(root_dir: &Path, ignore: Gitignore) -> Files {
        Files {
            root_dir: root_dir.to_path_buf(),
            ignore: ignore,
        }
    }

    #[cfg(test)]
    pub fn includes_file(&self, file: &Path) -> bool {
        let is_dir = false;
        self.includes_path(file, is_dir)
    }

    #[cfg(test)]
    pub fn includes_dir(&self, dir: &Path) -> bool {
        let is_dir = true;
        self.includes_path(dir, is_dir)
    }

    pub fn files(&self) -> FilesIterator {
        FilesIterator::new(self)
    }

    fn includes_entry(&self, entry: &DirEntry) -> bool {
        self.includes_path(entry.path(), entry.file_type().is_dir())
    }

    fn includes_path(&self, path: &Path, is_dir: bool) -> bool {
        match self.ignore.matched(path, is_dir) {
            Match::None => true,
            Match::Ignore(glob) => {
                debug!("{:?}: ignored {:?}", path, glob.original());
                false
            }
            Match::Whitelist(glob) => {
                debug!("{:?}: allowed {:?}", path, glob.original());
                true
            }
        }
    }
}

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

    macro_rules! assert_includes_dir {
        ($root: expr, $ignores: expr, $test: expr, $included: expr) => {
            let mut files = FilesBuilder::new(Path::new($root)).unwrap();
            let ignores: &[&str] = $ignores;
            for ignore in ignores {
                files.add_ignore(ignore).unwrap();
            }
            let files = files.build().unwrap();
            assert_eq!(files.includes_dir(Path::new($test)), $included);
        }
    }
    macro_rules! assert_includes_file {
        ($root: expr, $ignores: expr, $test: expr, $included: expr) => {
            let mut files = FilesBuilder::new(Path::new($root)).unwrap();
            let ignores: &[&str] = $ignores;
            for ignore in ignores {
                files.add_ignore(ignore).unwrap();
            }
            let files = files.build().unwrap();
            assert_eq!(files.includes_file(Path::new($test)), $included);
        }
    }

    #[test]
    fn files_includes_root_dir() {
        assert_includes_dir!("/usr/cobalt/site", &[], "/usr/cobalt/site", true);
    }

    #[test]
    fn files_includes_child_dir() {
        assert_includes_dir!("/usr/cobalt/site", &[], "/usr/cobalt/site/child", true);
    }

    #[test]
    fn files_excludes_hidden_dir() {
        assert_includes_dir!("/usr/cobalt/site", &[], "/usr/cobalt/site/_child", false);
        assert_includes_dir!("/usr/cobalt/site",
                             &[],
                             "/usr/cobalt/site/child/_child",
                             false);
        assert_includes_dir!("/usr/cobalt/site",
                             &[],
                             "/usr/cobalt/site/_child/child",
                             false);
    }

    #[test]
    fn files_excludes_dot_dir() {
        assert_includes_dir!("/usr/cobalt/site", &[], "/usr/cobalt/site/.child", false);
        assert_includes_dir!("/usr/cobalt/site",
                             &[],
                             "/usr/cobalt/site/child/.child",
                             false);
        assert_includes_dir!("/usr/cobalt/site",
                             &[],
                             "/usr/cobalt/site/.child/child",
                             false);
    }

    #[test]
    fn files_includes_file() {
        assert_includes_file!("/usr/cobalt/site", &[], "/usr/cobalt/site/child.txt", true);
    }

    #[test]
    fn files_includes_child_dir_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/child.txt",
                              true);
    }

    #[test]
    fn files_excludes_hidden_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/_child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/_child.txt",
                              false);
    }

    #[test]
    fn files_excludes_hidden_dir_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/_child/child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/_child/child.txt",
                              false);
    }

    #[test]
    fn files_excludes_dot_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/.child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/.child.txt",
                              false);
    }

    #[test]
    fn files_excludes_dot_dir_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/.child/child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/.child/child.txt",
                              false);
    }

    #[test]
    fn files_excludes_ignored_file() {
        let ignores = &["README", "**/*.scss"];

        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/README",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/README",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/blog.scss",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/blog.scss",
                              false);
    }

    #[test]
    fn files_includes_overriden_file() {
        let ignores = &["!.htaccess"];

        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/.htaccess",
                              true);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/.htaccess",
                              true);
    }

    #[test]
    fn files_includes_overriden_dir() {
        let ignores = &["!_posts", "!_posts/**", "_posts/**/_*", "_posts/**/_*/**"];

        assert_includes_dir!("/usr/cobalt/site", ignores, "/usr/cobalt/site/_posts", true);
        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/_posts/child",
                             true);

        // TODO These two cases should instead fail
        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/child/_posts",
                             true);
        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/child/_posts/child",
                             true);

        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/_posts/child/_child",
                             false);
        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/_posts/child/_child/child",
                             false);
    }

    #[test]
    fn files_includes_overriden_dir_file() {
        let ignores = &["!_posts", "!_posts/**", "_posts/**/_*", "_posts/**/_*/**"];

        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/_posts/child.txt",
                              true);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/_posts/child/child.txt",
                              true);

        // TODO These two cases should instead fail
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/_posts/child.txt",
                              true);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/_posts/child/child.txt",
                              true);

        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/_posts/child/_child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/_posts/child/_child/child.txt",
                              false);
    }

    #[test]
    fn files_iter_matches_include() {
        let root_dir = Path::new("tests/fixtures/hidden_files");
        let files = FilesBuilder::new(root_dir).unwrap().build().unwrap();
        let mut actual: Vec<_> = files.files().collect();
        actual.sort();

        let expected = vec![Path::new("child/child.txt").to_path_buf(),
                            Path::new("child.txt").to_path_buf()];

        assert_eq!(expected, actual);
    }
}
<commit_msg>tests(files): Test relative path inclusion / exclusion<commit_after>use std::path::{Path, PathBuf};

use ignore::Match;
use ignore::gitignore::{Gitignore, GitignoreBuilder};
use walkdir::{WalkDir, DirEntry, WalkDirIterator};

use error::Result;

pub struct FilesBuilder {
    root_dir: PathBuf,
    ignore: GitignoreBuilder,
}

impl FilesBuilder {
    pub fn new(root_dir: &Path) -> Result<FilesBuilder> {
        let mut ignore = GitignoreBuilder::new(root_dir);
        ignore.add_line(None, "**/.*")?;
        ignore.add_line(None, "**/.*/**")?;
        ignore.add_line(None, "**/_*")?;
        ignore.add_line(None, "**/_*/**")?;
        let builder = FilesBuilder {
            root_dir: root_dir.to_path_buf(),
            ignore: ignore,
        };

        Ok(builder)
    }

    pub fn add_ignore(&mut self, line: &str) -> Result<&mut FilesBuilder> {
        debug!("{:?}: adding '{}' ignore pattern", self.root_dir, line);
        self.ignore.add_line(None, line)?;
        Ok(self)
    }

    pub fn build(&self) -> Result<Files> {
        let files = Files::new(self.root_dir.as_path(), self.ignore.build()?);
        Ok(files)
    }
}

pub struct FilesIterator<'a> {
    inner: Box<Iterator<Item = PathBuf> + 'a>,
}

impl<'a> FilesIterator<'a> {
    fn new(files: &'a Files) -> FilesIterator<'a> {
        let walker = WalkDir::new(files.root_dir.as_path())
            .min_depth(1)
            .follow_links(false)
            .into_iter()
            .filter_entry(move |e| files.includes_entry(e))
            .filter_map(|e| e.ok())
            .filter(|e| e.file_type().is_file())
            .filter_map(move |e| {
                            e.path()
                                .strip_prefix(files.root_dir.as_path())
                                .ok()
                                .map(|p| p.to_path_buf())
                        });
        FilesIterator { inner: Box::new(walker) }
    }
}

impl<'a> Iterator for FilesIterator<'a> {
    type Item = PathBuf;

    fn next(&mut self) -> Option<PathBuf> {
        self.inner.next()
    }
}

pub struct Files {
    root_dir: PathBuf,
    ignore: Gitignore,
}

impl Files {
    fn new(root_dir: &Path, ignore: Gitignore) -> Files {
        Files {
            root_dir: root_dir.to_path_buf(),
            ignore: ignore,
        }
    }

    #[cfg(test)]
    pub fn includes_file(&self, file: &Path) -> bool {
        let is_dir = false;
        self.includes_path(file, is_dir)
    }

    #[cfg(test)]
    pub fn includes_dir(&self, dir: &Path) -> bool {
        let is_dir = true;
        self.includes_path(dir, is_dir)
    }

    pub fn files(&self) -> FilesIterator {
        FilesIterator::new(self)
    }

    fn includes_entry(&self, entry: &DirEntry) -> bool {
        self.includes_path(entry.path(), entry.file_type().is_dir())
    }

    fn includes_path(&self, path: &Path, is_dir: bool) -> bool {
        match self.ignore.matched(path, is_dir) {
            Match::None => true,
            Match::Ignore(glob) => {
                debug!("{:?}: ignored {:?}", path, glob.original());
                false
            }
            Match::Whitelist(glob) => {
                debug!("{:?}: allowed {:?}", path, glob.original());
                true
            }
        }
    }
}

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

    macro_rules! assert_includes_dir {
        ($root: expr, $ignores: expr, $test: expr, $included: expr) => {
            let mut files = FilesBuilder::new(Path::new($root)).unwrap();
            let ignores: &[&str] = $ignores;
            for ignore in ignores {
                files.add_ignore(ignore).unwrap();
            }
            let files = files.build().unwrap();
            assert_eq!(files.includes_dir(Path::new($test)), $included);
        }
    }
    macro_rules! assert_includes_file {
        ($root: expr, $ignores: expr, $test: expr, $included: expr) => {
            let mut files = FilesBuilder::new(Path::new($root)).unwrap();
            let ignores: &[&str] = $ignores;
            for ignore in ignores {
                files.add_ignore(ignore).unwrap();
            }
            let files = files.build().unwrap();
            assert_eq!(files.includes_file(Path::new($test)), $included);
        }
    }

    #[test]
    fn files_includes_root_dir() {
        assert_includes_dir!("/usr/cobalt/site", &[], "/usr/cobalt/site", true);

        assert_includes_dir!("./", &[], "./", true);
    }

    #[test]
    fn files_includes_child_dir() {
        assert_includes_dir!("/usr/cobalt/site", &[], "/usr/cobalt/site/child", true);

        assert_includes_dir!("./", &[], "./child", true);
    }

    #[test]
    fn files_excludes_hidden_dir() {
        assert_includes_dir!("/usr/cobalt/site", &[], "/usr/cobalt/site/_child", false);
        assert_includes_dir!("/usr/cobalt/site",
                             &[],
                             "/usr/cobalt/site/child/_child",
                             false);
        assert_includes_dir!("/usr/cobalt/site",
                             &[],
                             "/usr/cobalt/site/_child/child",
                             false);

        assert_includes_dir!("./", &[], "./_child", false);
        assert_includes_dir!("./", &[], "./child/_child", false);
        assert_includes_dir!("./", &[], "./_child/child", false);
    }

    #[test]
    fn files_excludes_dot_dir() {
        assert_includes_dir!("/usr/cobalt/site", &[], "/usr/cobalt/site/.child", false);
        assert_includes_dir!("/usr/cobalt/site",
                             &[],
                             "/usr/cobalt/site/child/.child",
                             false);
        assert_includes_dir!("/usr/cobalt/site",
                             &[],
                             "/usr/cobalt/site/.child/child",
                             false);

        assert_includes_dir!("./", &[], "./.child", false);
        assert_includes_dir!("./", &[], "./child/.child", false);
        assert_includes_dir!("./", &[], "./.child/child", false);
    }

    #[test]
    fn files_includes_file() {
        assert_includes_file!("/usr/cobalt/site", &[], "/usr/cobalt/site/child.txt", true);

        assert_includes_file!("./", &[], "./child.txt", true);
    }

    #[test]
    fn files_includes_child_dir_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/child.txt",
                              true);

        assert_includes_file!("./", &[], "./child/child.txt", true);
    }

    #[test]
    fn files_excludes_hidden_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/_child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/_child.txt",
                              false);

        assert_includes_file!("./", &[], "./_child.txt", false);
        assert_includes_file!("./", &[], "./child/_child.txt", false);
    }

    #[test]
    fn files_excludes_hidden_dir_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/_child/child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/_child/child.txt",
                              false);

        assert_includes_file!("./", &[], "./_child/child.txt", false);
        assert_includes_file!("./", &[], "./child/_child/child.txt", false);
    }

    #[test]
    fn files_excludes_dot_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/.child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/.child.txt",
                              false);

        assert_includes_file!("./", &[], "./.child.txt", false);
        assert_includes_file!("./", &[], "./child/.child.txt", false);
    }

    #[test]
    fn files_excludes_dot_dir_file() {
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/.child/child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              &[],
                              "/usr/cobalt/site/child/.child/child.txt",
                              false);

        assert_includes_file!("./", &[], "./.child/child.txt", false);
        assert_includes_file!("./", &[], "./child/.child/child.txt", false);
    }

    #[test]
    fn files_excludes_ignored_file() {
        let ignores = &["README", "**/*.scss"];

        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/README",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/README",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/blog.scss",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/blog.scss",
                              false);

        assert_includes_file!("./", ignores, "./README", false);
        assert_includes_file!("./", ignores, "./child/README", false);
        assert_includes_file!("./", ignores, "./blog.scss", false);
        assert_includes_file!("./", ignores, "./child/blog.scss", false);
    }

    #[test]
    fn files_includes_overriden_file() {
        let ignores = &["!.htaccess"];

        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/.htaccess",
                              true);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/.htaccess",
                              true);

        assert_includes_file!("./", ignores, "./.htaccess", true);
        assert_includes_file!("./", ignores, "./child/.htaccess", true);
    }

    #[test]
    fn files_includes_overriden_dir() {
        let ignores = &["!_posts", "!_posts/**", "_posts/**/_*", "_posts/**/_*/**"];

        assert_includes_dir!("/usr/cobalt/site", ignores, "/usr/cobalt/site/_posts", true);
        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/_posts/child",
                             true);

        // TODO These two cases should instead fail
        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/child/_posts",
                             true);
        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/child/_posts/child",
                             true);

        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/_posts/child/_child",
                             false);
        assert_includes_dir!("/usr/cobalt/site",
                             ignores,
                             "/usr/cobalt/site/_posts/child/_child/child",
                             false);

        assert_includes_dir!("./", ignores, "./_posts", true);
        assert_includes_dir!("./", ignores, "./_posts/child", true);

        // TODO These two cases should instead fail
        assert_includes_dir!("./", ignores, "./child/_posts", true);
        assert_includes_dir!("./", ignores, "./child/_posts/child", true);

        assert_includes_dir!("./", ignores, "./_posts/child/_child", false);
        assert_includes_dir!("./", ignores, "./_posts/child/_child/child", false);
    }

    #[test]
    fn files_includes_overriden_dir_file() {
        let ignores = &["!_posts", "!_posts/**", "_posts/**/_*", "_posts/**/_*/**"];

        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/_posts/child.txt",
                              true);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/_posts/child/child.txt",
                              true);

        // TODO These two cases should instead fail
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/_posts/child.txt",
                              true);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/child/_posts/child/child.txt",
                              true);

        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/_posts/child/_child.txt",
                              false);
        assert_includes_file!("/usr/cobalt/site",
                              ignores,
                              "/usr/cobalt/site/_posts/child/_child/child.txt",
                              false);

        assert_includes_file!("./", ignores, "./_posts/child.txt", true);
        assert_includes_file!("./", ignores, "./_posts/child/child.txt", true);

        // TODO These two cases should instead fail
        assert_includes_file!("./", ignores, "./child/_posts/child.txt", true);
        assert_includes_file!("./", ignores, "./child/_posts/child/child.txt", true);

        assert_includes_file!("./", ignores, "./_posts/child/_child.txt", false);
        assert_includes_file!("./", ignores, "./_posts/child/_child/child.txt", false);
    }

    #[test]
    fn files_iter_matches_include() {
        let root_dir = Path::new("tests/fixtures/hidden_files");
        let files = FilesBuilder::new(root_dir).unwrap().build().unwrap();
        let mut actual: Vec<_> = files.files().collect();
        actual.sort();

        let expected = vec![Path::new("child/child.txt").to_path_buf(),
                            Path::new("child.txt").to_path_buf()];

        assert_eq!(expected, actual);
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2019, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use arg_enum_proc_macro::ArgEnum;
use num_derive::FromPrimitive;

/// Sample position for subsampled chroma
#[derive(Copy, Clone, Debug, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum ChromaSamplePosition {
  /// The source video transfer function must be signaled
  /// outside the AV1 bitstream.
  Unknown,
  /// Horizontally co-located with (0, 0) luma sample, vertically positioned
  /// in the middle between two luma samples.
  Vertical,
  /// Co-located with (0, 0) luma sample.
  Colocated,
}

impl Default for ChromaSamplePosition {
  fn default() -> Self {
    ChromaSamplePosition::Unknown
  }
}

/// Chroma subsampling format
#[derive(Copy, Clone, Debug, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum ChromaSampling {
  /// Both vertically and horizontally subsampled.
  Cs420,
  /// Horizontally subsampled.
  Cs422,
  /// Not subsampled.
  Cs444,
  /// Monochrome.
  Cs400,
}

impl Default for ChromaSampling {
  fn default() -> Self {
    ChromaSampling::Cs420
  }
}

impl ChromaSampling {
  /// Provides the amount to right shift the luma plane dimensions to get the
  ///  chroma plane dimensions.
  /// Only values 0 or 1 are ever returned.
  /// The plane dimensions must also be rounded up to accommodate odd luma plane
  ///  sizes.
  /// Cs400 returns None, as there are no chroma planes.
  pub fn get_decimation(self) -> Option<(usize, usize)> {
    use self::ChromaSampling::*;
    match self {
      Cs420 => Some((1, 1)),
      Cs422 => Some((1, 0)),
      Cs444 => Some((0, 0)),
      Cs400 => None,
    }
  }

  /// Calculates the size of a chroma plane for this sampling type, given the luma plane dimensions.
  pub fn get_chroma_dimensions(
    self, luma_width: usize, luma_height: usize,
  ) -> (usize, usize) {
    if let Some((ss_x, ss_y)) = self.get_decimation() {
      ((luma_width + ss_x) >> ss_x, (luma_height + ss_y) >> ss_y)
    } else {
      (0, 0)
    }
  }
}

/// Supported Color Primaries
///
/// As defined by “Color primaries” section of ISO/IEC 23091-4/ITU-T H.273
#[derive(ArgEnum, Debug, Clone, Copy, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum ColorPrimaries {
  /// BT.709
  BT709 = 1,
  /// Unspecified, must be signaled or inferred outside of the bitstream
  Unspecified,
  /// BT.470 System M (historical)
  BT470M = 4,
  /// BT.470 System B, G (historical)
  BT470BG,
  /// BT.601-7 525 (SMPTE 170 M)
  BT601,
  /// SMPTE 240M (historical)
  SMPTE240,
  /// Generic film
  GenericFilm,
  /// BT.2020, BT.2100
  BT2020,
  /// SMPTE 248 (CIE 1921 XYZ)
  XYZ,
  /// SMPTE RP 431-2
  SMPTE431,
  /// SMPTE EG 432-1
  SMPTE432,
  /// EBU Tech. 3213-E
  EBU3213 = 22,
}

impl Default for ColorPrimaries {
  fn default() -> Self {
    ColorPrimaries::Unspecified
  }
}

/// Supported Transfer Characteristics
///
/// As defined by “Transfer characteristics” section of ISO/IEC 23091-4/ITU-TH.273.
#[derive(ArgEnum, Debug, Clone, Copy, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum TransferCharacteristics {
  /// BT.709
  BT709 = 1,
  /// Unspecified, must be signaled or inferred outside of the bitstream
  Unspecified,
  /// BT.470 System M (historical)
  BT470M = 4,
  /// BT.470 System B, G (historical)
  BT470BG,
  /// BT.601-7 525 (SMPTE 170 M)
  BT601,
  /// SMPTE 240 M
  SMPTE240,
  /// Linear
  Linear,
  /// Logarithmic (100:1 range)
  Log100,
  /// Logarithmic ((100 * √10):1 range)
  Log100Sqrt10,
  /// IEC 61966-2-4
  IEC61966,
  /// BT.1361 extended color gamut system (historical)
  BT1361,
  /// sRGB or sYCC
  SRGB,
  /// BT.2020 10-bit systems
  BT2020_10Bit,
  /// BT.2020 12-bit systems
  BT2020_12Bit,
  /// SMPTE ST 2084, ITU BT.2100 PQ
  SMPTE2084,
  /// SMPTE ST 428
  SMPTE428,
  /// BT.2100 HLG (Hybrid Log Gamma), ARIB STD-B67
  HLG,
}

impl Default for TransferCharacteristics {
  fn default() -> Self {
    TransferCharacteristics::Unspecified
  }
}

/// Matrix coefficients
///
/// As defined by the “Matrix coefficients” section of ISO/IEC 23091-4/ITU-TH.273.
#[derive(ArgEnum, Debug, Clone, Copy, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum MatrixCoefficients {
  /// Identity matrix
  Identity = 0,
  /// BT.709
  BT709,
  /// Unspecified, must be signaled or inferred outside of the bitstream.
  Unspecified,
  /// US FCC 73.628
  FCC = 4,
  /// BT.470 System B, G (historical)
  BT470BG,
  /// BT.601-7 525 (SMPTE 170 M)
  BT601,
  /// SMPTE 240 M
  SMPTE240,
  /// YCgCo
  YCgCo,
  /// BT.2020 non-constant luminance, BT.2100 YCbCr
  BT2020NCL,
  /// BT.2020 constant luminance
  BT2020CL,
  /// SMPTE ST 2085 YDzDx
  SMPTE2085,
  /// Chromaticity-derived non-constant luminance
  ChromatNCL,
  /// Chromaticity-derived constant luminance
  ChromatCL,
  /// BT.2020 ICtCp
  ICtCp,
}

impl Default for MatrixCoefficients {
  fn default() -> Self {
    MatrixCoefficients::Unspecified
  }
}

/// Signal the content color description
#[derive(Copy, Clone, Debug)]
pub struct ColorDescription {
  pub color_primaries: ColorPrimaries,
  pub transfer_characteristics: TransferCharacteristics,
  pub matrix_coefficients: MatrixCoefficients,
}

/// Allowed pixel value range
///
/// C.f. VideoFullRangeFlag variable specified in ISO/IEC 23091-4/ITU-T H.273
#[derive(ArgEnum, Debug, Clone, Copy, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum PixelRange {
  /// Studio swing representation
  Limited,
  /// Full swing representation
  Full,
}

impl Default for PixelRange {
  fn default() -> Self {
    PixelRange::Limited
  }
}

/// High dynamic range content light level
///
/// As defined by CEA-861.3, Appendix A.
#[derive(Copy, Clone, Debug)]
pub struct ContentLight {
  /// Maximum content light level
  pub max_content_light_level: u16,
  /// Maximum frame-average light level
  pub max_frame_average_light_level: u16,
}

/// Chromaticity coordinates expressed as 0.16 fixed-point values
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Point {
  pub x: u16,
  pub y: u16,
}

/// High dynamic range mastering display color volume
///
/// As defined by CIE 1931
#[derive(Copy, Clone, Debug)]
pub struct MasteringDisplay {
  /// Chromaticity coordinates in Red, Green, Blue order
  /// expressed as 0.16 fixed-point
  pub primaries: [Point; 3],
  /// Chromaticity coordinates expressed as 0.16 fixed-point
  pub white_point: Point,
  /// 24.8 fixed-point maximum luminance in candelas per square meter
  pub max_luminance: u32,
  /// 18.14 fixed-point minimum luminance in candelas per square meter
  pub min_luminance: u32,
}
<commit_msg>Document ColorDescription fields<commit_after>// Copyright (c) 2019, The rav1e contributors. All rights reserved
//
// This source code is subject to the terms of the BSD 2 Clause License and
// the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
// was not distributed with this source code in the LICENSE file, you can
// obtain it at www.aomedia.org/license/software. If the Alliance for Open
// Media Patent License 1.0 was not distributed with this source code in the
// PATENTS file, you can obtain it at www.aomedia.org/license/patent.

use arg_enum_proc_macro::ArgEnum;
use num_derive::FromPrimitive;

/// Sample position for subsampled chroma
#[derive(Copy, Clone, Debug, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum ChromaSamplePosition {
  /// The source video transfer function must be signaled
  /// outside the AV1 bitstream.
  Unknown,
  /// Horizontally co-located with (0, 0) luma sample, vertically positioned
  /// in the middle between two luma samples.
  Vertical,
  /// Co-located with (0, 0) luma sample.
  Colocated,
}

impl Default for ChromaSamplePosition {
  fn default() -> Self {
    ChromaSamplePosition::Unknown
  }
}

/// Chroma subsampling format
#[derive(Copy, Clone, Debug, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum ChromaSampling {
  /// Both vertically and horizontally subsampled.
  Cs420,
  /// Horizontally subsampled.
  Cs422,
  /// Not subsampled.
  Cs444,
  /// Monochrome.
  Cs400,
}

impl Default for ChromaSampling {
  fn default() -> Self {
    ChromaSampling::Cs420
  }
}

impl ChromaSampling {
  /// Provides the amount to right shift the luma plane dimensions to get the
  ///  chroma plane dimensions.
  /// Only values 0 or 1 are ever returned.
  /// The plane dimensions must also be rounded up to accommodate odd luma plane
  ///  sizes.
  /// Cs400 returns None, as there are no chroma planes.
  pub fn get_decimation(self) -> Option<(usize, usize)> {
    use self::ChromaSampling::*;
    match self {
      Cs420 => Some((1, 1)),
      Cs422 => Some((1, 0)),
      Cs444 => Some((0, 0)),
      Cs400 => None,
    }
  }

  /// Calculates the size of a chroma plane for this sampling type, given the luma plane dimensions.
  pub fn get_chroma_dimensions(
    self, luma_width: usize, luma_height: usize,
  ) -> (usize, usize) {
    if let Some((ss_x, ss_y)) = self.get_decimation() {
      ((luma_width + ss_x) >> ss_x, (luma_height + ss_y) >> ss_y)
    } else {
      (0, 0)
    }
  }
}

/// Supported Color Primaries
///
/// As defined by “Color primaries” section of ISO/IEC 23091-4/ITU-T H.273
#[derive(ArgEnum, Debug, Clone, Copy, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum ColorPrimaries {
  /// BT.709
  BT709 = 1,
  /// Unspecified, must be signaled or inferred outside of the bitstream
  Unspecified,
  /// BT.470 System M (historical)
  BT470M = 4,
  /// BT.470 System B, G (historical)
  BT470BG,
  /// BT.601-7 525 (SMPTE 170 M)
  BT601,
  /// SMPTE 240M (historical)
  SMPTE240,
  /// Generic film
  GenericFilm,
  /// BT.2020, BT.2100
  BT2020,
  /// SMPTE 248 (CIE 1921 XYZ)
  XYZ,
  /// SMPTE RP 431-2
  SMPTE431,
  /// SMPTE EG 432-1
  SMPTE432,
  /// EBU Tech. 3213-E
  EBU3213 = 22,
}

impl Default for ColorPrimaries {
  fn default() -> Self {
    ColorPrimaries::Unspecified
  }
}

/// Supported Transfer Characteristics
///
/// As defined by “Transfer characteristics” section of ISO/IEC 23091-4/ITU-TH.273.
#[derive(ArgEnum, Debug, Clone, Copy, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum TransferCharacteristics {
  /// BT.709
  BT709 = 1,
  /// Unspecified, must be signaled or inferred outside of the bitstream
  Unspecified,
  /// BT.470 System M (historical)
  BT470M = 4,
  /// BT.470 System B, G (historical)
  BT470BG,
  /// BT.601-7 525 (SMPTE 170 M)
  BT601,
  /// SMPTE 240 M
  SMPTE240,
  /// Linear
  Linear,
  /// Logarithmic (100:1 range)
  Log100,
  /// Logarithmic ((100 * √10):1 range)
  Log100Sqrt10,
  /// IEC 61966-2-4
  IEC61966,
  /// BT.1361 extended color gamut system (historical)
  BT1361,
  /// sRGB or sYCC
  SRGB,
  /// BT.2020 10-bit systems
  BT2020_10Bit,
  /// BT.2020 12-bit systems
  BT2020_12Bit,
  /// SMPTE ST 2084, ITU BT.2100 PQ
  SMPTE2084,
  /// SMPTE ST 428
  SMPTE428,
  /// BT.2100 HLG (Hybrid Log Gamma), ARIB STD-B67
  HLG,
}

impl Default for TransferCharacteristics {
  fn default() -> Self {
    TransferCharacteristics::Unspecified
  }
}

/// Matrix coefficients
///
/// As defined by the “Matrix coefficients” section of ISO/IEC 23091-4/ITU-TH.273.
#[derive(ArgEnum, Debug, Clone, Copy, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum MatrixCoefficients {
  /// Identity matrix
  Identity = 0,
  /// BT.709
  BT709,
  /// Unspecified, must be signaled or inferred outside of the bitstream.
  Unspecified,
  /// US FCC 73.628
  FCC = 4,
  /// BT.470 System B, G (historical)
  BT470BG,
  /// BT.601-7 525 (SMPTE 170 M)
  BT601,
  /// SMPTE 240 M
  SMPTE240,
  /// YCgCo
  YCgCo,
  /// BT.2020 non-constant luminance, BT.2100 YCbCr
  BT2020NCL,
  /// BT.2020 constant luminance
  BT2020CL,
  /// SMPTE ST 2085 YDzDx
  SMPTE2085,
  /// Chromaticity-derived non-constant luminance
  ChromatNCL,
  /// Chromaticity-derived constant luminance
  ChromatCL,
  /// BT.2020 ICtCp
  ICtCp,
}

impl Default for MatrixCoefficients {
  fn default() -> Self {
    MatrixCoefficients::Unspecified
  }
}

/// Signal the content color description
#[derive(Copy, Clone, Debug)]
pub struct ColorDescription {
  /// Color primaries.
  pub color_primaries: ColorPrimaries,
  /// Transfer charasteristics.
  pub transfer_characteristics: TransferCharacteristics,
  /// Matrix coefficients.
  pub matrix_coefficients: MatrixCoefficients,
}

/// Allowed pixel value range
///
/// C.f. VideoFullRangeFlag variable specified in ISO/IEC 23091-4/ITU-T H.273
#[derive(ArgEnum, Debug, Clone, Copy, PartialEq, FromPrimitive)]
#[repr(C)]
pub enum PixelRange {
  /// Studio swing representation
  Limited,
  /// Full swing representation
  Full,
}

impl Default for PixelRange {
  fn default() -> Self {
    PixelRange::Limited
  }
}

/// High dynamic range content light level
///
/// As defined by CEA-861.3, Appendix A.
#[derive(Copy, Clone, Debug)]
pub struct ContentLight {
  /// Maximum content light level
  pub max_content_light_level: u16,
  /// Maximum frame-average light level
  pub max_frame_average_light_level: u16,
}

/// Chromaticity coordinates expressed as 0.16 fixed-point values
#[derive(Clone, Copy, Debug)]
#[repr(C)]
pub struct Point {
  pub x: u16,
  pub y: u16,
}

/// High dynamic range mastering display color volume
///
/// As defined by CIE 1931
#[derive(Copy, Clone, Debug)]
pub struct MasteringDisplay {
  /// Chromaticity coordinates in Red, Green, Blue order
  /// expressed as 0.16 fixed-point
  pub primaries: [Point; 3],
  /// Chromaticity coordinates expressed as 0.16 fixed-point
  pub white_point: Point,
  /// 24.8 fixed-point maximum luminance in candelas per square meter
  pub max_luminance: u32,
  /// 18.14 fixed-point minimum luminance in candelas per square meter
  pub min_luminance: u32,
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

use regex::Regex;

#[repr(u32)]
#[derive(Debug, Clone)]
pub enum SubToken {
    Plus,
    Minus,
    Multiply,
    Divide,

    Number,

    LParen,
    RParen,

    End
}

#[derive(Debug, Clone)]
pub struct Token {
    kind: SubToken,
    value: Option<f64>
}

impl Token {
    pub fn new(kind: SubToken, value: Option<f64>) -> Self {
        Token{kind: kind, value: value}
    }
    pub fn set_kind(&mut self, kind: SubToken) {
        self.kind = kind;
    }
    pub fn set_value(&mut self, value: Option<f64>) {
        self.value = value;
    }
    pub fn get_kind(&self) -> SubToken {
        self.kind.clone()
    }
    pub fn get_value(&self) -> Option<f64> {
        self.value.clone()
    }
}

#[derive(Debug)]
pub struct Lexer {
    input: String,
    return_previous_token: bool,
    previous_token: Option<Token>
}

impl Lexer {
    pub fn new(input: String) -> Self {
        Lexer{input: input, return_previous_token: false, previous_token: None}
    }
    pub fn get_next_token(&mut self) -> Result<Token, String> {
        if self.return_previous_token {
            self.return_previous_token = false;
            return match self.previous_token.clone() {
                Some(t) => Ok(t),
                None => Err(String::from("No previous token"))
            }
        }
        let mut strip_input = strip_white_space(self.input.clone());
        lazy_static! {
            static ref NEG_RE: Regex = Regex::new(r"\A-\d+(\.\d+)?").unwrap();
            static ref PLUS_RE: Regex = Regex::new(r"\A\+").unwrap();
            static ref MINUS_RE: Regex = Regex::new(r"\A-").unwrap();
            static ref MULT_RE: Regex = Regex::new(r"\A\*").unwrap();
            static ref DIV_RE: Regex = Regex::new(r"\A/").unwrap();
            static ref NUM_RE: Regex = Regex::new(r"\A\d+(\.\d+)?").unwrap();
            static ref LPAREN_RE: Regex = Regex::new(r"\A\(").unwrap();
            static ref RPAREN_RE: Regex = Regex::new(r"\A\)").unwrap();
        }
        let token: Token;
        let temp = &(strip_input.clone())[..];
        if PLUS_RE.is_match(temp) {
            strip_input = PLUS_RE.replace(temp, "");
            token = Token::new(SubToken::Plus, None);
        } else if MINUS_RE.is_match(temp) {
            strip_input = MINUS_RE.replace(temp, "");
            token = Token::new(SubToken::Minus, None);
        } else if MULT_RE.is_match(temp) {
            strip_input = MULT_RE.replace(temp, "");
            token = Token::new(SubToken::Multiply, None);
        } else if DIV_RE.is_match(temp) {
            strip_input = DIV_RE.replace(temp, "");
            token = Token::new(SubToken::Divide, None);
        } else if NEG_RE.is_match(temp) {
            use std::str::FromStr;
            let value: Option<f64> = f64::from_str(NEG_RE.captures(temp).unwrap().at(0).unwrap()).ok();
            strip_input = NEG_RE.replace(temp, "");
            token = Token::new(SubToken::Number, value);
        } else if NUM_RE.is_match(temp) {
            use std::str::FromStr;
            let value: Option<f64> = f64::from_str(NUM_RE.captures(temp).unwrap().at(0).unwrap()).ok();
            strip_input = NUM_RE.replace(temp, "");
            token = Token::new(SubToken::Number, value);
        } else if LPAREN_RE.is_match(temp) {
            strip_input = LPAREN_RE.replace(temp, "");
            token = Token::new(SubToken::LParen, None);
        } else if RPAREN_RE.is_match(temp) {
            strip_input = RPAREN_RE.replace(temp, "");
            token = Token::new(SubToken::RParen, None);
        } else if strip_input.is_empty() {
            token = Token::new(SubToken::End, None);
        } else {
            let mut error: String = String::from("Unknown token: ");
            error.push_str(temp);
            return Err(error);
        }
        self.input = strip_input;
        self.previous_token = Some(token.clone());
        println!("{:?}", token);
        Ok(token)
    }
    pub fn revert(&mut self) {
        self.return_previous_token = true;
    }
}

fn strip_white_space(input: String) -> String {
    input.split_whitespace().collect::<Vec<&str>>().join("")
}
<commit_msg>Removed println<commit_after>#![allow(dead_code)]

use regex::Regex;

#[repr(u32)]
#[derive(Debug, Clone)]
pub enum SubToken {
    Plus,
    Minus,
    Multiply,
    Divide,

    Number,

    LParen,
    RParen,

    End
}

#[derive(Debug, Clone)]
pub struct Token {
    kind: SubToken,
    value: Option<f64>
}

impl Token {
    pub fn new(kind: SubToken, value: Option<f64>) -> Self {
        Token{kind: kind, value: value}
    }
    pub fn set_kind(&mut self, kind: SubToken) {
        self.kind = kind;
    }
    pub fn set_value(&mut self, value: Option<f64>) {
        self.value = value;
    }
    pub fn get_kind(&self) -> SubToken {
        self.kind.clone()
    }
    pub fn get_value(&self) -> Option<f64> {
        self.value.clone()
    }
}

#[derive(Debug)]
pub struct Lexer {
    input: String,
    return_previous_token: bool,
    previous_token: Option<Token>
}

impl Lexer {
    pub fn new(input: String) -> Self {
        Lexer{input: input, return_previous_token: false, previous_token: None}
    }
    pub fn get_next_token(&mut self) -> Result<Token, String> {
        if self.return_previous_token {
            self.return_previous_token = false;
            return match self.previous_token.clone() {
                Some(t) => Ok(t),
                None => Err(String::from("No previous token"))
            }
        }
        let mut strip_input = strip_white_space(self.input.clone());
        lazy_static! {
            static ref NEG_RE: Regex = Regex::new(r"\A-\d+(\.\d+)?").unwrap();
            static ref PLUS_RE: Regex = Regex::new(r"\A\+").unwrap();
            static ref MINUS_RE: Regex = Regex::new(r"\A-").unwrap();
            static ref MULT_RE: Regex = Regex::new(r"\A\*").unwrap();
            static ref DIV_RE: Regex = Regex::new(r"\A/").unwrap();
            static ref NUM_RE: Regex = Regex::new(r"\A\d+(\.\d+)?").unwrap();
            static ref LPAREN_RE: Regex = Regex::new(r"\A\(").unwrap();
            static ref RPAREN_RE: Regex = Regex::new(r"\A\)").unwrap();
        }
        let token: Token;
        let temp = &(strip_input.clone())[..];
        if PLUS_RE.is_match(temp) {
            strip_input = PLUS_RE.replace(temp, "");
            token = Token::new(SubToken::Plus, None);
        } else if MINUS_RE.is_match(temp) {
            strip_input = MINUS_RE.replace(temp, "");
            token = Token::new(SubToken::Minus, None);
        } else if MULT_RE.is_match(temp) {
            strip_input = MULT_RE.replace(temp, "");
            token = Token::new(SubToken::Multiply, None);
        } else if DIV_RE.is_match(temp) {
            strip_input = DIV_RE.replace(temp, "");
            token = Token::new(SubToken::Divide, None);
        } else if NEG_RE.is_match(temp) {
            use std::str::FromStr;
            let value: Option<f64> = f64::from_str(NEG_RE.captures(temp).unwrap().at(0).unwrap()).ok();
            strip_input = NEG_RE.replace(temp, "");
            token = Token::new(SubToken::Number, value);
        } else if NUM_RE.is_match(temp) {
            use std::str::FromStr;
            let value: Option<f64> = f64::from_str(NUM_RE.captures(temp).unwrap().at(0).unwrap()).ok();
            strip_input = NUM_RE.replace(temp, "");
            token = Token::new(SubToken::Number, value);
        } else if LPAREN_RE.is_match(temp) {
            strip_input = LPAREN_RE.replace(temp, "");
            token = Token::new(SubToken::LParen, None);
        } else if RPAREN_RE.is_match(temp) {
            strip_input = RPAREN_RE.replace(temp, "");
            token = Token::new(SubToken::RParen, None);
        } else if strip_input.is_empty() {
            token = Token::new(SubToken::End, None);
        } else {
            let mut error: String = String::from("Unknown token: ");
            error.push_str(temp);
            return Err(error);
        }
        self.input = strip_input;
        self.previous_token = Some(token.clone());
        Ok(token)
    }
    pub fn revert(&mut self) {
        self.return_previous_token = true;
    }
}

fn strip_white_space(input: String) -> String {
    input.split_whitespace().collect::<Vec<&str>>().join("")
}
<|endoftext|>


<commit_before>pub struct Model {
    pub board: Board,
    pub turn: Turn,
    pub white_pieces: u32,
    pub white_hexes: u32,
    pub black_pieces: u32,
    pub black_hexes: u32,
    pub selected_piece: Option<FieldCoord>,
}

impl Model {
    pub fn init() -> Model {
        Model {
            board: Board::new(),
            turn: Turn::White,
            white_pieces: 18,
            white_hexes: 0,
            black_pieces: 18,
            black_hexes: 0,
            selected_piece: None,
        }
    }
}

pub enum Turn {
    White,
    Black,
}

pub struct Board {
    // The hex board uses an axial coordinate system with (0, 0) at the center. The x-axis slopes
    // up to the right, and the y-axis goes up and down. The board is stored as a dense 2D array,
    // as a ragged array won't work (since each row would have to be a different type).
    board: [[Option<Hex>; 5]; 5],
}

// Fields are numbered clockwise from the top. Even indicies are black, odd indicies are white.
type Hex = [Field; 6];

// There's no need to store the color of the piece on this field, since only white pieces can be on
// white fields, and vice versa.
#[derive(Clone, Copy)]
pub enum Field {
    Piece,
    Empty,
}

impl Board {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    /// Create a new board with the "Laurentius" starting position.
    pub fn new() -> Board {
        let mut board = Board {
            board: [[None; 5]; 5]
        };

        // (0, 0) is the only empty hex
        board.set_hex(&HexCoord::new(0, 0), Some([Field::Empty; 6]));

        // Conveniently, every other hex has exactly two pieces on it in the starting position.
        let piece_locations = [
            (-2,  2, 0, 4),
            (-2,  1, 0, 3),
            (-2,  0, 3, 5),
            (-1,  2, 1, 4),
            (-1,  1, 0, 4),
            (-1,  0, 3, 5),
            (-1, -1, 2, 5),
            ( 0,  2, 1, 5),
            ( 0,  1, 1, 5),
            ( 0, -1, 2, 4),
            ( 0, -2, 2, 4),
            ( 1,  1, 2, 5),
            ( 1,  0, 0, 2),
            ( 1, -1, 1, 3),
            ( 1, -2, 1, 4),
            ( 2,  0, 0, 2),
            ( 2, -1, 0, 3),
            ( 2, -2, 1, 3),
        ];

        for &(x, y, f1, f2) in &piece_locations {
            let mut hex = [Field::Empty; 6];
            hex[f1] = Field::Piece;
            hex[f2] = Field::Piece;
            board.set_hex(&HexCoord::new(x, y), Some(hex));
        }

        board
    }

    pub fn get_field(&self, coord: &FieldCoord) -> &Field {
        match *self.get_hex(&coord.to_hex()) {
            Some(ref hex) => &hex[coord.f as usize],
            None => panic!("Tried to get field on removed hex: {:?}", coord),
        }
    }
    /// Return fields that share an edge with the given field. These fields are always the opposite
    /// color of the given field. If all of a piece's edge neighbors are occupied, that piece might
    /// be capturable.
    pub fn get_field_edge_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![
            // There are always two edge neighbors on the same hex as the given field
            FieldCoord {
                f: (coord.f + 1) % 6,
                ..*coord
            },
            FieldCoord {
                f: (coord.f + 5) % 6,
                ..*coord
            },
        ];

        for (i, neighbor) in coord.to_hex().get_neighbors() {
            if coord.f == i && self.get_hex(&neighbor).is_some() {
                let f = (i + 3) % 6;
                neighbors.push(neighbor.to_field(f));
                break;
            }
        }
        neighbors
    }
    /// Return fields that share a vertex with the given field and have the same color as the given
    /// field. Pieces can move to fields that are vertex neighbors of the field they are on.
    pub fn get_field_vertex_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        // A field's vertex neighbors can be defined as the edge neighbors of its edge neighbors
        let mut neighbors = vec![];
        for neighbor in self.get_field_edge_neighbors(coord) {
            neighbors.append(&mut self.get_field_edge_neighbors(&neighbor));
        }
        // A field is not its own neighbor
        neighbors.into_iter().filter(|n| n != coord).collect()
    }
    pub fn move_piece(&mut self, from: &FieldCoord, to: &FieldCoord) {
        unimplemented!();
    }
    pub fn remove_piece(&mut self, coord: &FieldCoord) {
        unimplemented!();
    }

    fn get_hex(&self, coord: &HexCoord) -> &Option<Hex> {
        &self.board[(coord.x + 2) as usize][(coord.y + 2) as usize]
    }
    fn set_hex(&mut self, coord: &HexCoord, hex: Option<Hex>) {
        self.board[(coord.x + 2) as usize][(coord.y + 2) as usize] = hex;
    }
    pub fn get_hex_neighbors(&self, coord: &HexCoord) -> Vec<HexCoord> {
        coord
            .get_neighbors()
            .iter()
            .filter_map(|&(_, c)| match *self.get_hex(&c) {
                Some(_) => Some(c),
                None => None,
            })
            .collect()
    }
    /// Return fields that share an edge with the given hex and are outside of the given hex. If a
    /// hex is removed from the board, pieces occupying that hex's field neighbors might be
    /// capturable.
    pub fn get_hex_field_neighbors(&self, coord: &HexCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![];

        for (i, neighbor) in coord.get_neighbors() {
            if self.get_hex(&neighbor).is_some() {
                let f = (i + 3) % 6;
                neighbors.push(neighbor.to_field(f));
            }
        }
        neighbors
    }
    /// A hex is removable (and must be removed) if it is empty and is "attached to the board by 3
    /// or less adjacent sides."
    pub fn is_hex_removable(&self, coord: &HexCoord) -> bool {
        match *self.get_hex(coord) {
            Some(hex) => {
                if hex.iter().any(|&f| match f {
                    Field::Piece => true,
                    Field::Empty => false,
                }) {
                    return false;
                }
            }
            None => {
                panic!(
                    "Can't call Board::is_hex_removable on a removed hex: {:?}",
                    coord
                )
            }
        }

        let neighbor_idxs: Vec<u32> = coord.get_neighbors().iter().map(|&(i, _)| i).collect();
        let neighbor_idxs_slice = neighbor_idxs.as_slice();

        match neighbor_idxs_slice.len() {
            0 => panic!("A hex at {:?} is empty and has no neighbors", coord),
            1 => true,
            2 => {
                let valid_idx_combos = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [0, 5]];
                valid_idx_combos.iter().any(|c| c == neighbor_idxs_slice)
            }
            3 => {
                let valid_idx_combos = [
                    [0, 1, 2],
                    [1, 2, 3],
                    [2, 3, 4],
                    [3, 4, 5],
                    [0, 4, 5],
                    [0, 1, 5],
                ];
                valid_idx_combos.iter().any(|c| c == neighbor_idxs_slice)
            }
            _ => false,
        }
    }
    pub fn remove_hex(&mut self, coord: &HexCoord) {
        assert!(
            self.is_hex_removable(coord),
            "Tried to remove non-removable hex at {:?}",
            coord
        );
        self.set_hex(coord, None);
    }
}

#[derive(Debug, PartialEq)]
pub struct FieldCoord {
    x: i32,
    y: i32,
    f: u32,
}

#[derive(Clone, Copy, Debug)]
pub struct HexCoord {
    x: i32,
    y: i32,
}

impl FieldCoord {
    pub fn new(x: i32, y: i32, f: u32) -> FieldCoord {
        assert!((x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2 && f < 6);
        FieldCoord { x, y, f }
    }
    pub fn to_hex(&self) -> HexCoord {
        HexCoord::new(self.x, self.y)
    }
}

impl HexCoord {
    pub fn new(x: i32, y: i32) -> HexCoord {
        assert!((x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2);
        HexCoord { x, y }
    }
    pub fn to_field(&self, f: u32) -> FieldCoord {
        FieldCoord::new(self.x, self.y, f)
    }
    // We return a Vec of tuples so that get_hex_field_neighbors and is_hex_removable know which
    // neighbors are on which side of the hex. They need to know this for different reasons:
    //   * get_hex_field_neighbors: the index of each neighboring field depends on which hex
    //                              neighbor that field neighbor is on
    //   * is_hex_removable: a hex is removable only if it is "attached to the board by 3 or less
    //                       adjacent sides"
    fn get_neighbors(&self) -> Vec<(u32, HexCoord)> {
        let mut neighbors = vec![];

        if self.y < 2 && (self.x + self.y) != 2 {
            neighbors.push((0, HexCoord::new(self.x, self.y + 1)));
        }
        if (self.x + self.y) != 2 && self.x < 2 {
            neighbors.push((1, HexCoord::new(self.x + 1, self.y)));
        }
        if self.x < 2 && self.y > -2 {
            neighbors.push((2, HexCoord::new(self.x + 1, self.y - 1)));
        }
        if self.y > -2 && (self.x + self.y) > -2 {
            neighbors.push((3, HexCoord::new(self.x, self.y - 1)));
        }
        if (self.x + self.y) > -2 && self.x > -2 {
            neighbors.push((4, HexCoord::new(self.x - 1, self.y)));
        }
        if self.x > -2 && self.y < 2 {
            neighbors.push((5, HexCoord::new(self.x - 1, self.y + 1)));
        }

        neighbors
    }
}
<commit_msg>Implement remove_piece<commit_after>pub struct Model {
    pub board: Board,
    pub turn: Turn,
    pub white_pieces: u32,
    pub white_hexes: u32,
    pub black_pieces: u32,
    pub black_hexes: u32,
    pub selected_piece: Option<FieldCoord>,
}

impl Model {
    pub fn init() -> Model {
        Model {
            board: Board::new(),
            turn: Turn::White,
            white_pieces: 18,
            white_hexes: 0,
            black_pieces: 18,
            black_hexes: 0,
            selected_piece: None,
        }
    }
}

pub enum Turn {
    White,
    Black,
}

pub struct Board {
    // The hex board uses an axial coordinate system with (0, 0) at the center. The x-axis slopes
    // up to the right, and the y-axis goes up and down. The board is stored as a dense 2D array,
    // as a ragged array won't work (since each row would have to be a different type).
    board: [[Option<Hex>; 5]; 5],
}

// Fields are numbered clockwise from the top. Even indicies are black, odd indicies are white.
type Hex = [Field; 6];

// There's no need to store the color of the piece on this field, since only white pieces can be on
// white fields, and vice versa.
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Field {
    Piece,
    Empty,
}

impl Board {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    /// Create a new board with the "Laurentius" starting position.
    pub fn new() -> Board {
        let mut board = Board {
            board: [[None; 5]; 5]
        };

        // (0, 0) is the only empty hex
        board.set_hex(&HexCoord::new(0, 0), Some([Field::Empty; 6]));

        // Conveniently, every other hex has exactly two pieces on it in the starting position.
        let piece_locations = [
            (-2,  2, 0, 4),
            (-2,  1, 0, 3),
            (-2,  0, 3, 5),
            (-1,  2, 1, 4),
            (-1,  1, 0, 4),
            (-1,  0, 3, 5),
            (-1, -1, 2, 5),
            ( 0,  2, 1, 5),
            ( 0,  1, 1, 5),
            ( 0, -1, 2, 4),
            ( 0, -2, 2, 4),
            ( 1,  1, 2, 5),
            ( 1,  0, 0, 2),
            ( 1, -1, 1, 3),
            ( 1, -2, 1, 4),
            ( 2,  0, 0, 2),
            ( 2, -1, 0, 3),
            ( 2, -2, 1, 3),
        ];

        for &(x, y, f1, f2) in &piece_locations {
            let mut hex = [Field::Empty; 6];
            hex[f1] = Field::Piece;
            hex[f2] = Field::Piece;
            board.set_hex(&HexCoord::new(x, y), Some(hex));
        }

        board
    }

    pub fn get_field(&self, coord: &FieldCoord) -> &Field {
        match *self.get_hex(&coord.to_hex()) {
            Some(ref hex) => &hex[coord.f as usize],
            None => panic!("Tried to get field on removed hex: {:?}", coord),
        }
    }
    fn set_field(&mut self, coord: &FieldCoord, field: Field) {
        match *self.get_hex(&coord.to_hex()) {
            Some(mut hex) => hex[coord.f as usize] = field,
            None => panic!("Tried to set field on removed hex: {:?}", coord),
        }
    }
    /// Return fields that share an edge with the given field. These fields are always the opposite
    /// color of the given field. If all of a piece's edge neighbors are occupied, that piece might
    /// be capturable.
    pub fn get_field_edge_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![
            // There are always two edge neighbors on the same hex as the given field
            FieldCoord {
                f: (coord.f + 1) % 6,
                ..*coord
            },
            FieldCoord {
                f: (coord.f + 5) % 6,
                ..*coord
            },
        ];

        for (i, neighbor) in coord.to_hex().get_neighbors() {
            if coord.f == i && self.get_hex(&neighbor).is_some() {
                let f = (i + 3) % 6;
                neighbors.push(neighbor.to_field(f));
                break;
            }
        }
        neighbors
    }
    /// Return fields that share a vertex with the given field and have the same color as the given
    /// field. Pieces can move to fields that are vertex neighbors of the field they are on.
    pub fn get_field_vertex_neighbors(&self, coord: &FieldCoord) -> Vec<FieldCoord> {
        // A field's vertex neighbors can be defined as the edge neighbors of its edge neighbors
        let mut neighbors = vec![];
        for neighbor in self.get_field_edge_neighbors(coord) {
            neighbors.append(&mut self.get_field_edge_neighbors(&neighbor));
        }
        // A field is not its own neighbor
        neighbors.into_iter().filter(|n| n != coord).collect()
    }
    pub fn move_piece(&mut self, from: &FieldCoord, to: &FieldCoord) {
        unimplemented!();
    }
    pub fn remove_piece(&mut self, coord: &FieldCoord) {
        assert_eq!(
            self.get_field(coord),
            &Field::Piece,
            "There is no piece at {:?} to remove",
            coord
        );
        self.set_field(coord, Field::Empty);
    }

    fn get_hex(&self, coord: &HexCoord) -> &Option<Hex> {
        &self.board[(coord.x + 2) as usize][(coord.y + 2) as usize]
    }
    fn set_hex(&mut self, coord: &HexCoord, hex: Option<Hex>) {
        self.board[(coord.x + 2) as usize][(coord.y + 2) as usize] = hex;
    }
    pub fn get_hex_neighbors(&self, coord: &HexCoord) -> Vec<HexCoord> {
        coord
            .get_neighbors()
            .iter()
            .filter_map(|&(_, c)| match *self.get_hex(&c) {
                Some(_) => Some(c),
                None => None,
            })
            .collect()
    }
    /// Return fields that share an edge with the given hex and are outside of the given hex. If a
    /// hex is removed from the board, pieces occupying that hex's field neighbors might be
    /// capturable.
    pub fn get_hex_field_neighbors(&self, coord: &HexCoord) -> Vec<FieldCoord> {
        let mut neighbors = vec![];

        for (i, neighbor) in coord.get_neighbors() {
            if self.get_hex(&neighbor).is_some() {
                let f = (i + 3) % 6;
                neighbors.push(neighbor.to_field(f));
            }
        }
        neighbors
    }
    /// A hex is removable (and must be removed) if it is empty and is "attached to the board by 3
    /// or less adjacent sides."
    pub fn is_hex_removable(&self, coord: &HexCoord) -> bool {
        match *self.get_hex(coord) {
            Some(hex) => {
                if hex.iter().any(|&f| match f {
                    Field::Piece => true,
                    Field::Empty => false,
                }) {
                    return false;
                }
            }
            None => {
                panic!(
                    "Can't call Board::is_hex_removable on a removed hex: {:?}",
                    coord
                )
            }
        }

        let neighbor_idxs: Vec<u32> = coord.get_neighbors().iter().map(|&(i, _)| i).collect();
        let neighbor_idxs_slice = neighbor_idxs.as_slice();

        match neighbor_idxs_slice.len() {
            0 => panic!("A hex at {:?} is empty and has no neighbors", coord),
            1 => true,
            2 => {
                let valid_idx_combos = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [0, 5]];
                valid_idx_combos.iter().any(|c| c == neighbor_idxs_slice)
            }
            3 => {
                let valid_idx_combos = [
                    [0, 1, 2],
                    [1, 2, 3],
                    [2, 3, 4],
                    [3, 4, 5],
                    [0, 4, 5],
                    [0, 1, 5],
                ];
                valid_idx_combos.iter().any(|c| c == neighbor_idxs_slice)
            }
            _ => false,
        }
    }
    pub fn remove_hex(&mut self, coord: &HexCoord) {
        assert!(
            self.is_hex_removable(coord),
            "Tried to remove non-removable hex at {:?}",
            coord
        );
        self.set_hex(coord, None);
    }
}

#[derive(Debug, PartialEq)]
pub struct FieldCoord {
    x: i32,
    y: i32,
    f: u32,
}

#[derive(Clone, Copy, Debug)]
pub struct HexCoord {
    x: i32,
    y: i32,
}

impl FieldCoord {
    pub fn new(x: i32, y: i32, f: u32) -> FieldCoord {
        assert!((x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2 && f < 6);
        FieldCoord { x, y, f }
    }
    pub fn to_hex(&self) -> HexCoord {
        HexCoord::new(self.x, self.y)
    }
}

impl HexCoord {
    pub fn new(x: i32, y: i32) -> HexCoord {
        assert!((x + y).abs() <= 2 && x.abs() <= 2 && y.abs() <= 2);
        HexCoord { x, y }
    }
    pub fn to_field(&self, f: u32) -> FieldCoord {
        FieldCoord::new(self.x, self.y, f)
    }
    // We return a Vec of tuples so that get_hex_field_neighbors and is_hex_removable know which
    // neighbors are on which side of the hex. They need to know this for different reasons:
    //   * get_hex_field_neighbors: the index of each neighboring field depends on which hex
    //                              neighbor that field neighbor is on
    //   * is_hex_removable: a hex is removable only if it is "attached to the board by 3 or less
    //                       adjacent sides"
    fn get_neighbors(&self) -> Vec<(u32, HexCoord)> {
        let mut neighbors = vec![];

        if self.y < 2 && (self.x + self.y) != 2 {
            neighbors.push((0, HexCoord::new(self.x, self.y + 1)));
        }
        if (self.x + self.y) != 2 && self.x < 2 {
            neighbors.push((1, HexCoord::new(self.x + 1, self.y)));
        }
        if self.x < 2 && self.y > -2 {
            neighbors.push((2, HexCoord::new(self.x + 1, self.y - 1)));
        }
        if self.y > -2 && (self.x + self.y) > -2 {
            neighbors.push((3, HexCoord::new(self.x, self.y - 1)));
        }
        if (self.x + self.y) > -2 && self.x > -2 {
            neighbors.push((4, HexCoord::new(self.x - 1, self.y)));
        }
        if self.x > -2 && self.y < 2 {
            neighbors.push((5, HexCoord::new(self.x - 1, self.y + 1)));
        }

        neighbors
    }
}
<|endoftext|>


<commit_before>//use time::precise_time_ns;
use serde_json::from_str;
use serde_json::value::Value as SDValue;
use std::collections::HashMap;

use errors::*;

pub type WikiTitles = HashMap<String, String>;

pub fn parse_item(line: &str, languages: &Vec<String>)
                  -> Result<Option<WikiTitles>, WikiError> {
    //let t0 = precise_time_ns();
    let end = if line.ends_with(",\n") {
        line.len() - 2
    } else {
        line.len() - 1
    };
    let item: SDValue = from_str(&line[0..end])?;

    let mut sites = WikiTitles::new();
    if let Some(sitelinks) = item.find("sitelinks") {
        for l in languages {
            let link = format!("{}wiki", l);
            if let Some(res) = sitelinks.find(&link) {
                if let Some(title) = res.find("title") {
                    sites.insert(l.to_owned(),
                                 title.to_string());
                }
            }
        }
    }
    let result = if sites.len() == languages.len() {
        Some(sites)
    } else {
        None
    };
    //let t1 = precise_time_ns();
    //println!("parse_item: {} us", (t1 - t0) / 1000 );
    Ok(result)
}

<commit_msg>Implementing clippy's suggestion<commit_after>//use time::precise_time_ns;
use serde_json::from_str;
use serde_json::value::Value as SDValue;
use std::collections::HashMap;

use errors::*;

pub type WikiTitles = HashMap<String, String>;

pub fn parse_item(line: &str, languages: &[String])
                  -> Result<Option<WikiTitles>, WikiError> {
    //let t0 = precise_time_ns();
    let end = if line.ends_with(",\n") {
        line.len() - 2
    } else {
        line.len() - 1
    };
    let item: SDValue = from_str(&line[0..end])?;

    let mut sites = WikiTitles::new();
    if let Some(sitelinks) = item.find("sitelinks") {
        for l in languages {
            let link = format!("{}wiki", l);
            if let Some(res) = sitelinks.find(&link) {
                if let Some(title) = res.find("title") {
                    sites.insert(l.to_owned(),
                                 title.to_string());
                }
            }
        }
    }
    let result = if sites.len() == languages.len() {
        Some(sites)
    } else {
        None
    };
    //let t1 = precise_time_ns();
    //println!("parse_item: {} us", (t1 - t0) / 1000 );
    Ok(result)
}

<|endoftext|>