<commit_before>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-windows TempDir may cause IoError on windows: #10462

#![cfg_attr(test, deny(warnings))]

extern crate glob;
extern crate tempdir;

use glob::glob;
use std::env;
use std::path::PathBuf;
use std::fs;
use tempdir::TempDir;

#[test]
fn main() {
    fn mk_file(path: &str, directory: bool) {
        if directory {
            fs::create_dir(path).unwrap();
        } else {
            fs::File::create(path).unwrap();
        }
    }

    fn glob_vec(pattern: &str) -> Vec<PathBuf> {
        glob(pattern).unwrap().map(|r| r.unwrap()).collect()
    }

    let root = TempDir::new("glob-tests");
    let root = root.ok().expect("Should have created a temp directory");
    assert!(env::set_current_dir(root.path()).is_ok());

    mk_file("aaa", true);
    mk_file("aaa/apple", true);
    mk_file("aaa/orange", true);
    mk_file("aaa/tomato", true);
    mk_file("aaa/tomato/tomato.txt", false);
    mk_file("aaa/tomato/tomoto.txt", false);
    mk_file("bbb", true);
    mk_file("bbb/specials", true);
    mk_file("bbb/specials/!", false);

    // windows does not allow `*` or `?` characters to exist in filenames
    if env::consts::FAMILY != "windows" {
        mk_file("bbb/specials/*", false);
        mk_file("bbb/specials/?", false);
    }

    mk_file("bbb/specials/[", false);
    mk_file("bbb/specials/]", false);
    mk_file("ccc", true);
    mk_file("xyz", true);
    mk_file("xyz/x", false);
    mk_file("xyz/y", false);
    mk_file("xyz/z", false);

    mk_file("r", true);
    mk_file("r/current_dir.md", false);
    mk_file("r/one", true);
    mk_file("r/one/a.md", false);
    mk_file("r/one/another", true);
    mk_file("r/one/another/a.md", false);
    mk_file("r/one/another/deep", true);
    mk_file("r/one/another/deep/spelunking.md", false);
    mk_file("r/another", true);
    mk_file("r/another/a.md", false);
    mk_file("r/two", true);
    mk_file("r/two/b.md", false);
    mk_file("r/three", true);
    mk_file("r/three/c.md", false);

    // all recursive entities
    assert_eq!(glob_vec("r/**"), vec!(
        PathBuf::from("r/another"),
        PathBuf::from("r/one"),
        PathBuf::from("r/one/another"),
        PathBuf::from("r/one/another/deep"),
        PathBuf::from("r/three"),
        PathBuf::from("r/two")));

    // collapse consecutive recursive patterns
    assert_eq!(glob_vec("r/**/**"), vec!(
        PathBuf::from("r/another"),
        PathBuf::from("r/one"),
        PathBuf::from("r/one/another"),
        PathBuf::from("r/one/another/deep"),
        PathBuf::from("r/three"),
        PathBuf::from("r/two")));

    assert_eq!(glob_vec("r/**/*"), vec!(
        PathBuf::from("r/another"),
        PathBuf::from("r/another/a.md"),
        PathBuf::from("r/current_dir.md"),
        PathBuf::from("r/one"),
        PathBuf::from("r/one/a.md"),
        PathBuf::from("r/one/another"),
        PathBuf::from("r/one/another/a.md"),
        PathBuf::from("r/one/another/deep"),
        PathBuf::from("r/one/another/deep/spelunking.md"),
        PathBuf::from("r/three"),
        PathBuf::from("r/three/c.md"),
        PathBuf::from("r/two"),
        PathBuf::from("r/two/b.md")));

    // followed by a wildcard
    assert_eq!(glob_vec("r/**/*.md"), vec!(
        PathBuf::from("r/another/a.md"),
        PathBuf::from("r/current_dir.md"),
        PathBuf::from("r/one/a.md"),
        PathBuf::from("r/one/another/a.md"),
        PathBuf::from("r/one/another/deep/spelunking.md"),
        PathBuf::from("r/three/c.md"),
        PathBuf::from("r/two/b.md")));

    // followed by a precise pattern
    assert_eq!(glob_vec("r/one/**/a.md"), vec!(
        PathBuf::from("r/one/a.md"),
        PathBuf::from("r/one/another/a.md")));

    // followed by another recursive pattern
    // collapses consecutive recursives into one
    assert_eq!(glob_vec("r/one/**/**/a.md"), vec!(
        PathBuf::from("r/one/a.md"),
        PathBuf::from("r/one/another/a.md")));

    // followed by two precise patterns
    assert_eq!(glob_vec("r/**/another/a.md"), vec!(
        PathBuf::from("r/another/a.md"),
        PathBuf::from("r/one/another/a.md")));

    assert_eq!(glob_vec(""), Vec::new());
    assert_eq!(glob_vec("."), vec!(PathBuf::from(".")));
    assert_eq!(glob_vec(".."), vec!(PathBuf::from("..")));

    assert_eq!(glob_vec("aaa"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aaa/"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("a"), Vec::new());
    assert_eq!(glob_vec("aa"), Vec::new());
    assert_eq!(glob_vec("aaaa"), Vec::new());

    assert_eq!(glob_vec("aaa/apple"), vec!(PathBuf::from("aaa/apple")));
    assert_eq!(glob_vec("aaa/apple/nope"), Vec::new());

    // windows should support both / and \ as directory separators
    if env::consts::FAMILY == "windows" {
        assert_eq!(glob_vec("aaa\\apple"), vec!(PathBuf::from("aaa/apple")));
    }

    assert_eq!(glob_vec("???/"), vec!(
        PathBuf::from("aaa"),
        PathBuf::from("bbb"),
        PathBuf::from("ccc"),
        PathBuf::from("xyz")));

    assert_eq!(glob_vec("aaa/tomato/tom?to.txt"), vec!(
        PathBuf::from("aaa/tomato/tomato.txt"),
        PathBuf::from("aaa/tomato/tomoto.txt")));

    assert_eq!(glob_vec("xyz/?"), vec!(
        PathBuf::from("xyz/x"),
        PathBuf::from("xyz/y"),
        PathBuf::from("xyz/z")));

    assert_eq!(glob_vec("a*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("*a*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("a*a"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aaa*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("*aaa"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("*aaa*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("*a*a*a*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aaa*/"), vec!(PathBuf::from("aaa")));

    assert_eq!(glob_vec("aaa/*"), vec!(
        PathBuf::from("aaa/apple"),
        PathBuf::from("aaa/orange"),
        PathBuf::from("aaa/tomato")));

    assert_eq!(glob_vec("aaa/*a*"), vec!(
        PathBuf::from("aaa/apple"),
        PathBuf::from("aaa/orange"),
        PathBuf::from("aaa/tomato")));

    assert_eq!(glob_vec("*/*/*.txt"), vec!(
        PathBuf::from("aaa/tomato/tomato.txt"),
        PathBuf::from("aaa/tomato/tomoto.txt")));

    assert_eq!(glob_vec("*/*/t[aob]m?to[.]t[!y]t"), vec!(
        PathBuf::from("aaa/tomato/tomato.txt"),
        PathBuf::from("aaa/tomato/tomoto.txt")));

    assert_eq!(glob_vec("./aaa"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("./*"), glob_vec("*"));
    assert_eq!(glob_vec("*/..").pop().unwrap(), PathBuf::from("xyz/.."));
    assert_eq!(glob_vec("aaa/../bbb"), vec!(PathBuf::from("aaa/../bbb")));
    assert_eq!(glob_vec("nonexistent/../bbb"), Vec::new());
    assert_eq!(glob_vec("aaa/tomato/tomato.txt/.."), Vec::new());

    assert_eq!(glob_vec("aaa/tomato/tomato.txt/"), Vec::new());

    assert_eq!(glob_vec("aa[a]"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aa[abc]"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("a[bca]a"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aa[b]"), Vec::new());
    assert_eq!(glob_vec("aa[xyz]"), Vec::new());
    assert_eq!(glob_vec("aa[]]"), Vec::new());

    assert_eq!(glob_vec("aa[!b]"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aa[!bcd]"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("a[!bcd]a"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aa[!a]"), Vec::new());
    assert_eq!(glob_vec("aa[!abc]"), Vec::new());

    assert_eq!(glob_vec("bbb/specials/[[]"), vec!(PathBuf::from("bbb/specials/[")));
    assert_eq!(glob_vec("bbb/specials/!"), vec!(PathBuf::from("bbb/specials/!")));
    assert_eq!(glob_vec("bbb/specials/[]]"), vec!(PathBuf::from("bbb/specials/]")));

    if env::consts::FAMILY != "windows" {
        assert_eq!(glob_vec("bbb/specials/[*]"), vec!(PathBuf::from("bbb/specials/*")));
        assert_eq!(glob_vec("bbb/specials/[?]"), vec!(PathBuf::from("bbb/specials/?")));
    }

    if env::consts::FAMILY == "windows" {

        assert_eq!(glob_vec("bbb/specials/[![]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/]")));

        assert_eq!(glob_vec("bbb/specials/[!]]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/[")));

        assert_eq!(glob_vec("bbb/specials/[!!]"), vec!(
            PathBuf::from("bbb/specials/["),
            PathBuf::from("bbb/specials/]")));

    } else {

        assert_eq!(glob_vec("bbb/specials/[![]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/*"),
            PathBuf::from("bbb/specials/?"),
            PathBuf::from("bbb/specials/]")));

        assert_eq!(glob_vec("bbb/specials/[!]]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/*"),
            PathBuf::from("bbb/specials/?"),
            PathBuf::from("bbb/specials/[")));

        assert_eq!(glob_vec("bbb/specials/[!!]"), vec!(
            PathBuf::from("bbb/specials/*"),
            PathBuf::from("bbb/specials/?"),
            PathBuf::from("bbb/specials/["),
            PathBuf::from("bbb/specials/]")));

        assert_eq!(glob_vec("bbb/specials/[!*]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/?"),
            PathBuf::from("bbb/specials/["),
            PathBuf::from("bbb/specials/]")));

        assert_eq!(glob_vec("bbb/specials/[!?]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/*"),
            PathBuf::from("bbb/specials/["),
            PathBuf::from("bbb/specials/]")));

    }
}
<commit_msg>can't infer type of empty vec<commit_after>// Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

// ignore-windows TempDir may cause IoError on windows: #10462

#![cfg_attr(test, deny(warnings))]

extern crate glob;
extern crate tempdir;

use glob::glob;
use std::env;
use std::path::PathBuf;
use std::fs;
use tempdir::TempDir;

#[test]
fn main() {
    fn mk_file(path: &str, directory: bool) {
        if directory {
            fs::create_dir(path).unwrap();
        } else {
            fs::File::create(path).unwrap();
        }
    }

    fn glob_vec(pattern: &str) -> Vec<PathBuf> {
        glob(pattern).unwrap().map(|r| r.unwrap()).collect()
    }

    let root = TempDir::new("glob-tests");
    let root = root.ok().expect("Should have created a temp directory");
    assert!(env::set_current_dir(root.path()).is_ok());

    mk_file("aaa", true);
    mk_file("aaa/apple", true);
    mk_file("aaa/orange", true);
    mk_file("aaa/tomato", true);
    mk_file("aaa/tomato/tomato.txt", false);
    mk_file("aaa/tomato/tomoto.txt", false);
    mk_file("bbb", true);
    mk_file("bbb/specials", true);
    mk_file("bbb/specials/!", false);

    // windows does not allow `*` or `?` characters to exist in filenames
    if env::consts::FAMILY != "windows" {
        mk_file("bbb/specials/*", false);
        mk_file("bbb/specials/?", false);
    }

    mk_file("bbb/specials/[", false);
    mk_file("bbb/specials/]", false);
    mk_file("ccc", true);
    mk_file("xyz", true);
    mk_file("xyz/x", false);
    mk_file("xyz/y", false);
    mk_file("xyz/z", false);

    mk_file("r", true);
    mk_file("r/current_dir.md", false);
    mk_file("r/one", true);
    mk_file("r/one/a.md", false);
    mk_file("r/one/another", true);
    mk_file("r/one/another/a.md", false);
    mk_file("r/one/another/deep", true);
    mk_file("r/one/another/deep/spelunking.md", false);
    mk_file("r/another", true);
    mk_file("r/another/a.md", false);
    mk_file("r/two", true);
    mk_file("r/two/b.md", false);
    mk_file("r/three", true);
    mk_file("r/three/c.md", false);

    // all recursive entities
    assert_eq!(glob_vec("r/**"), vec!(
        PathBuf::from("r/another"),
        PathBuf::from("r/one"),
        PathBuf::from("r/one/another"),
        PathBuf::from("r/one/another/deep"),
        PathBuf::from("r/three"),
        PathBuf::from("r/two")));

    // collapse consecutive recursive patterns
    assert_eq!(glob_vec("r/**/**"), vec!(
        PathBuf::from("r/another"),
        PathBuf::from("r/one"),
        PathBuf::from("r/one/another"),
        PathBuf::from("r/one/another/deep"),
        PathBuf::from("r/three"),
        PathBuf::from("r/two")));

    assert_eq!(glob_vec("r/**/*"), vec!(
        PathBuf::from("r/another"),
        PathBuf::from("r/another/a.md"),
        PathBuf::from("r/current_dir.md"),
        PathBuf::from("r/one"),
        PathBuf::from("r/one/a.md"),
        PathBuf::from("r/one/another"),
        PathBuf::from("r/one/another/a.md"),
        PathBuf::from("r/one/another/deep"),
        PathBuf::from("r/one/another/deep/spelunking.md"),
        PathBuf::from("r/three"),
        PathBuf::from("r/three/c.md"),
        PathBuf::from("r/two"),
        PathBuf::from("r/two/b.md")));

    // followed by a wildcard
    assert_eq!(glob_vec("r/**/*.md"), vec!(
        PathBuf::from("r/another/a.md"),
        PathBuf::from("r/current_dir.md"),
        PathBuf::from("r/one/a.md"),
        PathBuf::from("r/one/another/a.md"),
        PathBuf::from("r/one/another/deep/spelunking.md"),
        PathBuf::from("r/three/c.md"),
        PathBuf::from("r/two/b.md")));

    // followed by a precise pattern
    assert_eq!(glob_vec("r/one/**/a.md"), vec!(
        PathBuf::from("r/one/a.md"),
        PathBuf::from("r/one/another/a.md")));

    // followed by another recursive pattern
    // collapses consecutive recursives into one
    assert_eq!(glob_vec("r/one/**/**/a.md"), vec!(
        PathBuf::from("r/one/a.md"),
        PathBuf::from("r/one/another/a.md")));

    // followed by two precise patterns
    assert_eq!(glob_vec("r/**/another/a.md"), vec!(
        PathBuf::from("r/another/a.md"),
        PathBuf::from("r/one/another/a.md")));

    assert_eq!(glob_vec(""), Vec::<PathBuf>::new());
    assert_eq!(glob_vec("."), vec!(PathBuf::from(".")));
    assert_eq!(glob_vec(".."), vec!(PathBuf::from("..")));

    assert_eq!(glob_vec("aaa"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aaa/"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("a"), Vec::<PathBuf>::new());
    assert_eq!(glob_vec("aa"), Vec::<PathBuf>::new());
    assert_eq!(glob_vec("aaaa"), Vec::<PathBuf>::new());

    assert_eq!(glob_vec("aaa/apple"), vec!(PathBuf::from("aaa/apple")));
    assert_eq!(glob_vec("aaa/apple/nope"), Vec::<PathBuf>::new());

    // windows should support both / and \ as directory separators
    if env::consts::FAMILY == "windows" {
        assert_eq!(glob_vec("aaa\\apple"), vec!(PathBuf::from("aaa/apple")));
    }

    assert_eq!(glob_vec("???/"), vec!(
        PathBuf::from("aaa"),
        PathBuf::from("bbb"),
        PathBuf::from("ccc"),
        PathBuf::from("xyz")));

    assert_eq!(glob_vec("aaa/tomato/tom?to.txt"), vec!(
        PathBuf::from("aaa/tomato/tomato.txt"),
        PathBuf::from("aaa/tomato/tomoto.txt")));

    assert_eq!(glob_vec("xyz/?"), vec!(
        PathBuf::from("xyz/x"),
        PathBuf::from("xyz/y"),
        PathBuf::from("xyz/z")));

    assert_eq!(glob_vec("a*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("*a*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("a*a"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aaa*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("*aaa"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("*aaa*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("*a*a*a*"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aaa*/"), vec!(PathBuf::from("aaa")));

    assert_eq!(glob_vec("aaa/*"), vec!(
        PathBuf::from("aaa/apple"),
        PathBuf::from("aaa/orange"),
        PathBuf::from("aaa/tomato")));

    assert_eq!(glob_vec("aaa/*a*"), vec!(
        PathBuf::from("aaa/apple"),
        PathBuf::from("aaa/orange"),
        PathBuf::from("aaa/tomato")));

    assert_eq!(glob_vec("*/*/*.txt"), vec!(
        PathBuf::from("aaa/tomato/tomato.txt"),
        PathBuf::from("aaa/tomato/tomoto.txt")));

    assert_eq!(glob_vec("*/*/t[aob]m?to[.]t[!y]t"), vec!(
        PathBuf::from("aaa/tomato/tomato.txt"),
        PathBuf::from("aaa/tomato/tomoto.txt")));

    assert_eq!(glob_vec("./aaa"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("./*"), glob_vec("*"));
    assert_eq!(glob_vec("*/..").pop().unwrap(), PathBuf::from("xyz/.."));
    assert_eq!(glob_vec("aaa/../bbb"), vec!(PathBuf::from("aaa/../bbb")));
    assert_eq!(glob_vec("nonexistent/../bbb"), Vec::<PathBuf>::new());
    assert_eq!(glob_vec("aaa/tomato/tomato.txt/.."), Vec::<PathBuf>::new());

    assert_eq!(glob_vec("aaa/tomato/tomato.txt/"), Vec::<PathBuf>::new());

    assert_eq!(glob_vec("aa[a]"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aa[abc]"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("a[bca]a"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aa[b]"), Vec::<PathBuf>::new());
    assert_eq!(glob_vec("aa[xyz]"), Vec::<PathBuf>::new());
    assert_eq!(glob_vec("aa[]]"), Vec::<PathBuf>::new());

    assert_eq!(glob_vec("aa[!b]"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aa[!bcd]"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("a[!bcd]a"), vec!(PathBuf::from("aaa")));
    assert_eq!(glob_vec("aa[!a]"), Vec::<PathBuf>::new());
    assert_eq!(glob_vec("aa[!abc]"), Vec::<PathBuf>::new());

    assert_eq!(glob_vec("bbb/specials/[[]"), vec!(PathBuf::from("bbb/specials/[")));
    assert_eq!(glob_vec("bbb/specials/!"), vec!(PathBuf::from("bbb/specials/!")));
    assert_eq!(glob_vec("bbb/specials/[]]"), vec!(PathBuf::from("bbb/specials/]")));

    if env::consts::FAMILY != "windows" {
        assert_eq!(glob_vec("bbb/specials/[*]"), vec!(PathBuf::from("bbb/specials/*")));
        assert_eq!(glob_vec("bbb/specials/[?]"), vec!(PathBuf::from("bbb/specials/?")));
    }

    if env::consts::FAMILY == "windows" {

        assert_eq!(glob_vec("bbb/specials/[![]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/]")));

        assert_eq!(glob_vec("bbb/specials/[!]]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/[")));

        assert_eq!(glob_vec("bbb/specials/[!!]"), vec!(
            PathBuf::from("bbb/specials/["),
            PathBuf::from("bbb/specials/]")));

    } else {

        assert_eq!(glob_vec("bbb/specials/[![]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/*"),
            PathBuf::from("bbb/specials/?"),
            PathBuf::from("bbb/specials/]")));

        assert_eq!(glob_vec("bbb/specials/[!]]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/*"),
            PathBuf::from("bbb/specials/?"),
            PathBuf::from("bbb/specials/[")));

        assert_eq!(glob_vec("bbb/specials/[!!]"), vec!(
            PathBuf::from("bbb/specials/*"),
            PathBuf::from("bbb/specials/?"),
            PathBuf::from("bbb/specials/["),
            PathBuf::from("bbb/specials/]")));

        assert_eq!(glob_vec("bbb/specials/[!*]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/?"),
            PathBuf::from("bbb/specials/["),
            PathBuf::from("bbb/specials/]")));

        assert_eq!(glob_vec("bbb/specials/[!?]"), vec!(
            PathBuf::from("bbb/specials/!"),
            PathBuf::from("bbb/specials/*"),
            PathBuf::from("bbb/specials/["),
            PathBuf::from("bbb/specials/]")));

    }
}
<|endoftext|>


<commit_before>#[allow(unused_imports)];
#[allow(dead_code)];

use core::mem::size_of;
use core::ptr::set_memory;
use core::option::Some;
use core;

use kernel::heap;
use kernel::memory::physical;
use kernel::memory::physical::Phys;

pub type Frame = [u8, ..PAGE_SIZE];

static PAGE_SIZE: uint = 0x1000;
static PAGE_SIZE_LOG2: uint = 12;

define_flags!(Flags: u32 {
    SECTION = 0b10010,

    BUFFER = 1 << 2,
    CACHE,
    RW     = 1 << 10,
    USER
})

#[packed]
struct Descriptor(u32);

#[packed]
struct PageTableCoarse {
    pages: [Descriptor, ..256]
}

#[packed]
pub struct PageDirectory {
    tables: [Descriptor, ..4096]
}

pub unsafe fn init() {
    let dir: Phys<PageDirectory> = physical::zero_alloc_frames(4);

    (*dir.as_ptr()).tables[0] = Descriptor::section(0, RW);
    (*dir.as_ptr()).enable();
}

pub unsafe fn map(page_ptr: *mut u8, size: uint, flags: Flags) {
    // TODO
}

impl Descriptor {
    fn section(base: u32, flags: Flags) -> Descriptor {
        // make a section descriptor
        //                /permissions
        Descriptor(base) | flags | SECTION
    }
}

impl core::ops::BitOr<Flags, Descriptor> for Descriptor {
    #[inline(always)]
    fn bitor(&self, other: &Flags) -> Descriptor {
        match (self, other) {
            (&Descriptor(p), &Flags(f)) => Descriptor(p | f)
        }
    }
}

impl core::ops::BitAnd<Flags, bool> for Descriptor {
    #[inline(always)]
    fn bitand(&self, other: &Flags) -> bool {
        match (self, other) {
            (&Descriptor(p), &Flags(f)) => p & f != 0
        }
    }
}

impl PageDirectory {
    pub unsafe fn enable(&self) {
        asm!("mov ip, 0
              mcr p15, 0, ip, c7, c5, 0     // invalidate I cache
              mcr p15, 0, ip, c7, c10, 4    // drain WB
              mcr p15, 0, r0, c2, c0, 0     // load page table pointer
              mcr p15, 0, ip, c8, c7, 0     // invalidate I & D TLBs"
            :: "{r0}"(self) : "ip")
    }
}
<commit_msg>arm: Enable MMU and access domains<commit_after>#[allow(unused_imports)];
#[allow(dead_code)];

use core::mem::size_of;
use core::ptr::set_memory;
use core::option::Some;
use core;

use kernel::heap;
use kernel::memory::physical;
use kernel::memory::physical::Phys;

// use macros;

pub type Frame = [u8, ..PAGE_SIZE];

static PAGE_SIZE: uint = 0x1000;
static PAGE_SIZE_LOG2: uint = 12;

define_flags!(Flags: u32 {
    SECTION = 0b10010,

    BUFFER = 1 << 2,
    CACHE,
    RW     = 1 << 10,
    CLIENT_ACCESS
})

#[packed]
struct Descriptor(u32);

#[packed]
struct PageTableCoarse {
    pages: [Descriptor, ..256]
}

#[packed]
struct PageDirectory {
    entries: [Descriptor, ..4096]
}

define_reg!(CR, CRFlags: uint {
    CR_M  = 1 << 0,  // MMU enable
    CR_A  = 1 << 1,
    CR_C  = 1 << 2,  // Data cache enable
    CR_W  = 1 << 3,
    CR_P  = 1 << 4,  // 32-bit exception handler
    CR_D  = 1 << 5,  // 32-bit data address range
    CR_L  = 1 << 6,  // Implementation defined
    CR_B  = 1 << 7,  // Endianness
    CR_S  = 1 << 8,
    CR_R  = 1 << 9,
    CR_F  = 1 << 10, // Implementation defined
    CR_Z  = 1 << 11, // Implementation defined
    CR_I  = 1 << 12, // Instruction cache enable
    CR_V  = 1 << 13,
    CR_RR = 1 << 14,
    CR_L4 = 1 << 15
})

// Each of the 16 domains can be either allowed full access (manager)
// to a region of memory or restricted access to some pages in that region (client).
define_flags!(DomainTypeMask: uint {
    KERNEL = 0b11 << 0,
    USER   = 0b11 << 2,
    NOACCESS = 0,
    CLIENT   = 0b01 * 0x55555555,
    MANAGER  = 0b11 * 0x55555555
})

impl CR {
    #[inline] #[allow(dead_code)]
    pub fn read() -> CRFlags {
        unsafe {
            let flags;
            asm!(concat!("mrc p15, 0, $0, c1, c0, 0") : "=r"(flags));
            CRFlags(flags)
        }
    }

    #[inline] #[allow(dead_code)]
    pub fn write(f: CRFlags) {
        match f {
            CRFlags(val) => unsafe {
                asm!(concat!("mcr p15, 0, $0, c1, c0, 0") :: "r"(val) :: "volatile");
            }
        }
    }
}

pub unsafe fn init() {
    let dir: Phys<PageDirectory> = physical::zero_alloc_frames(4);

    (*dir.as_ptr()).entries[0] = Descriptor::section(0, RW);

    switch_directory(dir);
    enable_paging();
}

pub fn switch_directory(dir: Phys<PageDirectory>) {
    // Memory protection is determined by control register c1 bits S and R,
    // domain access reg. c3 and per-page domain number and permission bits.
    let cpu_domain = KERNEL & MANAGER | USER & MANAGER;

    unsafe {
        asm!("mcr p15, 0, $0, c3, c0, 0     // load domain access register
              mcr p15, 0, $1, c2, c0, 0     // load page table pointer
            " :: "r"(cpu_domain), "r"(dir.offset()) : "ip" : "volatile");
    }
}

fn enable_paging() {
    unsafe {
        asm!("mov ip, 0
              mcr p15, 0, ip, c7, c5, 0     // invalidate I & D cache
              mcr p15, 0, ip, c7, c10, 4    // drain write buffer
              mcr p15, 0, ip, c8, c7, 0     // invalidate I & D TLBs
            " ::: "ip" : "volatile");

        CR::write(CR - (CR_A | CR_W | CR_P | CR_D | CR_R | CR_F | CR_Z | CR_V | CR_RR)
                     | (CR_S | CR_I | CR_C | CR_M));
    }
}

pub unsafe fn map(page_ptr: *mut u8, size: uint, flags: Flags) {
    // TODO
}

impl Descriptor {
    fn section(base: u32, flags: Flags) -> Descriptor {
        // make a section descriptor
        //                /permissions
        Descriptor(base) | flags | SECTION
    }
}

impl_ops!(Descriptor, Flags)

impl PageDirectory {
    pub unsafe fn map(&self, page_ptr: *mut u8, size: uint, flags: Flags) {
        // TODO
    }
}
<|endoftext|>


<commit_before>#[macro_use]
pub mod style;

pub mod exit_dialogue;
pub mod messaging;
pub mod msg_area;
pub mod tabbed;
pub mod termbox;
pub mod text_field;
pub mod widget;

use std::fs::File;
use std::fs;
use std::io::Write;
use std::io;
use std::mem;
use std::str;

use time::Tm;
use time;

use self::tabbed::{Tabbed, TabbedRet, MsgSource};

use term_input::{Event, Key};
use termbox_simple::{Termbox, OutputMode};

pub struct TUI {
    /// Termbox instance
    termbox  : Termbox,

    /// A tab for every server + channel
    ui       : Tabbed,

    /// For debugging only - `write()` method is called with incomplete lines,
    /// we collect those here. Messages are only shown with `flush()`.
    buffer   : Vec<u8>,

    /// For debugging only - `write()` also logs to a file.
    log_file : File,
}

#[derive(Debug)]
pub enum TUIRet {
    Abort,
    KeyHandled,
    KeyIgnored(Key),
    EventIgnored(Event),

    /// INVARIANT: The vec will have at least one char.
    // Can't make MsgSource a ref because of this weird error:
    // https://users.rust-lang.org/t/borrow-checker-bug/5165
    Input {
        msg  : Vec<char>,
        from : MsgSource,
    },
}

impl TUI {
    pub fn new() -> TUI {
        let mut tui = Termbox::init().unwrap(); // TODO: check errors
        tui.set_output_mode(OutputMode::Output256);
        tui.set_clear_attributes(style::CLEAR.fg, style::CLEAR.bg);

        let _ = fs::create_dir("logs");

        TUI {
            ui: Tabbed::new(tui.width() as i32, tui.height() as i32),
            termbox: tui,
            buffer: Vec::with_capacity(100),
            log_file: File::create("logs/debug.txt").unwrap(),
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Tab stuff

impl TUI {
    pub fn new_server_tab(&mut self, serv_name : &str) {
        self.ui.new_server_tab(serv_name);
    }

    pub fn new_chan_tab(&mut self, serv_name : &str, chan_name : &str) {
        self.ui.new_chan_tab(serv_name, chan_name);
    }

    pub fn new_user_tab(&mut self, serv_name : &str, nick : &str) {
        self.ui.new_user_tab(serv_name, nick);
    }
}

////////////////////////////////////////////////////////////////////////////////
// Event handling

impl TUI {
    pub fn handle_input_event(&mut self, ev : Event) -> TUIRet {
        match ev {
            Event::Resize => {
                // This never happens, probably because the our select() loop,
                // termbox can't really get resize signals.
                self.resize();
                TUIRet::KeyHandled
            },

            Event::Key(key) => {
                match self.ui.keypressed(key) {
                    TabbedRet::KeyHandled => TUIRet::KeyHandled,
                    TabbedRet::KeyIgnored => TUIRet::KeyIgnored(key),
                    TabbedRet::Input { msg, from } => {
                        TUIRet::Input {
                            msg: msg,
                            from: from.clone(),
                        }
                    },
                    TabbedRet::Abort => TUIRet::Abort,
                }
            },

            Event::String(str) => {
                // This happens when keys pressed too fast or a text pasted to the terminal
                if str.len() <= 8 {
                    // Assume fast key press
                    let mut ret = TUIRet::KeyHandled;
                    for ch in str.chars() {
                        ret = self.handle_input_event(Event::Key(Key::Char(ch)));
                    }
                    ret
                } else {
                    // Assume paste
                    TUIRet::EventIgnored(Event::String(str.to_owned()))
                }
            },

            ev => {
                TUIRet::EventIgnored(ev)
            },
        }
    }

    pub fn resize(&mut self) {
        self.termbox.resize();
        let w = self.termbox.width();
        let h = self.termbox.height();
        self.ui.resize(w, h);
    }

/*
    /// Loop until something's entered to the user input field. Useful for
    /// waiting for a command when there's no connection yet.
    pub fn idle_loop(&mut self) -> TUIRet {
        loop {
            self.draw();

            match self.keypressed_poll() {
                ret @ TUIRet::Abort => { return ret; },
                ret @ TUIRet::Input { .. } => { return ret; },
                _ => {}
            }
        }
    }
*/

    pub fn draw(&mut self) {
        self.termbox.clear();
        self.ui.draw(&mut self.termbox, 0, 0);
        self.termbox.present();
    }
}

////////////////////////////////////////////////////////////////////////////////
// Showing messages

/// Target of a message coming from an IRC server.
#[derive(Debug)]
pub enum MsgTarget<'a> {
    Server { serv_name: &'a str },
    Chan { serv_name: &'a str, chan_name: &'a str },
    User { serv_name: &'a str, nick: &'a str },

    /// Show the message in all tabs of a server.
    AllServTabs { serv_name: &'a str },

    /// Show the message all server tabs that have the user. (i.e. channels,
    /// privmsg tabs)
    AllUserTabs { serv_name: &'a str, nick: &'a str },

    /// Show the message in all tabs.
    AllTabs,

    /// Show the message in currently active tab.
    CurrentTab,

    MultipleTabs(Vec<MsgTarget<'a>>),
}

impl TUI {
    /// An error message coming from Tiny, probably because of a command error
    /// etc. Those are not timestamped and not logged.
    #[inline]
    pub fn add_client_err_msg(&mut self, msg : &str, target : &MsgTarget) {
        self.ui.add_client_err_msg(msg, target);
    }

    /// A message from client, usually just to indidate progress, e.g.
    /// "Connecting...". Not timestamed and not logged.
    #[inline]
    pub fn add_client_msg(&mut self, msg : &str, target : &MsgTarget) {
        self.ui.add_client_msg(msg, target);
    }

    /// privmsg is a message coming from a server or client. Shown with sender's
    /// nick/name and receive time and logged.
    #[inline]
    pub fn add_privmsg(&mut self, sender : &str, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.ui.add_privmsg(sender, msg, tm, target);
    }

    /// A message without any explicit sender info. Useful for e.g. in server
    /// and debug log tabs. Timestamped and logged.
    #[inline]
    pub fn add_msg(&mut self, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.ui.add_msg(msg, tm, target);
    }

    /// Error messages related with the protocol - e.g. can't join a channel,
    /// nickname is in use etc. Timestamped and logged.
    #[inline]
    pub fn add_err_msg(&mut self, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.ui.add_err_msg(msg, tm, target);
    }

    pub fn set_topic(&mut self, msg : &str, target : &MsgTarget) {
        self.ui.set_topic(msg, target);
    }

    #[inline]
    pub fn add_nick(&mut self, nick : &str, tm : Option<&Tm>, target : &MsgTarget) {
        self.ui.add_nick(nick, tm, target);
    }

    #[inline]
    pub fn remove_nick(&mut self, nick : &str, tm : Option<&Tm>, target : &MsgTarget) {
        self.ui.remove_nick(nick, tm, target);
    }

    #[inline]
    pub fn rename_nick(&mut self, old_nick : &str, new_nick : &str, tm : &Tm, target : &MsgTarget) {
        self.ui.rename_nick(old_nick, new_nick, tm, target);
    }
}

////////////////////////////////////////////////////////////////////////////////

// Write instance is used for debugging - messages show up in a "debug" tab.

impl Write for TUI {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let mut line_start = 0;

        for (byte_idx, byte) in buf.iter().enumerate() {
            if *byte == b'\n' {
                if self.buffer.len() != 0 {
                    debug_assert!(line_start == 0);
                    self.buffer.extend_from_slice(&buf[ 0 .. byte_idx ]);
                    let mut msg : Vec<u8> = Vec::with_capacity(100);
                    mem::swap(&mut msg, &mut self.buffer);
                    self.add_msg(&unsafe { String::from_utf8_unchecked(msg) },
                                 &time::now(),
                                 &MsgTarget::Server { serv_name: "debug" });
                } else {
                    self.add_msg(unsafe { str::from_utf8_unchecked(&buf[ line_start .. byte_idx ]) },
                                 &time::now(),
                                 &MsgTarget::Server { serv_name: "debug" });
                }

                line_start = byte_idx + 1;
            }
        }

        self.buffer.extend_from_slice(&buf[ line_start .. ]);
        self.log_file.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.log_file.flush()
    }
}
<commit_msg>Fix resize signal handler<commit_after>#[macro_use]
pub mod style;

pub mod exit_dialogue;
pub mod messaging;
pub mod msg_area;
pub mod tabbed;
pub mod termbox;
pub mod text_field;
pub mod widget;

use std::fs::File;
use std::fs;
use std::io::Write;
use std::io;
use std::mem;
use std::str;

use time::Tm;
use time;

use self::tabbed::{Tabbed, TabbedRet, MsgSource};

use term_input::{Event, Key};
use termbox_simple::{Termbox, OutputMode};

pub struct TUI {
    /// Termbox instance
    termbox  : Termbox,

    /// A tab for every server + channel
    ui       : Tabbed,

    /// For debugging only - `write()` method is called with incomplete lines,
    /// we collect those here. Messages are only shown with `flush()`.
    buffer   : Vec<u8>,

    /// For debugging only - `write()` also logs to a file.
    log_file : File,
}

#[derive(Debug)]
pub enum TUIRet {
    Abort,
    KeyHandled,
    KeyIgnored(Key),
    EventIgnored(Event),

    /// INVARIANT: The vec will have at least one char.
    // Can't make MsgSource a ref because of this weird error:
    // https://users.rust-lang.org/t/borrow-checker-bug/5165
    Input {
        msg  : Vec<char>,
        from : MsgSource,
    },
}

impl TUI {
    pub fn new() -> TUI {
        let mut tui = Termbox::init().unwrap(); // TODO: check errors
        tui.set_output_mode(OutputMode::Output256);
        tui.set_clear_attributes(style::CLEAR.fg, style::CLEAR.bg);

        let _ = fs::create_dir("logs");

        TUI {
            ui: Tabbed::new(tui.width() as i32, tui.height() as i32),
            termbox: tui,
            buffer: Vec::with_capacity(100),
            log_file: File::create("logs/debug.txt").unwrap(),
        }
    }
}

////////////////////////////////////////////////////////////////////////////////
// Tab stuff

impl TUI {
    pub fn new_server_tab(&mut self, serv_name : &str) {
        self.ui.new_server_tab(serv_name);
    }

    pub fn new_chan_tab(&mut self, serv_name : &str, chan_name : &str) {
        self.ui.new_chan_tab(serv_name, chan_name);
    }

    pub fn new_user_tab(&mut self, serv_name : &str, nick : &str) {
        self.ui.new_user_tab(serv_name, nick);
    }
}

////////////////////////////////////////////////////////////////////////////////
// Event handling

impl TUI {
    pub fn handle_input_event(&mut self, ev : Event) -> TUIRet {
        match ev {
            Event::Resize => {
                // This never happens, probably because the our select() loop,
                // termbox can't really get resize signals.
                self.resize();
                TUIRet::KeyHandled
            },

            Event::Key(key) => {
                match self.ui.keypressed(key) {
                    TabbedRet::KeyHandled => TUIRet::KeyHandled,
                    TabbedRet::KeyIgnored => TUIRet::KeyIgnored(key),
                    TabbedRet::Input { msg, from } => {
                        TUIRet::Input {
                            msg: msg,
                            from: from.clone(),
                        }
                    },
                    TabbedRet::Abort => TUIRet::Abort,
                }
            },

            Event::String(str) => {
                // This happens when keys pressed too fast or a text pasted to the terminal
                if str.len() <= 8 {
                    // Assume fast key press
                    let mut ret = TUIRet::KeyHandled;
                    for ch in str.chars() {
                        ret = self.handle_input_event(Event::Key(Key::Char(ch)));
                    }
                    ret
                } else {
                    // Assume paste
                    TUIRet::EventIgnored(Event::String(str.to_owned()))
                }
            },

            ev => {
                TUIRet::EventIgnored(ev)
            },
        }
    }

    pub fn resize(&mut self) {
        self.termbox.resize();
        self.termbox.clear();
        let w = self.termbox.width();
        let h = self.termbox.height();
        self.ui.resize(w, h);
    }

/*
    /// Loop until something's entered to the user input field. Useful for
    /// waiting for a command when there's no connection yet.
    pub fn idle_loop(&mut self) -> TUIRet {
        loop {
            self.draw();

            match self.keypressed_poll() {
                ret @ TUIRet::Abort => { return ret; },
                ret @ TUIRet::Input { .. } => { return ret; },
                _ => {}
            }
        }
    }
*/

    pub fn draw(&mut self) {
        self.termbox.clear();
        self.ui.draw(&mut self.termbox, 0, 0);
        self.termbox.present();
    }
}

////////////////////////////////////////////////////////////////////////////////
// Showing messages

/// Target of a message coming from an IRC server.
#[derive(Debug)]
pub enum MsgTarget<'a> {
    Server { serv_name: &'a str },
    Chan { serv_name: &'a str, chan_name: &'a str },
    User { serv_name: &'a str, nick: &'a str },

    /// Show the message in all tabs of a server.
    AllServTabs { serv_name: &'a str },

    /// Show the message all server tabs that have the user. (i.e. channels,
    /// privmsg tabs)
    AllUserTabs { serv_name: &'a str, nick: &'a str },

    /// Show the message in all tabs.
    AllTabs,

    /// Show the message in currently active tab.
    CurrentTab,

    MultipleTabs(Vec<MsgTarget<'a>>),
}

impl TUI {
    /// An error message coming from Tiny, probably because of a command error
    /// etc. Those are not timestamped and not logged.
    #[inline]
    pub fn add_client_err_msg(&mut self, msg : &str, target : &MsgTarget) {
        self.ui.add_client_err_msg(msg, target);
    }

    /// A message from client, usually just to indidate progress, e.g.
    /// "Connecting...". Not timestamed and not logged.
    #[inline]
    pub fn add_client_msg(&mut self, msg : &str, target : &MsgTarget) {
        self.ui.add_client_msg(msg, target);
    }

    /// privmsg is a message coming from a server or client. Shown with sender's
    /// nick/name and receive time and logged.
    #[inline]
    pub fn add_privmsg(&mut self, sender : &str, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.ui.add_privmsg(sender, msg, tm, target);
    }

    /// A message without any explicit sender info. Useful for e.g. in server
    /// and debug log tabs. Timestamped and logged.
    #[inline]
    pub fn add_msg(&mut self, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.ui.add_msg(msg, tm, target);
    }

    /// Error messages related with the protocol - e.g. can't join a channel,
    /// nickname is in use etc. Timestamped and logged.
    #[inline]
    pub fn add_err_msg(&mut self, msg : &str, tm : &Tm, target : &MsgTarget) {
        self.ui.add_err_msg(msg, tm, target);
    }

    pub fn set_topic(&mut self, msg : &str, target : &MsgTarget) {
        self.ui.set_topic(msg, target);
    }

    #[inline]
    pub fn add_nick(&mut self, nick : &str, tm : Option<&Tm>, target : &MsgTarget) {
        self.ui.add_nick(nick, tm, target);
    }

    #[inline]
    pub fn remove_nick(&mut self, nick : &str, tm : Option<&Tm>, target : &MsgTarget) {
        self.ui.remove_nick(nick, tm, target);
    }

    #[inline]
    pub fn rename_nick(&mut self, old_nick : &str, new_nick : &str, tm : &Tm, target : &MsgTarget) {
        self.ui.rename_nick(old_nick, new_nick, tm, target);
    }
}

////////////////////////////////////////////////////////////////////////////////

// Write instance is used for debugging - messages show up in a "debug" tab.

impl Write for TUI {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        let mut line_start = 0;

        for (byte_idx, byte) in buf.iter().enumerate() {
            if *byte == b'\n' {
                if self.buffer.len() != 0 {
                    debug_assert!(line_start == 0);
                    self.buffer.extend_from_slice(&buf[ 0 .. byte_idx ]);
                    let mut msg : Vec<u8> = Vec::with_capacity(100);
                    mem::swap(&mut msg, &mut self.buffer);
                    self.add_msg(&unsafe { String::from_utf8_unchecked(msg) },
                                 &time::now(),
                                 &MsgTarget::Server { serv_name: "debug" });
                } else {
                    self.add_msg(unsafe { str::from_utf8_unchecked(&buf[ line_start .. byte_idx ]) },
                                 &time::now(),
                                 &MsgTarget::Server { serv_name: "debug" });
                }

                line_start = byte_idx + 1;
            }
        }

        self.buffer.extend_from_slice(&buf[ line_start .. ]);
        self.log_file.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.log_file.flush()
    }
}
<|endoftext|>


<commit_before>use std::cell::Cell;
use std::cell::RefCell;

use cairo::Rectangle;

use allowed_url::Fragment;
use aspect_ratio::*;
use attributes::Attribute;
use css::CssStyles;
use dpi::Dpi;
use drawing_ctx::DrawingCtx;
use error::{AttributeResultExt, RenderingError};
use float_eq_cairo::ApproxEqCairo;
use length::*;
use node::*;
use parsers::{Parse, ParseValue};
use properties::Overflow;
use property_bag::{OwnedPropertyBag, PropertyBag};
use rect::RectangleExt;
use viewbox::*;
use viewport::{draw_in_viewport, ClipMode};

pub struct NodeGroup();

impl NodeGroup {
    pub fn new() -> NodeGroup {
        NodeGroup()
    }
}

impl NodeTrait for NodeGroup {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            node.draw_children(cascaded, dc, clipping)
        })
    }
}

pub struct NodeDefs();

impl NodeDefs {
    pub fn new() -> NodeDefs {
        NodeDefs()
    }
}

impl NodeTrait for NodeDefs {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }
}

pub struct NodeSwitch();

impl NodeSwitch {
    pub fn new() -> NodeSwitch {
        NodeSwitch()
    }
}

impl NodeTrait for NodeSwitch {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            if let Some(child) = node
                .children()
                .filter(|c| c.get_type() != NodeType::Chars)
                .find(|c| c.get_cond())
            {
                dc.draw_node_from_stack(&CascadedValues::new(cascaded, &child), &child, clipping)
            } else {
                Ok(())
            }
        })
    }
}

/// Intrinsic dimensions of an SVG document fragment
#[derive(Copy, Clone)]
pub struct IntrinsicDimensions {
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,
    pub vbox: Option<ViewBox>,
}

pub struct NodeSvg {
    preserve_aspect_ratio: Cell<AspectRatio>,
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    w: Cell<LengthHorizontal>,
    h: Cell<LengthVertical>,
    vbox: Cell<Option<ViewBox>>,
    pbag: RefCell<Option<OwnedPropertyBag>>,
}

impl NodeSvg {
    pub fn new() -> NodeSvg {
        NodeSvg {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            x: Cell::new(LengthHorizontal::parse_str("0").unwrap()),
            y: Cell::new(LengthVertical::parse_str("0").unwrap()),
            w: Cell::new(LengthHorizontal::parse_str("100%").unwrap()),
            h: Cell::new(LengthVertical::parse_str("100%").unwrap()),
            vbox: Cell::new(None),
            pbag: RefCell::new(None),
        }
    }

    pub fn set_delayed_style(&self, node: &RsvgNode, css_styles: &CssStyles) {
        if let Some(owned_pbag) = self.pbag.borrow().as_ref() {
            let pbag = PropertyBag::from_owned(owned_pbag);
            node.set_style(css_styles, &pbag);
        }
    }

    pub fn get_size(&self, dpi: Dpi) -> Option<(i32, i32)> {
        match (self.w.get(), self.h.get(), self.vbox.get()) {
            (w, h, Some(vbox)) => Some((
                w.hand_normalize(dpi.x(), vbox.width, 12.0).round() as i32,
                h.hand_normalize(dpi.y(), vbox.height, 12.0).round() as i32,
            )),
            (w, h, None) if w.unit() != LengthUnit::Percent && h.unit() != LengthUnit::Percent => {
                Some((
                    w.hand_normalize(dpi.x(), 0.0, 12.0).round() as i32,
                    h.hand_normalize(dpi.y(), 0.0, 12.0).round() as i32,
                ))
            }
            (_, _, _) => None,
        }
    }

    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        // FIXME: width/height are Option<>, and we don't store that yet;
        // we resolve to 100% for default values at parsing time.
        IntrinsicDimensions {
            width: Some(self.w.get()),
            height: Some(self.h.get()),
            vbox: self.vbox.get(),
        }
    }
}

impl NodeTrait for NodeSvg {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        // SVG element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        // x & y attributes have no effect on outermost svg
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
        let is_inner_svg = node.get_parent().is_some();

        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio.set(attr.parse(value)?)
                }

                Attribute::X => {
                    if is_inner_svg {
                        self.x.set(attr.parse(value)?);
                    }
                }

                Attribute::Y => {
                    if is_inner_svg {
                        self.y.set(attr.parse(value)?);
                    }
                }

                Attribute::Width => self
                    .w
                    .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),

                Attribute::Height => self
                    .h
                    .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),

                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),

                _ => (),
            }
        }

        // The "style" sub-element is not loaded yet here, so we need
        // to store other attributes to be applied later.
        *self.pbag.borrow_mut() = Some(pbag.to_owned());

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let params = draw_ctx.get_view_params();

        let viewport = Rectangle::new(
            self.x.get().normalize(values, &params),
            self.y.get().normalize(values, &params),
            self.w.get().normalize(values, &params),
            self.h.get().normalize(values, &params),
        );

        let clip_mode = if !values.is_overflow() && node.get_parent().is_some() {
            Some(ClipMode::ClipToViewport)
        } else {
            None
        };

        draw_in_viewport(
            &viewport,
            clip_mode,
            self.vbox.get(),
            self.preserve_aspect_ratio.get(),
            node,
            values,
            draw_ctx,
            clipping,
            &mut |dc| {
                // we don't push a layer because draw_in_viewport() already does it
                node.draw_children(cascaded, dc, clipping)
            },
        )
    }
}

pub struct NodeUse {
    link: RefCell<Option<Fragment>>,
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    w: Cell<Option<LengthHorizontal>>,
    h: Cell<Option<LengthVertical>>,
}

impl NodeUse {
    pub fn new() -> NodeUse {
        NodeUse {
            link: RefCell::new(None),
            x: Cell::new(Default::default()),
            y: Cell::new(Default::default()),
            w: Cell::new(None),
            h: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeUse {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::XlinkHref => {
                    *self.link.borrow_mut() =
                        Some(Fragment::parse(value).attribute(Attribute::XlinkHref)?)
                }

                Attribute::X => self.x.set(attr.parse(value)?),
                Attribute::Y => self.y.set(attr.parse(value)?),

                Attribute::Width => self.w.set(
                    attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)
                        .map(Some)?,
                ),
                Attribute::Height => self.h.set(
                    attr.parse_and_validate(value, LengthVertical::check_nonnegative)
                        .map(Some)?,
                ),

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let link = self.link.borrow();

        if link.is_none() {
            return Ok(());
        }

        let link = link.as_ref().unwrap();

        let child = if let Some(acquired) = draw_ctx.get_acquired_node(link) {
            // Here we clone the acquired child, so that we can drop the AcquiredNode as
            // early as possible.  This is so that the child's drawing method will be able
            // to re-acquire the child for other purposes.
            acquired.get().clone()
        } else {
            rsvg_log!(
                "element {} references nonexistent \"{}\"",
                node.get_human_readable_name(),
                link,
            );
            return Ok(());
        };

        if Node::is_ancestor(child.clone(), node.clone()) {
            // or, if we're <use>'ing ourselves
            return Err(RenderingError::CircularReference);
        }

        draw_ctx.increase_num_elements_rendered_through_use(1);

        let params = draw_ctx.get_view_params();

        let nx = self.x.get().normalize(values, &params);
        let ny = self.y.get().normalize(values, &params);

        // If attributes ‘width’ and/or ‘height’ are not specified,
        // [...] use values of '100%' for these attributes.
        // From https://www.w3.org/TR/SVG/struct.html#UseElement in
        // "If the ‘use’ element references a ‘symbol’ element"

        let nw = self
            .w
            .get()
            .unwrap_or_else(|| LengthHorizontal::parse_str("100%").unwrap())
            .normalize(values, &params);
        let nh = self
            .h
            .get()
            .unwrap_or_else(|| LengthVertical::parse_str("100%").unwrap())
            .normalize(values, &params);

        // width or height set to 0 disables rendering of the element
        // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
        if nw.approx_eq_cairo(&0.0) || nh.approx_eq_cairo(&0.0) {
            return Ok(());
        }

        let viewport = Rectangle::new(nx, ny, nw, nh);

        if child.get_type() != NodeType::Symbol {
            let cr = draw_ctx.get_cairo_context();
            cr.translate(viewport.x, viewport.y);

            draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
                dc.draw_node_from_stack(
                    &CascadedValues::new_from_values(&child, values),
                    &child,
                    clipping,
                )
            })
        } else {
            child.with_impl(|symbol: &NodeSymbol| {
                let clip_mode = if !values.is_overflow()
                    || (values.overflow == Overflow::Visible && child.is_overflow())
                {
                    Some(ClipMode::ClipToVbox)
                } else {
                    None
                };

                draw_in_viewport(
                    &viewport,
                    clip_mode,
                    symbol.vbox.get(),
                    symbol.preserve_aspect_ratio.get(),
                    node,
                    values,
                    draw_ctx,
                    clipping,
                    &mut |dc| {
                        // We don't push a layer because draw_in_viewport() already does it
                        child.draw_children(
                            &CascadedValues::new_from_values(&child, values),
                            dc,
                            clipping,
                        )
                    },
                )
            })
        }
    }
}

pub struct NodeSymbol {
    preserve_aspect_ratio: Cell<AspectRatio>,
    vbox: Cell<Option<ViewBox>>,
}

impl NodeSymbol {
    pub fn new() -> NodeSymbol {
        NodeSymbol {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            vbox: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeSymbol {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        // symbol element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio.set(attr.parse(value)?)
                }

                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),

                _ => (),
            }
        }

        Ok(())
    }
}
<commit_msg>NodeSvg: extract the operation of getting the viewport to its own function<commit_after>use std::cell::Cell;
use std::cell::RefCell;

use cairo::Rectangle;

use allowed_url::Fragment;
use aspect_ratio::*;
use attributes::Attribute;
use css::CssStyles;
use dpi::Dpi;
use drawing_ctx::{DrawingCtx, ViewParams};
use error::{AttributeResultExt, RenderingError};
use float_eq_cairo::ApproxEqCairo;
use length::*;
use node::*;
use parsers::{Parse, ParseValue};
use properties::{ComputedValues, Overflow};
use property_bag::{OwnedPropertyBag, PropertyBag};
use rect::RectangleExt;
use viewbox::*;
use viewport::{draw_in_viewport, ClipMode};

pub struct NodeGroup();

impl NodeGroup {
    pub fn new() -> NodeGroup {
        NodeGroup()
    }
}

impl NodeTrait for NodeGroup {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            node.draw_children(cascaded, dc, clipping)
        })
    }
}

pub struct NodeDefs();

impl NodeDefs {
    pub fn new() -> NodeDefs {
        NodeDefs()
    }
}

impl NodeTrait for NodeDefs {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }
}

pub struct NodeSwitch();

impl NodeSwitch {
    pub fn new() -> NodeSwitch {
        NodeSwitch()
    }
}

impl NodeTrait for NodeSwitch {
    fn set_atts(&self, _: &RsvgNode, _: &PropertyBag<'_>) -> NodeResult {
        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
            if let Some(child) = node
                .children()
                .filter(|c| c.get_type() != NodeType::Chars)
                .find(|c| c.get_cond())
            {
                dc.draw_node_from_stack(&CascadedValues::new(cascaded, &child), &child, clipping)
            } else {
                Ok(())
            }
        })
    }
}

/// Intrinsic dimensions of an SVG document fragment
#[derive(Copy, Clone)]
pub struct IntrinsicDimensions {
    pub width: Option<LengthHorizontal>,
    pub height: Option<LengthVertical>,
    pub vbox: Option<ViewBox>,
}

pub struct NodeSvg {
    preserve_aspect_ratio: Cell<AspectRatio>,
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    w: Cell<LengthHorizontal>,
    h: Cell<LengthVertical>,
    vbox: Cell<Option<ViewBox>>,
    pbag: RefCell<Option<OwnedPropertyBag>>,
}

impl NodeSvg {
    pub fn new() -> NodeSvg {
        NodeSvg {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            x: Cell::new(LengthHorizontal::parse_str("0").unwrap()),
            y: Cell::new(LengthVertical::parse_str("0").unwrap()),
            w: Cell::new(LengthHorizontal::parse_str("100%").unwrap()),
            h: Cell::new(LengthVertical::parse_str("100%").unwrap()),
            vbox: Cell::new(None),
            pbag: RefCell::new(None),
        }
    }

    pub fn set_delayed_style(&self, node: &RsvgNode, css_styles: &CssStyles) {
        if let Some(owned_pbag) = self.pbag.borrow().as_ref() {
            let pbag = PropertyBag::from_owned(owned_pbag);
            node.set_style(css_styles, &pbag);
        }
    }

    pub fn get_size(&self, dpi: Dpi) -> Option<(i32, i32)> {
        match (self.w.get(), self.h.get(), self.vbox.get()) {
            (w, h, Some(vbox)) => Some((
                w.hand_normalize(dpi.x(), vbox.width, 12.0).round() as i32,
                h.hand_normalize(dpi.y(), vbox.height, 12.0).round() as i32,
            )),
            (w, h, None) if w.unit() != LengthUnit::Percent && h.unit() != LengthUnit::Percent => {
                Some((
                    w.hand_normalize(dpi.x(), 0.0, 12.0).round() as i32,
                    h.hand_normalize(dpi.y(), 0.0, 12.0).round() as i32,
                ))
            }
            (_, _, _) => None,
        }
    }

    pub fn get_intrinsic_dimensions(&self) -> IntrinsicDimensions {
        // FIXME: width/height are Option<>, and we don't store that yet;
        // we resolve to 100% for default values at parsing time.
        IntrinsicDimensions {
            width: Some(self.w.get()),
            height: Some(self.h.get()),
            vbox: self.vbox.get(),
        }
    }

    fn get_viewport(&self, values: &ComputedValues, params: &ViewParams) -> Rectangle {
        let x = self.x.get();
        let y = self.y.get();
        let w = self.w.get();
        let h = self.h.get();

        Rectangle::new(
            x.normalize(values, &params),
            y.normalize(values, &params),
            w.normalize(values, &params),
            h.normalize(values, &params),
        )
    }
}

impl NodeTrait for NodeSvg {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        // SVG element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        // x & y attributes have no effect on outermost svg
        // http://www.w3.org/TR/SVG/struct.html#SVGElement
        let is_inner_svg = node.get_parent().is_some();

        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio.set(attr.parse(value)?)
                }

                Attribute::X => {
                    if is_inner_svg {
                        self.x.set(attr.parse(value)?);
                    }
                }

                Attribute::Y => {
                    if is_inner_svg {
                        self.y.set(attr.parse(value)?);
                    }
                }

                Attribute::Width => self
                    .w
                    .set(attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)?),

                Attribute::Height => self
                    .h
                    .set(attr.parse_and_validate(value, LengthVertical::check_nonnegative)?),

                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),

                _ => (),
            }
        }

        // The "style" sub-element is not loaded yet here, so we need
        // to store other attributes to be applied later.
        *self.pbag.borrow_mut() = Some(pbag.to_owned());

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let params = draw_ctx.get_view_params();

        let clip_mode = if !values.is_overflow() && node.get_parent().is_some() {
            Some(ClipMode::ClipToViewport)
        } else {
            None
        };

        draw_in_viewport(
            &self.get_viewport(values, &params),
            clip_mode,
            self.vbox.get(),
            self.preserve_aspect_ratio.get(),
            node,
            values,
            draw_ctx,
            clipping,
            &mut |dc| {
                // we don't push a layer because draw_in_viewport() already does it
                node.draw_children(cascaded, dc, clipping)
            },
        )
    }
}

pub struct NodeUse {
    link: RefCell<Option<Fragment>>,
    x: Cell<LengthHorizontal>,
    y: Cell<LengthVertical>,
    w: Cell<Option<LengthHorizontal>>,
    h: Cell<Option<LengthVertical>>,
}

impl NodeUse {
    pub fn new() -> NodeUse {
        NodeUse {
            link: RefCell::new(None),
            x: Cell::new(Default::default()),
            y: Cell::new(Default::default()),
            w: Cell::new(None),
            h: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeUse {
    fn set_atts(&self, _: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::XlinkHref => {
                    *self.link.borrow_mut() =
                        Some(Fragment::parse(value).attribute(Attribute::XlinkHref)?)
                }

                Attribute::X => self.x.set(attr.parse(value)?),
                Attribute::Y => self.y.set(attr.parse(value)?),

                Attribute::Width => self.w.set(
                    attr.parse_and_validate(value, LengthHorizontal::check_nonnegative)
                        .map(Some)?,
                ),
                Attribute::Height => self.h.set(
                    attr.parse_and_validate(value, LengthVertical::check_nonnegative)
                        .map(Some)?,
                ),

                _ => (),
            }
        }

        Ok(())
    }

    fn draw(
        &self,
        node: &RsvgNode,
        cascaded: &CascadedValues<'_>,
        draw_ctx: &mut DrawingCtx,
        clipping: bool,
    ) -> Result<(), RenderingError> {
        let values = cascaded.get();

        let link = self.link.borrow();

        if link.is_none() {
            return Ok(());
        }

        let link = link.as_ref().unwrap();

        let child = if let Some(acquired) = draw_ctx.get_acquired_node(link) {
            // Here we clone the acquired child, so that we can drop the AcquiredNode as
            // early as possible.  This is so that the child's drawing method will be able
            // to re-acquire the child for other purposes.
            acquired.get().clone()
        } else {
            rsvg_log!(
                "element {} references nonexistent \"{}\"",
                node.get_human_readable_name(),
                link,
            );
            return Ok(());
        };

        if Node::is_ancestor(child.clone(), node.clone()) {
            // or, if we're <use>'ing ourselves
            return Err(RenderingError::CircularReference);
        }

        draw_ctx.increase_num_elements_rendered_through_use(1);

        let params = draw_ctx.get_view_params();

        let nx = self.x.get().normalize(values, &params);
        let ny = self.y.get().normalize(values, &params);

        // If attributes ‘width’ and/or ‘height’ are not specified,
        // [...] use values of '100%' for these attributes.
        // From https://www.w3.org/TR/SVG/struct.html#UseElement in
        // "If the ‘use’ element references a ‘symbol’ element"

        let nw = self
            .w
            .get()
            .unwrap_or_else(|| LengthHorizontal::parse_str("100%").unwrap())
            .normalize(values, &params);
        let nh = self
            .h
            .get()
            .unwrap_or_else(|| LengthVertical::parse_str("100%").unwrap())
            .normalize(values, &params);

        // width or height set to 0 disables rendering of the element
        // https://www.w3.org/TR/SVG/struct.html#UseElementWidthAttribute
        if nw.approx_eq_cairo(&0.0) || nh.approx_eq_cairo(&0.0) {
            return Ok(());
        }

        let viewport = Rectangle::new(nx, ny, nw, nh);

        if child.get_type() != NodeType::Symbol {
            let cr = draw_ctx.get_cairo_context();
            cr.translate(viewport.x, viewport.y);

            draw_ctx.with_discrete_layer(node, values, clipping, &mut |dc| {
                dc.draw_node_from_stack(
                    &CascadedValues::new_from_values(&child, values),
                    &child,
                    clipping,
                )
            })
        } else {
            child.with_impl(|symbol: &NodeSymbol| {
                let clip_mode = if !values.is_overflow()
                    || (values.overflow == Overflow::Visible && child.is_overflow())
                {
                    Some(ClipMode::ClipToVbox)
                } else {
                    None
                };

                draw_in_viewport(
                    &viewport,
                    clip_mode,
                    symbol.vbox.get(),
                    symbol.preserve_aspect_ratio.get(),
                    node,
                    values,
                    draw_ctx,
                    clipping,
                    &mut |dc| {
                        // We don't push a layer because draw_in_viewport() already does it
                        child.draw_children(
                            &CascadedValues::new_from_values(&child, values),
                            dc,
                            clipping,
                        )
                    },
                )
            })
        }
    }
}

pub struct NodeSymbol {
    preserve_aspect_ratio: Cell<AspectRatio>,
    vbox: Cell<Option<ViewBox>>,
}

impl NodeSymbol {
    pub fn new() -> NodeSymbol {
        NodeSymbol {
            preserve_aspect_ratio: Cell::new(AspectRatio::default()),
            vbox: Cell::new(None),
        }
    }
}

impl NodeTrait for NodeSymbol {
    fn set_atts(&self, node: &RsvgNode, pbag: &PropertyBag<'_>) -> NodeResult {
        // symbol element has overflow:hidden
        // https://www.w3.org/TR/SVG/styling.html#UAStyleSheet
        node.set_overflow_hidden();

        for (attr, value) in pbag.iter() {
            match attr {
                Attribute::PreserveAspectRatio => {
                    self.preserve_aspect_ratio.set(attr.parse(value)?)
                }

                Attribute::ViewBox => self.vbox.set(attr.parse(value).map(Some)?),

                _ => (),
            }
        }

        Ok(())
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::iter::Iterator;
use std::rc::Rc;
use std::ops::Deref;

use storage::file::File;

pub trait FilePrinter {

    fn new(verbose: bool, debug: bool) -> Self;

    /*
     * Print a single file
     */
    fn print_file(&self, Rc<RefCell<File>>);

    /*
     * Print a list of files
     */
    fn print_files<I: Iterator<Item = Rc<RefCell<File>>>>(&self, files: I) {
        for file in files {
            self.print_file(file);
        }
    }

}

struct DebugPrinter {
    debug: bool,
}

impl FilePrinter for DebugPrinter {

    fn new(_: bool, debug: bool) -> DebugPrinter {
        DebugPrinter {
            debug: debug,
        }
    }

    fn print_file(&self, f: Rc<RefCell<File>>) {
        if self.debug {
            debug!("[DebugPrinter] ->\n{:?}", f);
        }
    }

}

struct SimplePrinter {
    verbose:    bool,
    debug:      bool,
}

impl FilePrinter for SimplePrinter {

    fn new(verbose: bool, debug: bool) -> SimplePrinter {
        SimplePrinter {
            debug:      debug,
            verbose:    verbose,
        }
    }

    fn print_file(&self, f: Rc<RefCell<File>>) {
        if self.debug {
            debug!("{:?}", f);
        } else if self.verbose {
            info!("{}", &*f.deref().borrow());
        } else {
            info!("[File]: {}", f.deref().borrow().id());
        }
    }

}

pub struct TablePrinter {
    verbose:    bool,
    debug:      bool,
    sp:         SimplePrinter,
}

impl FilePrinter for TablePrinter {

    fn new(verbose: bool, debug: bool) -> TablePrinter {
        TablePrinter {
            debug:      debug,
            verbose:    verbose,
            sp:         SimplePrinter::new(verbose, debug),
        }
    }

    fn print_file(&self, f: Rc<RefCell<File>>) {
        self.sp.print_file(f);
    }

    fn print_files<I: Iterator<Item = Rc<RefCell<File>>>>(&self, files: I) {
        use prettytable::Table;
        use prettytable::row::Row;
        use prettytable::cell::Cell;

        let titles = row!["File#", "Owner", "ID"];

        let mut tab = Table::new();
        tab.set_titles(titles);

        let mut i = 0;
        for file in files {
            debug!("Printing file: {:?}", file);
            i += 1;
            let cell_i  = Cell::new(&format!("{}", i)[..]);
            let cell_o  = Cell::new(&format!("{}", file.deref().borrow().owner_name())[..]);

            let id : String = file.deref().borrow().id().clone().into();
            let cell_id = Cell::new(&id[..]);
            let row = Row::new(vec![cell_i, cell_o, cell_id]);
            tab.add_row(row);
        }

        if i != 0 {
            debug!("Printing {} table entries", i);
            tab.printstd();
        } else {
            debug!("Not printing table because there are zero entries");
        }
    }

}
<commit_msg>FilePrinter: Add print_file_custom(), print_files_custom()<commit_after>use std::cell::RefCell;
use std::iter::Iterator;
use std::rc::Rc;
use std::ops::Deref;

use storage::file::File;

pub trait FilePrinter {

    fn new(verbose: bool, debug: bool) -> Self;

    /*
     * Print a single file
     */
    fn print_file(&self, Rc<RefCell<File>>);

    /*
     * Print a list of files
     */
    fn print_files<I: Iterator<Item = Rc<RefCell<File>>>>(&self, files: I) {
        for file in files {
            self.print_file(file);
        }
    }

    fn print_file_custom<F>(&self, file: Rc<RefCell<File>>, f: &F)
        where F: Fn(Rc<RefCell<File>>) -> String
    {
        info!("{}", f(file));
    }

    fn print_files_custom<F, I>(&self, files: I, f: &F)
        where I: Iterator<Item = Rc<RefCell<File>>>,
              F: Fn(Rc<RefCell<File>>) -> String
    {
        for file in files {
            self.print_file_custom(file, f);
        }
    }

}

struct DebugPrinter {
    debug: bool,
}

impl FilePrinter for DebugPrinter {

    fn new(_: bool, debug: bool) -> DebugPrinter {
        DebugPrinter {
            debug: debug,
        }
    }

    fn print_file(&self, f: Rc<RefCell<File>>) {
        if self.debug {
            debug!("[DebugPrinter] ->\n{:?}", f);
        }
    }

}

struct SimplePrinter {
    verbose:    bool,
    debug:      bool,
}

impl FilePrinter for SimplePrinter {

    fn new(verbose: bool, debug: bool) -> SimplePrinter {
        SimplePrinter {
            debug:      debug,
            verbose:    verbose,
        }
    }

    fn print_file(&self, f: Rc<RefCell<File>>) {
        if self.debug {
            debug!("{:?}", f);
        } else if self.verbose {
            info!("{}", &*f.deref().borrow());
        } else {
            info!("[File]: {}", f.deref().borrow().id());
        }
    }

}

pub struct TablePrinter {
    verbose:    bool,
    debug:      bool,
    sp:         SimplePrinter,
}

impl FilePrinter for TablePrinter {

    fn new(verbose: bool, debug: bool) -> TablePrinter {
        TablePrinter {
            debug:      debug,
            verbose:    verbose,
            sp:         SimplePrinter::new(verbose, debug),
        }
    }

    fn print_file(&self, f: Rc<RefCell<File>>) {
        self.sp.print_file(f);
    }

    fn print_files<I: Iterator<Item = Rc<RefCell<File>>>>(&self, files: I) {
        use prettytable::Table;
        use prettytable::row::Row;
        use prettytable::cell::Cell;

        let titles = row!["File#", "Owner", "ID"];

        let mut tab = Table::new();
        tab.set_titles(titles);

        let mut i = 0;
        for file in files {
            debug!("Printing file: {:?}", file);
            i += 1;
            let cell_i  = Cell::new(&format!("{}", i)[..]);
            let cell_o  = Cell::new(&format!("{}", file.deref().borrow().owner_name())[..]);

            let id : String = file.deref().borrow().id().clone().into();
            let cell_id = Cell::new(&id[..]);
            let row = Row::new(vec![cell_i, cell_o, cell_id]);
            tab.add_row(row);
        }

        if i != 0 {
            debug!("Printing {} table entries", i);
            tab.printstd();
        } else {
            debug!("Not printing table because there are zero entries");
        }
    }

}
<|endoftext|>


<commit_before>//! Uniform state machine abstraction
//!
//! By "uniform" we mean a state machine which does the same action on any
//! event handler. I.e. it can check some global state on every wakeup,
//! timeout, or other event and react the same way.

use rotor::{Scope, Machine, Response, EventSet, Void};

struct Uniform<T: Action>(T);

trait Action: Sized {
    type Context;
    type Seed;
    fn create(seed: Self::Seed, scope: &mut Scope<Self::Context>)
        -> Response<Self, Void>;
    fn action(self, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>;
}

impl<T: Action> Machine for Uniform<T> {
    type Context = T::Context;
    type Seed = T::Seed;
    fn create(seed: Self::Seed, scope: &mut Scope<Self::Context>)
        -> Response<Self, Void>
    {
        Action::create(seed, scope).wrap(Uniform)
    }
    fn ready(self, _events: EventSet, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>
    {
        self.0.action(scope).wrap(Uniform)
    }
    fn spawned(self, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>
    {
        self.0.action(scope).wrap(Uniform)
    }
    fn timeout(self, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>
    {
        self.0.action(scope).wrap(Uniform)
    }
    fn wakeup(self, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>
    {
        self.0.action(scope).wrap(Uniform)
    }
}
<commit_msg>Internal data of `uniform::Uniform` made public<commit_after>//! Uniform state machine abstraction
//!
//! By "uniform" we mean a state machine which does the same action on any
//! event handler. I.e. it can check some global state on every wakeup,
//! timeout, or other event and react the same way.

use rotor::{Scope, Machine, Response, EventSet, Void};

struct Uniform<T: Action>(pub T);

trait Action: Sized {
    type Context;
    type Seed;
    fn create(seed: Self::Seed, scope: &mut Scope<Self::Context>)
        -> Response<Self, Void>;
    fn action(self, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>;
}

impl<T: Action> Machine for Uniform<T> {
    type Context = T::Context;
    type Seed = T::Seed;
    fn create(seed: Self::Seed, scope: &mut Scope<Self::Context>)
        -> Response<Self, Void>
    {
        Action::create(seed, scope).wrap(Uniform)
    }
    fn ready(self, _events: EventSet, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>
    {
        self.0.action(scope).wrap(Uniform)
    }
    fn spawned(self, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>
    {
        self.0.action(scope).wrap(Uniform)
    }
    fn timeout(self, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>
    {
        self.0.action(scope).wrap(Uniform)
    }
    fn wakeup(self, scope: &mut Scope<Self::Context>)
        -> Response<Self, Self::Seed>
    {
        self.0.action(scope).wrap(Uniform)
    }
}
<|endoftext|>


<commit_before>use std::cmp::Ordering;
use std::ffi::CStr;
use crate::gl;

/// Describes a version.
///
/// A version can only be compared to another version if they belong to the same API.
/// For example, both `Version(Gl, 3, 0) >= Version(GlEs, 3, 0)` and `Version(GlEs, 3, 0) >=
/// Version(Gl, 3, 0)` return `false`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Version(pub Api, pub u8, pub u8);

/// Describes an OpenGL-related API.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Api {
    /// Regular OpenGL.
    Gl,
    /// OpenGL embedded system.
    GlEs,
}

impl PartialOrd for Version {
    #[inline]
    fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
        if self.0 != other.0 {
            return None;
        }

        match self.1.cmp(&other.1) {
            Ordering::Equal => Some(self.2.cmp(&other.2)),
            a => Some(a)
        }
    }
}

/// Obtains the OpenGL version of the current context using the loaded functions.
///
/// # Unsafe
///
/// You must ensure that the functions belong to the current context, otherwise you will get
/// an undefined behavior.
pub unsafe fn get_gl_version(gl: &gl::Gl) -> Version {
    let version = gl.GetString(gl::VERSION);
    let version = String::from_utf8(CStr::from_ptr(version as *const _).to_bytes().to_vec()).unwrap();

    // for the moment we mock WebGL as OpenGL ES 2.0
    // TODO: handle the differences between WebGL and OpenGL ES
    if version.starts_with("WebGL ") {
        return Version(Api::GlEs, 2, 0);
    }

    let (version, api) = if version.starts_with("OpenGL ES ") {
        (&version[10..], Api::GlEs)
    } else if version.starts_with("OpenGL ES-") {
        (&version[13..], Api::GlEs)
    } else {
        (&version[..], Api::Gl)
    };

    let version = version.split(' ').next().expect("glGetString(GL_VERSION) returned an empty \
                                                    string");

    let mut iter = version.split(move |c: char| c == '.');
    let major = iter.next().unwrap();
    let minor = iter.next().expect("glGetString(GL_VERSION) did not return a correct version");

    Version(
        api,
        major.parse().ok().expect("failed to parse GL major version"),
        minor.parse().ok().expect("failed to parse GL minor version"),
    )
}

/// Given an API version, this function returns the GLSL version that the implementation is
/// required to support.
///
/// # Panic
///
/// Panics if the version is invalid or is not supposed to support a GLSL version.
pub fn get_supported_glsl_version(gl_version: &Version) -> Version {
    match gl_version.0 {
        Api::Gl => {
            // since OpenGL 3.3: glsl versions match gl version, just return a copy
            if *gl_version >= Version(gl_version.0, 3, 3) {
                return *gl_version;
            }

            // match to detect invalid versions
            match *gl_version {
                Version(a, 2, 0) => Version(a, 1, 1),
                Version(a, 2, 1) => Version(a, 1, 2),
                Version(a, 3, 0) => Version(a, 1, 3),
                Version(a, 3, 1) => Version(a, 1, 4),
                Version(a, 3, 2) => Version(a, 1, 5),
                _ => panic!("no corresponding glsl version exists")
            }
        },
        Api::GlEs => {
            // since OpenGL ES 3.0: glsl versions match gl version, just return a copy
            if *gl_version >= Version(gl_version.0, 3, 0) {
                return *gl_version;
            }

            // only other valid GLES version is 2.0
            if *gl_version == Version(gl_version.0, 2, 0){
                Version(Api::GlEs, 1, 0)
            } else {
                panic!("no corresponding glsl version exists")
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Version, Api, get_supported_glsl_version};

    macro_rules! assert_versions {
        ( $api:path, $gl_major:expr, $gl_minor:expr => $glsl_major:expr, $glsl_minor:expr) => {
            assert_eq!(
                get_supported_glsl_version(&Version($api, $gl_major, $gl_minor)),
                Version($api, $glsl_major, $glsl_minor)
                    );
        }
    }

    #[test]
    fn valid_gl_versions() {
        // irregular versions
        assert_versions!(Api::Gl, 2, 0 => 1, 1);
        assert_versions!(Api::Gl, 2, 1 => 1, 2);
        assert_versions!(Api::Gl, 3, 0 => 1, 3);
        assert_versions!(Api::Gl, 3, 1 => 1, 4);
        assert_versions!(Api::Gl, 3, 2 => 1, 5);

        // test a few regular versions
        assert_versions!(Api::Gl, 3, 3 => 3, 3);
        assert_versions!(Api::Gl, 4, 0 => 4, 0);
        assert_versions!(Api::Gl, 4, 5 => 4, 5);
    }

    #[test]
    fn valid_gles_versions() {
        // only irregular version
        assert_versions!(Api::GlEs, 2, 0 => 1, 0);

        // some regular versions
        assert_versions!(Api::GlEs, 3, 0 => 3, 0);
        assert_versions!(Api::GlEs, 3, 1 => 3, 1);
    }

    #[test]
    #[should_panic]
    fn invalid_gl_version() {
        get_supported_glsl_version(&Version(Api::Gl, 1, 0));
    }

    #[test]
    #[should_panic]
    fn invalid_gles_version() {
        get_supported_glsl_version(&Version(Api::GlEs, 1, 0));
    }
}
<commit_msg>Simplify split function<commit_after>use std::cmp::Ordering;
use std::ffi::CStr;
use crate::gl;

/// Describes a version.
///
/// A version can only be compared to another version if they belong to the same API.
/// For example, both `Version(Gl, 3, 0) >= Version(GlEs, 3, 0)` and `Version(GlEs, 3, 0) >=
/// Version(Gl, 3, 0)` return `false`.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Version(pub Api, pub u8, pub u8);

/// Describes an OpenGL-related API.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Api {
    /// Regular OpenGL.
    Gl,
    /// OpenGL embedded system.
    GlEs,
}

impl PartialOrd for Version {
    #[inline]
    fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
        if self.0 != other.0 {
            return None;
        }

        match self.1.cmp(&other.1) {
            Ordering::Equal => Some(self.2.cmp(&other.2)),
            a => Some(a)
        }
    }
}

/// Obtains the OpenGL version of the current context using the loaded functions.
///
/// # Unsafe
///
/// You must ensure that the functions belong to the current context, otherwise you will get
/// an undefined behavior.
pub unsafe fn get_gl_version(gl: &gl::Gl) -> Version {
    let version = gl.GetString(gl::VERSION);
    let version = String::from_utf8(CStr::from_ptr(version as *const _).to_bytes().to_vec()).unwrap();

    // for the moment we mock WebGL as OpenGL ES 2.0
    // TODO: handle the differences between WebGL and OpenGL ES
    if version.starts_with("WebGL ") {
        return Version(Api::GlEs, 2, 0);
    }

    let (version, api) = if version.starts_with("OpenGL ES ") {
        (&version[10..], Api::GlEs)
    } else if version.starts_with("OpenGL ES-") {
        (&version[13..], Api::GlEs)
    } else {
        (&version[..], Api::Gl)
    };

    let version = version.split(' ').next().expect("glGetString(GL_VERSION) returned an empty \
                                                    string");

    let mut iter = version.split('.');
    let major = iter.next().unwrap();
    let minor = iter.next().expect("glGetString(GL_VERSION) did not return a correct version");

    Version(
        api,
        major.parse().ok().expect("failed to parse GL major version"),
        minor.parse().ok().expect("failed to parse GL minor version"),
    )
}

/// Given an API version, this function returns the GLSL version that the implementation is
/// required to support.
///
/// # Panic
///
/// Panics if the version is invalid or is not supposed to support a GLSL version.
pub fn get_supported_glsl_version(gl_version: &Version) -> Version {
    match gl_version.0 {
        Api::Gl => {
            // since OpenGL 3.3: glsl versions match gl version, just return a copy
            if *gl_version >= Version(gl_version.0, 3, 3) {
                return *gl_version;
            }

            // match to detect invalid versions
            match *gl_version {
                Version(a, 2, 0) => Version(a, 1, 1),
                Version(a, 2, 1) => Version(a, 1, 2),
                Version(a, 3, 0) => Version(a, 1, 3),
                Version(a, 3, 1) => Version(a, 1, 4),
                Version(a, 3, 2) => Version(a, 1, 5),
                _ => panic!("no corresponding glsl version exists")
            }
        },
        Api::GlEs => {
            // since OpenGL ES 3.0: glsl versions match gl version, just return a copy
            if *gl_version >= Version(gl_version.0, 3, 0) {
                return *gl_version;
            }

            // only other valid GLES version is 2.0
            if *gl_version == Version(gl_version.0, 2, 0){
                Version(Api::GlEs, 1, 0)
            } else {
                panic!("no corresponding glsl version exists")
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Version, Api, get_supported_glsl_version};

    macro_rules! assert_versions {
        ( $api:path, $gl_major:expr, $gl_minor:expr => $glsl_major:expr, $glsl_minor:expr) => {
            assert_eq!(
                get_supported_glsl_version(&Version($api, $gl_major, $gl_minor)),
                Version($api, $glsl_major, $glsl_minor)
                    );
        }
    }

    #[test]
    fn valid_gl_versions() {
        // irregular versions
        assert_versions!(Api::Gl, 2, 0 => 1, 1);
        assert_versions!(Api::Gl, 2, 1 => 1, 2);
        assert_versions!(Api::Gl, 3, 0 => 1, 3);
        assert_versions!(Api::Gl, 3, 1 => 1, 4);
        assert_versions!(Api::Gl, 3, 2 => 1, 5);

        // test a few regular versions
        assert_versions!(Api::Gl, 3, 3 => 3, 3);
        assert_versions!(Api::Gl, 4, 0 => 4, 0);
        assert_versions!(Api::Gl, 4, 5 => 4, 5);
    }

    #[test]
    fn valid_gles_versions() {
        // only irregular version
        assert_versions!(Api::GlEs, 2, 0 => 1, 0);

        // some regular versions
        assert_versions!(Api::GlEs, 3, 0 => 3, 0);
        assert_versions!(Api::GlEs, 3, 1 => 3, 1);
    }

    #[test]
    #[should_panic]
    fn invalid_gl_version() {
        get_supported_glsl_version(&Version(Api::Gl, 1, 0));
    }

    #[test]
    #[should_panic]
    fn invalid_gles_version() {
        get_supported_glsl_version(&Version(Api::GlEs, 1, 0));
    }
}
<|endoftext|>