<commit_before>// Copyright 2015 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.

#![allow(unused)]

#[derive(Clone)]
struct A (B);

impl A {
    pub fn matches<F: Fn()>(&self, f: &F) {
        //~^ ERROR reached the recursion limit during monomorphization
        let &A(ref term) = self;
        term.matches(f);
    }
}

#[derive(Clone)]
enum B {
    Variant1,
    Variant2(C),
}

impl B {
    pub fn matches<F: Fn()>(&self, f: &F) {
        match self {
            &B::Variant2(ref factor) => {
                factor.matches(&|| ())
            }
            _ => unreachable!("")
        }
    }
}

#[derive(Clone)]
struct C (D);

impl C {
    pub fn matches<F: Fn()>(&self, f: &F) {
        let &C(ref base) = self;
        base.matches(&|| {
            C(base.clone()).matches(f)
        })
    }
}

#[derive(Clone)]
struct D (Box<A>);

impl D {
    pub fn matches<F: Fn()>(&self, f: &F) {
        let &D(ref a) = self;
        a.matches(f)
    }
}

pub fn matches() {
    A(B::Variant1).matches(&(|| ()))
}

fn main() {}
<commit_msg>ratchet down the recursion limit because, at least in my testing, under some configurations this still causes a stack overflow and hence a crash<commit_after>// Copyright 2015 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.

#![allow(unused)]

#![recursion_limit = "32"]

#[derive(Clone)]
struct A (B);

impl A {
    pub fn matches<F: Fn()>(&self, f: &F) {
        //~^ ERROR reached the recursion limit during monomorphization
        let &A(ref term) = self;
        term.matches(f);
    }
}

#[derive(Clone)]
enum B {
    Variant1,
    Variant2(C),
}

impl B {
    pub fn matches<F: Fn()>(&self, f: &F) {
        match self {
            &B::Variant2(ref factor) => {
                factor.matches(&|| ())
            }
            _ => unreachable!("")
        }
    }
}

#[derive(Clone)]
struct C (D);

impl C {
    pub fn matches<F: Fn()>(&self, f: &F) {
        let &C(ref base) = self;
        base.matches(&|| {
            C(base.clone()).matches(f)
        })
    }
}

#[derive(Clone)]
struct D (Box<A>);

impl D {
    pub fn matches<F: Fn()>(&self, f: &F) {
        let &D(ref a) = self;
        a.matches(f)
    }
}

pub fn matches() {
    A(B::Variant1).matches(&(|| ()))
}

fn main() {}
<|endoftext|>


<commit_before>// run-pass
// edition:2021

use Foo::*;

#[derive(Eq, PartialEq, Debug)]
enum Foo {
    A(u64),
    B(u64),
    C,
    D,
}

macro_rules! foo {
    ($orpat:pat, $val:expr) => {
        match $val {
            x @ ($orpat) => x, // leading vert would not be allowed in $orpat
            _ => B(0xDEADBEEFu64),
        }
    };
}

macro_rules! bar {
    ($orpat:pat, $val:expr) => {
        match $val {
            $orpat => 42, // leading vert allowed here
            _ => 0xDEADBEEFu64,
        }
    };
}

fn main() {
    // Test or-pattern.
    let y = foo!(A(_)|B(_), A(32));
    assert_eq!(y, A(32));

    // Leading vert in or-pattern.
    let y = bar!(|C| D, C);
    assert_eq!(y, 42u64);
}
<commit_msg>fix test<commit_after>// run-pass
// edition:2021

use Foo::*;

#[allow(dead_code)]
#[derive(Eq, PartialEq, Debug)]
enum Foo {
    A(u64),
    B(u64),
    C,
    D,
}

macro_rules! foo {
    ($orpat:pat, $val:expr) => {
        match $val {
            x @ ($orpat) => x, // leading vert would not be allowed in $orpat
            _ => B(0xDEADBEEFu64),
        }
    };
}

macro_rules! bar {
    ($orpat:pat, $val:expr) => {
        match $val {
            $orpat => 42, // leading vert allowed here
            _ => 0xDEADBEEFu64,
        }
    };
}

fn main() {
    // Test or-pattern.
    let y = foo!(A(_)|B(_), A(32));
    assert_eq!(y, A(32));

    // Leading vert in or-pattern.
    let y = bar!(|C| D, C);
    assert_eq!(y, 42u64);
}
<|endoftext|>


<commit_before>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "792c645ca7d11a8d254df307d019c5bf01445c37";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/issues/auxiliary/issue-21146-inc.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<commit_msg>Update test suite from nightly-2020-08-18 to nightly-2020-11-06<commit_after>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "9d78d1d02761b906038ba4d54c5f3427f920f5fb";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // TODO
    "src/test/ui/const-generics/argument_order.rs",
    "src/test/ui/const-generics/closing-args-token.rs",
    "src/test/ui/const-generics/const-param-before-other-params.rs",
    "src/test/ui/const-generics/defaults/intermixed-lifetime.rs",
    "src/test/ui/inline-const/const-expr-array-init.rs",
    "src/test/ui/inline-const/const-expr-basic.rs",
    "src/test/ui/inline-const/const-expr-reference.rs",
    "src/test/ui/inline-const/const-match-pat-range.rs",
    "src/test/ui/inline-const/const-match-pat.rs",
    "src/test/ui/proc-macro/unsafe-foreign-mod.rs",
    "src/test/ui/proc-macro/unsafe-mod.rs",

    // Compile-fail expr parameter in const generic position: f::<1 + 2>()
    "src/test/ui/const-generics/const-expression-parameter.rs",

    // Deprecated anonymous parameter syntax in traits
    "src/test/ui/issues/issue-13105.rs",
    "src/test/ui/issues/issue-13775.rs",
    "src/test/ui/issues/issue-34074.rs",
    "src/test/ui/proc-macro/trait-fn-args-2015.rs",

    // Not actually test cases
    "src/test/rustdoc-ui/test-compile-fail2.rs",
    "src/test/rustdoc-ui/test-compile-fail3.rs",
    "src/test/ui/include-single-expr-helper.rs",
    "src/test/ui/include-single-expr-helper-1.rs",
    "src/test/ui/issues/auxiliary/issue-21146-inc.rs",
    "src/test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "src/test/ui/lint/expansion-time-include.rs",
    "src/test/ui/macros/auxiliary/macro-comma-support.rs",
    "src/test/ui/macros/auxiliary/macro-include-items-expr.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    if path.starts_with("src/test/compile-fail") || path.starts_with("src/test/rustfix") {
        return false;
    }

    if path.starts_with("src/test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<|endoftext|>


<commit_before>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "46e85b4328fe18492894093c1092dfe509df4370";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // Deprecated anonymous parameter syntax in traits
    "test/ui/issues/issue-13105.rs",
    "test/ui/issues/issue-13775.rs",
    "test/ui/issues/issue-34074.rs",
    "test/ui/proc-macro/trait-fn-args-2015.rs",

    // not actually test cases
    "test/rustdoc-ui/test-compile-fail2.rs",
    "test/rustdoc-ui/test-compile-fail3.rs",
    "test/ui/include-single-expr-helper.rs",
    "test/ui/include-single-expr-helper-1.rs",
    "test/ui/issues/auxiliary/issue-21146-inc.rs",
    "test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "test/ui/macros/auxiliary/macro-comma-support.rs",
    "test/ui/macros/auxiliary/macro-include-items-expr.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    assert!(path_string.starts_with("tests/rust/src/"));
    let path = &path_string["tests/rust/src/".len()..];

    // TODO assert that parsing fails on the parse-fail cases
    if path.starts_with("test/parse-fail")
        || path.starts_with("test/compile-fail")
        || path.starts_with("test/rustfix")
    {
        return false;
    }

    if path.starts_with("test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust/src");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/src/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<commit_msg>Adjust location of standard library sources<commit_after>mod progress;

use self::progress::Progress;
use anyhow::Result;
use flate2::read::GzDecoder;
use std::fs;
use std::path::Path;
use tar::Archive;
use walkdir::DirEntry;

const REVISION: &str = "46e85b4328fe18492894093c1092dfe509df4370";

#[rustfmt::skip]
static EXCLUDE: &[&str] = &[
    // Deprecated anonymous parameter syntax in traits
    "test/ui/issues/issue-13105.rs",
    "test/ui/issues/issue-13775.rs",
    "test/ui/issues/issue-34074.rs",
    "test/ui/proc-macro/trait-fn-args-2015.rs",

    // not actually test cases
    "test/rustdoc-ui/test-compile-fail2.rs",
    "test/rustdoc-ui/test-compile-fail3.rs",
    "test/ui/include-single-expr-helper.rs",
    "test/ui/include-single-expr-helper-1.rs",
    "test/ui/issues/auxiliary/issue-21146-inc.rs",
    "test/ui/json-bom-plus-crlf-multifile-aux.rs",
    "test/ui/macros/auxiliary/macro-comma-support.rs",
    "test/ui/macros/auxiliary/macro-include-items-expr.rs",
];

pub fn base_dir_filter(entry: &DirEntry) -> bool {
    let path = entry.path();
    if path.is_dir() {
        return true; // otherwise walkdir does not visit the files
    }
    if path.extension().map(|e| e != "rs").unwrap_or(true) {
        return false;
    }

    let mut path_string = path.to_string_lossy();
    if cfg!(windows) {
        path_string = path_string.replace('\\', "/").into();
    }
    let path = if let Some(path) = path_string.strip_prefix("tests/rust/src/") {
        path
    } else if let Some(path) = path_string.strip_prefix("tests/rust/library/") {
        path
    } else {
        panic!("unexpected path in Rust dist: {}", path_string);
    };

    // TODO assert that parsing fails on the parse-fail cases
    if path.starts_with("test/parse-fail")
        || path.starts_with("test/compile-fail")
        || path.starts_with("test/rustfix")
    {
        return false;
    }

    if path.starts_with("test/ui") {
        let stderr_path = entry.path().with_extension("stderr");
        if stderr_path.exists() {
            // Expected to fail in some way
            return false;
        }
    }

    !EXCLUDE.contains(&path)
}

#[allow(dead_code)]
pub fn edition(path: &Path) -> &'static str {
    if path.ends_with("dyn-2015-no-warnings-without-lints.rs") {
        "2015"
    } else {
        "2018"
    }
}

pub fn clone_rust() {
    let needs_clone = match fs::read_to_string("tests/rust/COMMIT") {
        Err(_) => true,
        Ok(contents) => contents.trim() != REVISION,
    };
    if needs_clone {
        download_and_unpack().unwrap();
    }
    let mut missing = String::new();
    let test_src = Path::new("tests/rust/src");
    for exclude in EXCLUDE {
        if !test_src.join(exclude).exists() {
            missing += "\ntests/rust/src/";
            missing += exclude;
        }
    }
    if !missing.is_empty() {
        panic!("excluded test file does not exist:{}\n", missing);
    }
}

fn download_and_unpack() -> Result<()> {
    let url = format!(
        "https://github.com/rust-lang/rust/archive/{}.tar.gz",
        REVISION
    );
    let response = reqwest::blocking::get(&url)?.error_for_status()?;
    let progress = Progress::new(response);
    let decoder = GzDecoder::new(progress);
    let mut archive = Archive::new(decoder);
    let prefix = format!("rust-{}", REVISION);

    let tests_rust = Path::new("tests/rust");
    if tests_rust.exists() {
        fs::remove_dir_all(tests_rust)?;
    }

    for entry in archive.entries()? {
        let mut entry = entry?;
        let path = entry.path()?;
        if path == Path::new("pax_global_header") {
            continue;
        }
        let relative = path.strip_prefix(&prefix)?;
        let out = tests_rust.join(relative);
        entry.unpack(&out)?;
    }

    fs::write("tests/rust/COMMIT", REVISION)?;
    Ok(())
}
<|endoftext|>


<commit_before>use generate::GenerateCtx;
use testable::{IntoTestable, Testable, TestStatus, TestResult};

use std::{self, cmp};
use rand::{self, Rng, StdRng, SeedableRng};
use log::LogLevel;

pub type Result<T> = std::result::Result<T, QuickCheckError>;

#[derive(Clone, Debug)]
pub enum QuickCheckError {
    GaveUp {
        successful_tests: usize,
        attempts: usize
    },
    Failure {
        input: String,
        successful_tests: usize,
        seed: usize,
        size: usize
    },
    NoExpectedFailure
}

pub struct QuickCheck {
    tests: usize,
    max_discard_ratio: usize,
    max_size: usize,
    rng: rand::ThreadRng
}

struct QuickCheckState {
    successful_tests: usize,
    recently_discarded_tests: usize
}

impl QuickCheckState {
    fn new() -> Self {
        QuickCheckState { successful_tests: 0, recently_discarded_tests: 0 }
    }

    fn test_passed(&mut self) {
        self.successful_tests += 1;
        self.recently_discarded_tests = 0;
    }

    fn test_discarded(&mut self) {
        self.recently_discarded_tests += 1;
    }

    fn gave_up_after(&self, attempts: usize) -> Result<usize> {
        Err(QuickCheckError::GaveUp {
            successful_tests: self.successful_tests,
            attempts: attempts
        })
    }

    fn test_failed<T: Testable>(&self, testable: T, result: TestResult, seed: usize, size: usize) -> Result<usize> {
        match testable.is_expected_to_fail() {
            true => Ok(self.successful_tests),
            false => Err(QuickCheckError::Failure {
                input: result.input,
                successful_tests: self.successful_tests,
                seed: seed,
                size: size
            })
        }
    }
}

impl QuickCheck
{
    pub fn new() -> Self {
        QuickCheck {
            tests: 100,
            max_discard_ratio: 10,
            max_size: 100,
            rng: rand::thread_rng()
        }
    }

    pub fn max_size(self, max_size: usize) -> Self {
        QuickCheck {
            max_size: max_size,
            ..self
        }
    }

    pub fn quicktest<T: IntoTestable>(&mut self, t: T) -> Result<usize> {
        let testable = t.into_testable();
        let max_tests = self.tests * self.max_discard_ratio;

        let mut state = QuickCheckState::new();

        for _ in 0..max_tests {
            if state.successful_tests >= self.tests { return Ok(state.successful_tests) }

            let seed = self.rng.gen();
            let mut test_rng = StdRng::from_seed(&[seed]);
            let size = self.size(&state);
            let mut ctx = GenerateCtx::new(&mut test_rng, size);

            let result = testable.test(&mut ctx);
            self.log_result(&result);

            match result.status {
                TestStatus::Pass => state.test_passed(),
                TestStatus::Discard => state.test_discarded(),
                TestStatus::Fail => return state.test_failed(testable, result, seed, size)
            }
        }

        state.gave_up_after(max_tests)
    }

    fn log_result(&self, result: &TestResult) {
        let log_level = match result.status {
            TestStatus::Discard => LogLevel::Trace,
            _ => LogLevel::Debug
        };
        log!(log_level, "{:?}: {}", result.status, result.input);
    }

    fn size(&self, state: &QuickCheckState) -> usize {
        let n = state.successful_tests;
        let d = state.recently_discarded_tests;
        let max_size = self.max_size;

        fn round_down_to(value: usize, multiple: usize) -> usize { (value / multiple) * multiple }

        let proposed_size = {
            if (round_down_to(n, max_size) + max_size <= self.tests) || ((self.tests % max_size) == 0) {
                (n % max_size) + d / 10
            } else {
                ((n % max_size) * max_size) / (self.tests % max_size) + d / 10
            }
        };

        cmp::min(proposed_size, max_size)
    }

    pub fn quickcheck<T: IntoTestable>(&mut self, t: T) {
        let _ = ::env_logger::init();

        match self.quicktest(t) {
            Ok(ntests) => info!("(Passed {} QuickCheck tests.)", ntests),
            Err(err) => {
                match err {
                    QuickCheckError::Failure{ successful_tests: successful_tests, input: input, .. } =>
                        panic!("Falsifiable after {} tests with input {}", successful_tests, input),
                    _ => panic!("Failed: {:?}", err)
                }
            }
        }
    }
}

pub fn quicktest<T: IntoTestable>(t: T) -> Result<usize> { QuickCheck::new().quicktest(t) }
pub fn quickcheck<T: IntoTestable>(t: T) { QuickCheck::new().quickcheck(t) }
<commit_msg>Remove unnecessary pattern matching.<commit_after>use generate::GenerateCtx;
use testable::{IntoTestable, Testable, TestStatus, TestResult};

use std::{self, cmp};
use rand::{self, Rng, StdRng, SeedableRng};
use log::LogLevel;

pub type Result<T> = std::result::Result<T, QuickCheckError>;

#[derive(Clone, Debug)]
pub enum QuickCheckError {
    GaveUp {
        successful_tests: usize,
        attempts: usize
    },
    Failure {
        input: String,
        successful_tests: usize,
        seed: usize,
        size: usize
    },
    NoExpectedFailure
}

pub struct QuickCheck {
    tests: usize,
    max_discard_ratio: usize,
    max_size: usize,
    rng: rand::ThreadRng
}

struct QuickCheckState {
    successful_tests: usize,
    recently_discarded_tests: usize
}

impl QuickCheckState {
    fn new() -> Self {
        QuickCheckState { successful_tests: 0, recently_discarded_tests: 0 }
    }

    fn test_passed(&mut self) {
        self.successful_tests += 1;
        self.recently_discarded_tests = 0;
    }

    fn test_discarded(&mut self) {
        self.recently_discarded_tests += 1;
    }

    fn gave_up_after(&self, attempts: usize) -> Result<usize> {
        Err(QuickCheckError::GaveUp {
            successful_tests: self.successful_tests,
            attempts: attempts
        })
    }

    fn test_failed<T: Testable>(&self, testable: T, result: TestResult, seed: usize, size: usize) -> Result<usize> {
        match testable.is_expected_to_fail() {
            true => Ok(self.successful_tests),
            false => Err(QuickCheckError::Failure {
                input: result.input,
                successful_tests: self.successful_tests,
                seed: seed,
                size: size
            })
        }
    }
}

impl QuickCheck
{
    pub fn new() -> Self {
        QuickCheck {
            tests: 100,
            max_discard_ratio: 10,
            max_size: 100,
            rng: rand::thread_rng()
        }
    }

    pub fn max_size(self, max_size: usize) -> Self {
        QuickCheck {
            max_size: max_size,
            ..self
        }
    }

    pub fn quicktest<T: IntoTestable>(&mut self, t: T) -> Result<usize> {
        let testable = t.into_testable();
        let max_tests = self.tests * self.max_discard_ratio;

        let mut state = QuickCheckState::new();

        for _ in 0..max_tests {
            if state.successful_tests >= self.tests { return Ok(state.successful_tests) }

            let seed = self.rng.gen();
            let mut test_rng = StdRng::from_seed(&[seed]);
            let size = self.size(&state);
            let mut ctx = GenerateCtx::new(&mut test_rng, size);

            let result = testable.test(&mut ctx);
            self.log_result(&result);

            match result.status {
                TestStatus::Pass => state.test_passed(),
                TestStatus::Discard => state.test_discarded(),
                TestStatus::Fail => return state.test_failed(testable, result, seed, size)
            }
        }

        state.gave_up_after(max_tests)
    }

    fn log_result(&self, result: &TestResult) {
        let log_level = match result.status {
            TestStatus::Discard => LogLevel::Trace,
            _ => LogLevel::Debug
        };
        log!(log_level, "{:?}: {}", result.status, result.input);
    }

    fn size(&self, state: &QuickCheckState) -> usize {
        let n = state.successful_tests;
        let d = state.recently_discarded_tests;
        let max_size = self.max_size;

        fn round_down_to(value: usize, multiple: usize) -> usize { (value / multiple) * multiple }

        let proposed_size = {
            if (round_down_to(n, max_size) + max_size <= self.tests) || ((self.tests % max_size) == 0) {
                (n % max_size) + d / 10
            } else {
                ((n % max_size) * max_size) / (self.tests % max_size) + d / 10
            }
        };

        cmp::min(proposed_size, max_size)
    }

    pub fn quickcheck<T: IntoTestable>(&mut self, t: T) {
        let _ = ::env_logger::init();

        match self.quicktest(t) {
            Ok(ntests) => info!("(Passed {} QuickCheck tests.)", ntests),
            Err(err) => {
                match err {
                    QuickCheckError::Failure{ successful_tests, input, .. } =>
                        panic!("Falsifiable after {} tests with input {}", successful_tests, input),
                    _ => panic!("Failed: {:?}", err)
                }
            }
        }
    }
}

pub fn quicktest<T: IntoTestable>(t: T) -> Result<usize> { QuickCheck::new().quicktest(t) }
pub fn quickcheck<T: IntoTestable>(t: T) { QuickCheck::new().quickcheck(t) }
<|endoftext|>


<commit_before>extern crate libc;

use std::ptr;
use std::mem;
use std::ffi;
use std::str;
use std::slice::from_raw_parts;
use std::string::String;
use std::collections::HashMap;
use std::boxed::Box;
use x11::xlib;
use x11::keysym;

use super::super::libx;

use super::config::Config;
use super::workspace::{ Workspace, Workspaces };
use super::handler;

unsafe extern fn error_handler(display: *mut xlib::Display, event: *mut xlib::XErrorEvent) -> libc::c_int {
    // match event.error_code {
    //     xlib::BadAtom => {
    //         println!("bad atom");
    //     }
    //     _ => {}
    // }
    1
}

pub struct WindowManager {
    pub display: *mut xlib::Display,
    pub screen_num: libc::c_int,
    pub root:    xlib::Window,

    pub workspaces: Workspaces,
    config: Config
}

impl WindowManager {
    pub fn new() -> WindowManager {
	let display = libx::open_display(None);
        let display = match display {
            Some(p) => {
                p
            }
            None => {
                panic!("can't open display")
            }
        };

	let screen_num  = libx::default_screen(display);
	let root = libx::root_window(display, screen_num);
        println!("root window {}", root);
	let mut wm = WindowManager {
	    display: display,
            screen_num: screen_num,
	    root: root,

            config: Config::load(),
            workspaces: Workspaces::new()
        };
        wm.workspaces.create('1');
        wm
    }

    pub fn clean(&mut self) {
        libx::close_display(self.display);
    }

    pub fn handle_create(&mut self, event: &xlib::XCreateWindowEvent) {
        if event.override_redirect != 0 {
            return;
        }

        let attrs = libx::get_window_attributes(self.display, event.window);
        println!("before");
        println!("attr all event {}", attrs.all_event_masks);
        println!("attr you event {}", attrs.your_event_mask);

        // get window property
        let n = libx::get_text_property(self.display, event.window, xlib::XA_WM_NAME);
        match n {
            Some(s) => {debug!("create window {} for {}", event.window, s);}
            None => {}
        }

        // let atoms = libx::get_wm_protocols(self.display, event.window);
        // for a in atoms.iter() {
        //     println!("atom {}", a);
        //     let name = libx::get_atom_name(self.display, *a);
        //     match name {
        //         Some(n) => println!("name {}", n),
        //         None => println!("name {}", a),
        //     };
        // }
    }

    pub fn handle_destroy(&mut self, event: &xlib::XDestroyWindowEvent) {
        let workspace = self.workspaces.current();
        match workspace.contain(event.window) {
            Some(index) => {
                workspace.remove(event.window);
                workspace.config(self.display, self.screen_num);
            }
            None => {}
        }
    }

    pub fn handle_map_request(&mut self, event: &xlib::XMapRequestEvent) {
        unsafe{
            xlib::XMapWindow(self.display, event.window);

            // add app top-level window to workspace

            let attrs = libx::get_window_attributes(self.display, event.window);
            let transientfor_hint = libx::get_transient_for_hint(self.display, event.window);

            println!("trans hint {}", transientfor_hint);
            if attrs.override_redirect == 0 && transientfor_hint == 0 {
                debug!("top level window");
                let mut workspace = self.workspaces.current();
                if workspace.contain(event.window).is_none() {
                    workspace.add(event.window);
                    workspace.config(self.display, self.screen_num);
                }

                // change attributes before display
                unsafe{
                    let mut attrs: xlib::XSetWindowAttributes = mem::zeroed();
                    attrs.event_mask = xlib::KeyReleaseMask | xlib::FocusChangeMask | xlib::EnterWindowMask;
                    let valuemask = xlib::CWEventMask;
                    libx::change_window_attributes(self.display, event.window, valuemask, &mut attrs);
                }
            }
        }
    }

    pub fn handle_client_message(&mut self, event: &xlib::XClientMessageEvent) {
        println!("message type {}", event.message_type);
        let s = libx::get_atom_name(self.display, event.message_type);
        match s {
            Some(v) => println!("{}", &v),
            None => {}
        }

        match event.format {
            8 => {
                for i in 0..20 {
                    println!("{}", event.data.get_byte(i));
                }
            }
            16 => {
                for i in 0..10 {
                    println!("{}", event.data.get_short(i))
                }
            }
            32 => {
                for i in 0..5 {
                    let a = event.data.get_long(i);
                    println!("data {}", a);
                    // let s = libx::get_atom_name(self.display, a as xlib::Atom);
                    // match s {
                    //     Some(v) => println!("{}", v),
                    //     None => {}
                    // }
                }
            }
            _ => {}
        }
    }

    pub fn handle_key_release(&mut self, event: &mut xlib::XKeyEvent) {
        if event.state > 0 {
            let mut sym: xlib::KeySym = 0;
            let status: *mut xlib::XComposeStatus = ptr::null_mut();
            unsafe{
                xlib::XLookupString(event, ptr::null_mut(), 0, &mut sym, status);
            }
            let b = handler::KeyBind {
                key: sym,
                mask: event.state,
            };
            debug!("key {} {}", event.state, sym);
            // let mut h: Box<handler::Handler>;
            match self.config.bindsyms.get_mut(&b) {
                Some(handler) => {
                    handler.handle(&mut self.workspaces, self.display, self.screen_num);
                }
                None => {
                    println!("no bind");
                }
            }
        }
    }

    pub fn handle_configure_request(&mut self, event: &xlib::XConfigureRequestEvent) {
        let mut change = xlib::XWindowChanges {
            x: event.x,
            y: event.y,
            width: event.width,
            height: event.height,
            border_width: event.border_width,
            sibling: event.above,
            stack_mode: event.detail
        };
        debug!("config x: {}, y: {}, width: {}, height: {}",
               change.x, change.y, change.width, change.height);
        libx::configure_window(event.display, event.window, event.value_mask as u32, change);
    }

    pub fn handle_focus_in(&mut self, event: &xlib::XFocusChangeEvent) {
        libx::set_input_focus(self.display, event.window);
        let (window, _) = libx::get_input_focus(self.display);
        println!("window {}", window);
      }

    pub fn handle_enter(&mut self, event: &xlib::XCrossingEvent){
        println!("set focus");
        libx::set_input_focus(self.display, event.window, 0, 0);

        // if event.focus == 0 {
        // }
    }

    pub fn handle(&mut self, e: xlib::XEvent) {
        let t = e.get_type();
        match t {
            xlib::CreateNotify => {
                let event: xlib::XCreateWindowEvent = From::from(e);
                self.handle_create(&event);
            }
            xlib::DestroyNotify => {
                debug!("destroy notify");
                let event: xlib::XDestroyWindowEvent = From::from(e);
                self.handle_destroy(&event);
            }
            xlib::MapNotify => {
                debug!("map notify");
                let event: xlib::XMapEvent = From::from(e);

            }
            xlib::UnmapNotify => {
                debug!("unmap notify");
                let event: xlib::XUnmapEvent = From::from(e);
            }
            xlib::MapRequest => {
                debug!("map request");
                let event: xlib::XMapRequestEvent = From::from(e);
                self.handle_map_request(&event);
            }
            xlib::ClientMessage => {
                debug!("client message");
                // let event = cast_event::<xlib::XClientMessageEvent>(&e);
                let event: xlib::XClientMessageEvent = From::from(e);
                self.handle_client_message(&event);
            }
            xlib::KeyRelease => {
                debug!("key release");
                let mut event: xlib::XKeyEvent = From::from(e);
                self.handle_key_release(&mut event);
            }
            xlib::ConfigureNotify => {
                debug!("configure notify");
            }
            xlib::ConfigureRequest =>{
                let mut event: xlib::XConfigureRequestEvent = From::from(e);
                debug!("configure request {}", event.window);
                self.handle_configure_request(&event);
            }
            xlib::FocusIn => {
                let mut event: xlib::XFocusChangeEvent = From::from(e);
                debug!("focus in {}", event.window);
                self.handle_focus_in(&event);

            }
            xlib::FocusOut => {
                let mut event: xlib::XFocusChangeEvent = From::from(e);
                debug!("focus out {}", event.window);
            }

            xlib::EnterNotify => {
                let mut event: xlib::XCrossingEvent = From::from(e);
                debug!("enter window {}", event.window);
                self.handle_enter(&event);
            }
            _ => {
                debug!("unhandled event {}", t);
            }
        }
    }
    pub fn run(&mut self) {
        loop {
            //handle events here
            unsafe {
                let mut e = libx::next_event(self.display);
                self.handle(e);
            }
        }
    }

    pub fn init(&mut self) {
        let mask = xlib::SubstructureRedirectMask | xlib::SubstructureNotifyMask;
        let keymask = xlib::KeyPressMask | xlib::KeyReleaseMask;
        unsafe{
            // xlib::XSetErrorHandler(Some(error_handler));
            xlib::XSelectInput(self.display, self.root,
                               mask | keymask);
            xlib::XSync(self.display, 0);
        }
    }
}
<commit_msg>use set input focus wrapper<commit_after>extern crate libc;

use std::ptr;
use std::mem;
use std::ffi;
use std::str;
use std::slice::from_raw_parts;
use std::string::String;
use std::collections::HashMap;
use std::boxed::Box;
use x11::xlib;
use x11::keysym;

use super::super::libx;

use super::config::Config;
use super::workspace::{ Workspace, Workspaces };
use super::handler;

unsafe extern fn error_handler(display: *mut xlib::Display, event: *mut xlib::XErrorEvent) -> libc::c_int {
    // match event.error_code {
    //     xlib::BadAtom => {
    //         println!("bad atom");
    //     }
    //     _ => {}
    // }
    1
}

pub struct WindowManager {
    pub display: *mut xlib::Display,
    pub screen_num: libc::c_int,
    pub root:    xlib::Window,

    pub workspaces: Workspaces,
    config: Config
}

impl WindowManager {
    pub fn new() -> WindowManager {
	let display = libx::open_display(None);
        let display = match display {
            Some(p) => {
                p
            }
            None => {
                panic!("can't open display")
            }
        };

	let screen_num  = libx::default_screen(display);
	let root = libx::root_window(display, screen_num);
        println!("root window {}", root);
	let mut wm = WindowManager {
	    display: display,
            screen_num: screen_num,
	    root: root,

            config: Config::load(),
            workspaces: Workspaces::new()
        };
        wm.workspaces.create('1');
        wm
    }

    pub fn clean(&mut self) {
        libx::close_display(self.display);
    }

    pub fn handle_create(&mut self, event: &xlib::XCreateWindowEvent) {
        if event.override_redirect != 0 {
            return;
        }

        let attrs = libx::get_window_attributes(self.display, event.window);
        println!("before");
        println!("attr all event {}", attrs.all_event_masks);
        println!("attr you event {}", attrs.your_event_mask);

        // get window property
        let n = libx::get_text_property(self.display, event.window, xlib::XA_WM_NAME);
        match n {
            Some(s) => {debug!("create window {} for {}", event.window, s);}
            None => {}
        }

        // let atoms = libx::get_wm_protocols(self.display, event.window);
        // for a in atoms.iter() {
        //     println!("atom {}", a);
        //     let name = libx::get_atom_name(self.display, *a);
        //     match name {
        //         Some(n) => println!("name {}", n),
        //         None => println!("name {}", a),
        //     };
        // }
    }

    pub fn handle_destroy(&mut self, event: &xlib::XDestroyWindowEvent) {
        let workspace = self.workspaces.current();
        match workspace.contain(event.window) {
            Some(index) => {
                workspace.remove(event.window);
                workspace.config(self.display, self.screen_num);
            }
            None => {}
        }
    }

    pub fn handle_map_request(&mut self, event: &xlib::XMapRequestEvent) {
        unsafe{
            xlib::XMapWindow(self.display, event.window);

            // add app top-level window to workspace

            let attrs = libx::get_window_attributes(self.display, event.window);
            let transientfor_hint = libx::get_transient_for_hint(self.display, event.window);

            println!("trans hint {}", transientfor_hint);
            if attrs.override_redirect == 0 && transientfor_hint == 0 {
                debug!("top level window");
                let mut workspace = self.workspaces.current();
                if workspace.contain(event.window).is_none() {
                    workspace.add(event.window);
                    workspace.config(self.display, self.screen_num);
                }

                // change attributes before display
                unsafe{
                    let mut attrs: xlib::XSetWindowAttributes = mem::zeroed();
                    attrs.event_mask = xlib::KeyReleaseMask | xlib::FocusChangeMask | xlib::EnterWindowMask;
                    let valuemask = xlib::CWEventMask;
                    libx::change_window_attributes(self.display, event.window, valuemask, &mut attrs);
                }
            }
        }
    }

    pub fn handle_client_message(&mut self, event: &xlib::XClientMessageEvent) {
        println!("message type {}", event.message_type);
        let s = libx::get_atom_name(self.display, event.message_type);
        match s {
            Some(v) => println!("{}", &v),
            None => {}
        }

        match event.format {
            8 => {
                for i in 0..20 {
                    println!("{}", event.data.get_byte(i));
                }
            }
            16 => {
                for i in 0..10 {
                    println!("{}", event.data.get_short(i))
                }
            }
            32 => {
                for i in 0..5 {
                    let a = event.data.get_long(i);
                    println!("data {}", a);
                    // let s = libx::get_atom_name(self.display, a as xlib::Atom);
                    // match s {
                    //     Some(v) => println!("{}", v),
                    //     None => {}
                    // }
                }
            }
            _ => {}
        }
    }

    pub fn handle_key_release(&mut self, event: &mut xlib::XKeyEvent) {
        if event.state > 0 {
            let mut sym: xlib::KeySym = 0;
            let status: *mut xlib::XComposeStatus = ptr::null_mut();
            unsafe{
                xlib::XLookupString(event, ptr::null_mut(), 0, &mut sym, status);
            }
            let b = handler::KeyBind {
                key: sym,
                mask: event.state,
            };
            debug!("key {} {}", event.state, sym);
            // let mut h: Box<handler::Handler>;
            match self.config.bindsyms.get_mut(&b) {
                Some(handler) => {
                    handler.handle(&mut self.workspaces, self.display, self.screen_num);
                }
                None => {
                    println!("no bind");
                }
            }
        }
    }

    pub fn handle_configure_request(&mut self, event: &xlib::XConfigureRequestEvent) {
        let mut change = xlib::XWindowChanges {
            x: event.x,
            y: event.y,
            width: event.width,
            height: event.height,
            border_width: event.border_width,
            sibling: event.above,
            stack_mode: event.detail
        };
        debug!("config x: {}, y: {}, width: {}, height: {}",
               change.x, change.y, change.width, change.height);
        libx::configure_window(event.display, event.window, event.value_mask as u32, change);
    }

    pub fn handle_focus_in(&mut self, event: &xlib::XFocusChangeEvent) {
        libx::set_input_focus(self.display, event.window);
        let (window, _) = libx::get_input_focus(self.display);
        println!("window {}", window);
      }

    pub fn handle_enter(&mut self, event: &xlib::XCrossingEvent){
        println!("set focus");
        println!("is focused {}", event.focus);
        if event.focus == 0 {
            libx::set_input_focus(self.display, event.window);
        }
    }

    pub fn handle(&mut self, e: xlib::XEvent) {
        let t = e.get_type();
        match t {
            xlib::CreateNotify => {
                let event: xlib::XCreateWindowEvent = From::from(e);
                self.handle_create(&event);
            }
            xlib::DestroyNotify => {
                debug!("destroy notify");
                let event: xlib::XDestroyWindowEvent = From::from(e);
                self.handle_destroy(&event);
            }
            xlib::MapNotify => {
                debug!("map notify");
                let event: xlib::XMapEvent = From::from(e);

            }
            xlib::UnmapNotify => {
                debug!("unmap notify");
                let event: xlib::XUnmapEvent = From::from(e);
            }
            xlib::MapRequest => {
                debug!("map request");
                let event: xlib::XMapRequestEvent = From::from(e);
                self.handle_map_request(&event);
            }
            xlib::ClientMessage => {
                debug!("client message");
                // let event = cast_event::<xlib::XClientMessageEvent>(&e);
                let event: xlib::XClientMessageEvent = From::from(e);
                self.handle_client_message(&event);
            }
            xlib::KeyRelease => {
                debug!("key release");
                let mut event: xlib::XKeyEvent = From::from(e);
                self.handle_key_release(&mut event);
            }
            xlib::ConfigureNotify => {
                debug!("configure notify");
            }
            xlib::ConfigureRequest =>{
                let mut event: xlib::XConfigureRequestEvent = From::from(e);
                debug!("configure request {}", event.window);
                self.handle_configure_request(&event);
            }
            xlib::FocusIn => {
                let mut event: xlib::XFocusChangeEvent = From::from(e);
                debug!("focus in {}", event.window);
                self.handle_focus_in(&event);

            }
            xlib::FocusOut => {
                let mut event: xlib::XFocusChangeEvent = From::from(e);
                debug!("focus out {}", event.window);
            }

            xlib::EnterNotify => {
                let mut event: xlib::XCrossingEvent = From::from(e);
                debug!("enter window {}", event.window);
                self.handle_enter(&event);
            }
            _ => {
                debug!("unhandled event {}", t);
            }
        }
    }
    pub fn run(&mut self) {
        loop {
            //handle events here
            unsafe {
                let mut e = libx::next_event(self.display);
                self.handle(e);
            }
        }
    }

    pub fn init(&mut self) {
        let mask = xlib::SubstructureRedirectMask | xlib::SubstructureNotifyMask;
        let keymask = xlib::KeyPressMask | xlib::KeyReleaseMask;
        unsafe{
            // xlib::XSetErrorHandler(Some(error_handler));
            xlib::XSelectInput(self.display, self.root,
                               mask | keymask);
            xlib::XSync(self.display, 0);
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2016-2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

mod password;
mod scroll;
mod settings;

use std::error::Error;
use std::fs::{File, read_dir};
use std::io::Read;
use std::ops::Deref;
use std::mem;

use cairo::{Context, Format, ImageSurface};
use glib::{Cast, ToVariant};
use gtk::{WidgetExt, Window};
use libc::getpid;
use relm::{Component, Relm, Widget};
use relm_attributes::widget;
use webkit2gtk::{
    self,
    CookiePersistentStorage,
    Download,
    FindController,
    FindOptions,
    NavigationPolicyDecision,
    PolicyDecision,
    PolicyDecisionExt,
    PrintOperation,
    ResponsePolicyDecision,
    UserContentManager,
    UserScript,
    UserStyleSheet,
    WebContext,
    WebViewExt,
    FIND_OPTIONS_BACKWARDS,
    FIND_OPTIONS_CASE_INSENSITIVE,
    FIND_OPTIONS_WRAP_AROUND,
};
use webkit2gtk::NavigationType::LinkClicked;
use webkit2gtk::PolicyDecisionType::{self, NavigationAction, Response};
use webkit2gtk::UserContentInjectedFrames::AllFrames;
use webkit2gtk::UserScriptInjectionTime::End;
use webkit2gtk::UserStyleLevel::User;

// TODO: remove coupling between webview and app modules.
use app::{AppResult, FollowMode};
use config_dir::ConfigDir;
use message_server::{MessageServer, PATH};
use message_server::Msg::MsgRecv;
use pass_manager::PasswordManager;
use self::Msg::*;
use stylesheet::get_stylesheet_and_whitelist;
use titanium_common::Message;
use titanium_common::Message::*;

pub struct Model {
    client: usize,
    config_dir: ConfigDir,
    //find_controller: FindController,
    follow_mode: FollowMode,
    message_server: Component<MessageServer>,
    open_in_new_window: bool,
    pub password_manager: PasswordManager,
    relm: Relm<WebView>,
    scrolled_callback: Option<Box<Fn(i64)>>,
    search_backwards: bool,
}

#[derive(Msg)]
pub enum Msg {
    Action(i32),
    ClickElement,
    Close,
    GoToInsertMode,
    NewWindow(String),
    Scroll(i64),
}

#[widget]
impl Widget for WebView {
    fn init_view(&mut self) {
        //self.model.find_controller = self.view.get_find_controller().unwrap();
        let message_server = &self.model.message_server;
        connect!(message_server@MsgRecv(_, ref msg), self.model.relm, match *msg {
            ActivateAction(action) => Some(Action(action)),
            ClickHintElement() => Some(ClickElement),
            Credentials(_, _) => None, // TODO
            EnterInsertMode() => Some(GoToInsertMode),
            ScrollPercentage(percentage) => Some(Scroll(percentage)),
            _ => {
                warn!("Unexpected message received: {:?}", msg);
                None
            },
        });
    }

    fn model(relm: &Relm<Self>, config_dir: ConfigDir) -> Model {
        Model {
            client: 0, // TODO: real client ID.
            config_dir,
            //find_controller: unsafe { mem::uninitialized() }, // TODO: remove uninitialized().
            follow_mode: FollowMode::Click,
            message_server: MessageServer::new().unwrap(), // TODO: handle error elsewhere.
            open_in_new_window: false,
            password_manager: PasswordManager::new(),
            relm: relm.clone(),
            scrolled_callback: None,
            search_backwards: false,
        }
    }

    fn update(&mut self, event: Msg) {
        match event {
            ClickElement => {
                let result = self.activate_hint();
                self.hide_hints();
            },
            _ => ()
        }
    }

    view! {
        #[name="view"]
        webkit2gtk::WebView({
            user_content_manager: UserContentManager::new(), // FIXME: seems to be deallocated.
            web_context: WebView::initialize_web_extension(&self.model.config_dir)
        }) {
            close => Close,
            vexpand: true,
            // Emit the scroll event whenever the view is drawn.
            draw(_, _) => return self.emit_scrolled_event(),
            decide_policy(_, policy_decision, policy_decision_type) =>
                return self.decide_policy(policy_decision, policy_decision_type),
        }
    }
}

impl WebView {
    /// Activate the selected hint.
    pub fn activate_hint(&self) -> AppResult<()> {
        self.view.grab_focus();
        self.server_send(ActivateHint(self.model.follow_mode.to_string()))
    }

    /// Activate the link in the selection
    pub fn activate_selection(&self) -> AppResult<()> {
        self.server_send(ActivateSelection())
    }

    /// Add the user scripts.
    pub fn add_scripts(&self, config_dir: &ConfigDir) -> AppResult<()> {
        if let Some(content_manager) = self.view.get_user_content_manager() {
            content_manager.remove_all_scripts();
            let script_path = config_dir.config_file("scripts")?;
            for filename in read_dir(script_path)? {
                let mut file = File::open(filename?.path())?;
                let mut content = String::new();
                file.read_to_string(&mut content)?;
                // TODO: support whitelist as a comment in the script.
                let script = UserScript::new(&content, AllFrames, End, &[], &[]);
                content_manager.add_script(&script);
            }
        }
        Ok(())
    }

    /// Add the user stylesheets.
    pub fn add_stylesheets(&self, config_dir: &ConfigDir) -> AppResult<()> {
        if let Some(content_manager) = self.view.get_user_content_manager() {
            content_manager.remove_all_style_sheets();
            let stylesheets_path = config_dir.config_file("stylesheets")?;
            for filename in read_dir(stylesheets_path)? {
                let mut file = File::open(filename?.path())?;
                let mut content = String::new();
                file.read_to_string(&mut content)?;
                let (stylesheet, stylesheet_whitelist) = get_stylesheet_and_whitelist(&content);
                let whitelist: Vec<_> = stylesheet_whitelist.iter().map(|url| url.as_ref()).collect();
                let stylesheet = UserStyleSheet::new(&stylesheet, AllFrames, User, &whitelist, &[]);
                content_manager.add_style_sheet(&stylesheet);
            }
        }
        Ok(())
    }

    /// Add a callback for the download started event.
    pub fn connect_download_started<F: Fn(&WebContext, &Download) + 'static>(&self, callback: F) {
        if let Some(context) = self.view.get_context() {
            context.connect_download_started(callback);
        }
    }

    /// Handle the decide policy event.
    fn decide_policy(&mut self, policy_decision: &PolicyDecision, policy_decision_type: PolicyDecisionType) -> bool {
        if policy_decision_type == NavigationAction {
            if self.handle_navigation_action(policy_decision.clone()) {
                return true;
            }
        }
        else if policy_decision_type == Response && self.handle_response(policy_decision.clone()) {
            return true;
        }
        false
    }

    /// Emit the new window event.
    pub fn emit_new_window_event(&self, url: &str) {
        self.model.relm.stream().emit(NewWindow(url.to_string()));
    }

    /// Send a key to the web process to process with the current hints.
    pub fn enter_hint_key(&self, key_char: char) -> AppResult<()> {
        self.server_send(EnterHintKey(key_char))
    }

    /// Clear the current search.
    pub fn finish_search(&self) {
        self.search("");
        //self.model.find_controller.search_finish();
    }

    /// Focus the first input element.
    pub fn focus_input(&self) -> AppResult<()> {
        self.view.grab_focus();
        self.server_send(FocusInput())
    }

    /// Follow a link.
    pub fn follow_link(&self, hint_chars: &str) -> AppResult<()> {
        self.server_send(ShowHints(hint_chars.to_string()))
    }

    /// Get the web context.
    pub fn get_context(&self) -> WebContext {
        self.view.get_context().expect("No web context")
    }

    /// Handle follow link in new window.
    fn handle_navigation_action(&mut self, policy_decision: PolicyDecision) -> bool {
        let policy_decision = policy_decision.clone();
        if let Ok(policy_decision) = policy_decision.downcast::<NavigationPolicyDecision>() {
            if self.model.open_in_new_window && policy_decision.get_navigation_type() == LinkClicked {
                let url = policy_decision.get_request()
                    .and_then(|request| request.get_uri());
                if let Some(url) = url {
                    policy_decision.ignore();
                    self.model.open_in_new_window = false;
                    self.emit_new_window_event(&url);
                    return true;
                }
            }
        }
        false
    }

    /// Download file whose mime type is not supported.
    fn handle_response(&self, policy_decision: PolicyDecision) -> bool {
        let policy_decision = policy_decision.clone();
        if let Ok(policy_decision) = policy_decision.downcast::<ResponsePolicyDecision>() {
            if !policy_decision.is_mime_type_supported() {
                policy_decision.download();
                return true;
            }
        }
        false
    }

    /// Hide the hints.
    pub fn hide_hints(&self) -> AppResult<()> {
        self.server_send(HideHints())
    }

    /// Create the context and initialize the web extension.
    fn initialize_web_extension(config_dir: &ConfigDir) -> WebContext {
        let context = WebContext::get_default().unwrap();
        if cfg!(debug_assertions) {
            context.set_web_extensions_directory("titanium-web-extension/target/debug");
        }
        else {
            let install_path = env!("TITANIUM_EXTENSION_INSTALL_PATH");
            context.set_web_extensions_directory(install_path);
        }

        //let pid = unsafe { getpid() };
        //let server_name = format!("com.titanium.process{}", pid);

        context.set_web_extensions_initialization_user_data(&PATH.to_variant());

        let cookie_path = config_dir.data_file("cookies")
            .expect("cannot create data directory");
        let cookie_manager = context.get_cookie_manager().unwrap();
        cookie_manager.set_persistent_storage(cookie_path.to_str().unwrap(), CookiePersistentStorage::Sqlite);

        context
    }

    /// Open the specified URL.
    pub fn open(&self, url: &str) {
        self.view.load_uri(&url);
    }

    /// Print the current page.
    pub fn print(&self) {
        let print_operation = PrintOperation::new(&self.view);
        let window = self.view.get_toplevel()
            .and_then(|toplevel| toplevel.downcast::<Window>().ok());
        print_operation.run_dialog(window.as_ref());
    }

    /// Save a screenshot of the web view.
    pub fn screenshot(&self, path: &str) {
        let allocation = self.view.get_allocation();
        let surface = ImageSurface::create(Format::ARgb32, allocation.width, allocation.height);
        let context = Context::new(&surface);
        self.view.draw(&context);
        let mut file = File::create(path).unwrap();
        surface.write_to_png(&mut file).unwrap();
    }

    /// Search some text.
    pub fn search(&self, input: &str) {
        let default_options = FIND_OPTIONS_CASE_INSENSITIVE | FIND_OPTIONS_WRAP_AROUND;
        let other_options =
            if self.model.search_backwards {
                FIND_OPTIONS_BACKWARDS
            }
            else {
                FindOptions::empty()
            };
        let options = default_options | other_options;
        /*self.model.find_controller.search("", options.bits(), ::std::u32::MAX); // Clear previous search.
        self.model.find_controller.search(input, options.bits(), ::std::u32::MAX);*/
    }

    /// Search the next occurence of the search text.
    pub fn search_next(&self) {
        /*if self.model.search_backwards {
            self.model.find_controller.search_previous();
        }
        else {
            self.model.find_controller.search_next();
        }*/
    }

    /// Search the previous occurence of the search text.
    pub fn search_previous(&self) {
        /*if self.model.search_backwards {
            self.model.find_controller.search_next();
        }
        else {
            self.model.find_controller.search_previous();
        }*/
    }

    /// Set the value of an input[type="file"].
    pub fn select_file(&self, file: String) -> AppResult<()> {
        self.server_send(SelectFile(file))
    }

    pub fn set_follow_mode(&mut self, follow_mode: FollowMode) {
        self.model.follow_mode = follow_mode;
    }

    fn server_send(&self, message: Message) -> AppResult<()> {
        // TODO: rename widget_mut().
        self.model.message_server.widget_mut().send(self.model.client, message)
            .map_err(From::from)
    }

    /// Set open in new window boolean to true to indicate that the next follow link will open a
    /// new window.
    pub fn set_open_in_new_window(&mut self, in_new_window: bool) {
        self.model.open_in_new_window = in_new_window;
    }

    /// Set whether the search is backward or not.
    pub fn set_search_backward(&mut self, backward: bool) {
        self.model.search_backwards = backward;
    }

    /// Show the web inspector.
    pub fn show_inspector(&self) {
        static mut SHOWN: bool = false;
        if let Some(inspector) = self.view.get_inspector() {
            inspector.connect_attach(|inspector| {
                unsafe {
                    if !SHOWN {
                        inspector.detach();
                        SHOWN = true;
                        return true;
                    }
                    SHOWN = true;
                }
                false
            });
            inspector.connect_closed(|_| {
                unsafe {
                    SHOWN = false;
                }
            });
            inspector.show();
        }
    }

    /// Zoom in.
    pub fn zoom_in(&self) -> i32 {
        let level = self.view.get_zoom_level();
        self.view.set_zoom_level(level + 0.1);
        (self.view.get_zoom_level() * 100.0) as i32
    }

    /// Zoom back to 100%.
    pub fn zoom_normal(&self) -> i32 {
        self.view.set_zoom_level(1.0);
        100
    }

    /// Zoom out.
    pub fn zoom_out(&self) -> i32 {
        let level = self.view.get_zoom_level();
        self.view.set_zoom_level(level - 0.1);
        (self.view.get_zoom_level() * 100.0) as i32
    }
}

impl Deref for WebView {
    type Target = webkit2gtk::WebView;

    fn deref(&self) -> &webkit2gtk::WebView {
        &self.view
    }
}
<commit_msg>Add back search feature<commit_after>/*
 * Copyright (c) 2016-2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

mod password;
mod scroll;
mod settings;

use std::error::Error;
use std::fs::{File, read_dir};
use std::io::Read;
use std::ops::Deref;
use std::mem;

use cairo::{Context, Format, ImageSurface};
use glib::{Cast, ToVariant};
use gtk::{WidgetExt, Window};
use libc::getpid;
use relm::{Component, Relm, Widget};
use relm_attributes::widget;
use webkit2gtk::{
    self,
    CookiePersistentStorage,
    Download,
    FindController,
    FindOptions,
    NavigationPolicyDecision,
    PolicyDecision,
    PolicyDecisionExt,
    PrintOperation,
    ResponsePolicyDecision,
    UserContentManager,
    UserScript,
    UserStyleSheet,
    WebContext,
    WebViewExt,
    FIND_OPTIONS_BACKWARDS,
    FIND_OPTIONS_CASE_INSENSITIVE,
    FIND_OPTIONS_WRAP_AROUND,
};
use webkit2gtk::NavigationType::LinkClicked;
use webkit2gtk::PolicyDecisionType::{self, NavigationAction, Response};
use webkit2gtk::UserContentInjectedFrames::AllFrames;
use webkit2gtk::UserScriptInjectionTime::End;
use webkit2gtk::UserStyleLevel::User;

// TODO: remove coupling between webview and app modules.
use app::{AppResult, FollowMode};
use config_dir::ConfigDir;
use message_server::{MessageServer, PATH};
use message_server::Msg::MsgRecv;
use pass_manager::PasswordManager;
use self::Msg::*;
use stylesheet::get_stylesheet_and_whitelist;
use titanium_common::Message;
use titanium_common::Message::*;

pub struct Model {
    client: usize,
    config_dir: ConfigDir,
    find_controller: FindController,
    follow_mode: FollowMode,
    message_server: Component<MessageServer>,
    open_in_new_window: bool,
    pub password_manager: PasswordManager,
    relm: Relm<WebView>,
    scrolled_callback: Option<Box<Fn(i64)>>,
    search_backwards: bool,
}

#[derive(Msg)]
pub enum Msg {
    Action(i32),
    ClickElement,
    Close,
    GoToInsertMode,
    NewWindow(String),
    Scroll(i64),
}

#[widget]
impl Widget for WebView {
    fn init_view(&mut self) {
        self.model.find_controller = self.view.get_find_controller().unwrap();
        let message_server = &self.model.message_server;
        connect!(message_server@MsgRecv(_, ref msg), self.model.relm, match *msg {
            ActivateAction(action) => Some(Action(action)),
            ClickHintElement() => Some(ClickElement),
            Credentials(_, _) => None, // TODO
            EnterInsertMode() => Some(GoToInsertMode),
            ScrollPercentage(percentage) => Some(Scroll(percentage)),
            _ => {
                warn!("Unexpected message received: {:?}", msg);
                None
            },
        });
    }

    fn model(relm: &Relm<Self>, config_dir: ConfigDir) -> Model {
        Model {
            client: 0, // TODO: real client ID.
            config_dir,
            find_controller: unsafe { mem::uninitialized() }, // TODO: remove uninitialized().
            follow_mode: FollowMode::Click,
            message_server: MessageServer::new().unwrap(), // TODO: handle error elsewhere.
            open_in_new_window: false,
            password_manager: PasswordManager::new(),
            relm: relm.clone(),
            scrolled_callback: None,
            search_backwards: false,
        }
    }

    fn update(&mut self, event: Msg) {
        match event {
            ClickElement => {
                let result = self.activate_hint();
                self.hide_hints();
            },
            _ => ()
        }
    }

    view! {
        #[name="view"]
        webkit2gtk::WebView({
            user_content_manager: UserContentManager::new(), // FIXME: seems to be deallocated.
            web_context: WebView::initialize_web_extension(&self.model.config_dir)
        }) {
            close => Close,
            vexpand: true,
            // Emit the scroll event whenever the view is drawn.
            draw(_, _) => return self.emit_scrolled_event(),
            decide_policy(_, policy_decision, policy_decision_type) =>
                return self.decide_policy(policy_decision, policy_decision_type),
        }
    }
}

impl WebView {
    /// Activate the selected hint.
    pub fn activate_hint(&self) -> AppResult<()> {
        self.view.grab_focus();
        self.server_send(ActivateHint(self.model.follow_mode.to_string()))
    }

    /// Activate the link in the selection
    pub fn activate_selection(&self) -> AppResult<()> {
        self.server_send(ActivateSelection())
    }

    /// Add the user scripts.
    pub fn add_scripts(&self, config_dir: &ConfigDir) -> AppResult<()> {
        if let Some(content_manager) = self.view.get_user_content_manager() {
            content_manager.remove_all_scripts();
            let script_path = config_dir.config_file("scripts")?;
            for filename in read_dir(script_path)? {
                let mut file = File::open(filename?.path())?;
                let mut content = String::new();
                file.read_to_string(&mut content)?;
                // TODO: support whitelist as a comment in the script.
                let script = UserScript::new(&content, AllFrames, End, &[], &[]);
                content_manager.add_script(&script);
            }
        }
        Ok(())
    }

    /// Add the user stylesheets.
    pub fn add_stylesheets(&self, config_dir: &ConfigDir) -> AppResult<()> {
        if let Some(content_manager) = self.view.get_user_content_manager() {
            content_manager.remove_all_style_sheets();
            let stylesheets_path = config_dir.config_file("stylesheets")?;
            for filename in read_dir(stylesheets_path)? {
                let mut file = File::open(filename?.path())?;
                let mut content = String::new();
                file.read_to_string(&mut content)?;
                let (stylesheet, stylesheet_whitelist) = get_stylesheet_and_whitelist(&content);
                let whitelist: Vec<_> = stylesheet_whitelist.iter().map(|url| url.as_ref()).collect();
                let stylesheet = UserStyleSheet::new(&stylesheet, AllFrames, User, &whitelist, &[]);
                content_manager.add_style_sheet(&stylesheet);
            }
        }
        Ok(())
    }

    /// Add a callback for the download started event.
    pub fn connect_download_started<F: Fn(&WebContext, &Download) + 'static>(&self, callback: F) {
        if let Some(context) = self.view.get_context() {
            context.connect_download_started(callback);
        }
    }

    /// Handle the decide policy event.
    fn decide_policy(&mut self, policy_decision: &PolicyDecision, policy_decision_type: PolicyDecisionType) -> bool {
        if policy_decision_type == NavigationAction {
            if self.handle_navigation_action(policy_decision.clone()) {
                return true;
            }
        }
        else if policy_decision_type == Response && self.handle_response(policy_decision.clone()) {
            return true;
        }
        false
    }

    /// Emit the new window event.
    pub fn emit_new_window_event(&self, url: &str) {
        self.model.relm.stream().emit(NewWindow(url.to_string()));
    }

    /// Send a key to the web process to process with the current hints.
    pub fn enter_hint_key(&self, key_char: char) -> AppResult<()> {
        self.server_send(EnterHintKey(key_char))
    }

    /// Clear the current search.
    pub fn finish_search(&self) {
        self.search("");
        self.model.find_controller.search_finish();
    }

    /// Focus the first input element.
    pub fn focus_input(&self) -> AppResult<()> {
        self.view.grab_focus();
        self.server_send(FocusInput())
    }

    /// Follow a link.
    pub fn follow_link(&self, hint_chars: &str) -> AppResult<()> {
        self.server_send(ShowHints(hint_chars.to_string()))
    }

    /// Get the web context.
    pub fn get_context(&self) -> WebContext {
        self.view.get_context().expect("No web context")
    }

    /// Handle follow link in new window.
    fn handle_navigation_action(&mut self, policy_decision: PolicyDecision) -> bool {
        let policy_decision = policy_decision.clone();
        if let Ok(policy_decision) = policy_decision.downcast::<NavigationPolicyDecision>() {
            if self.model.open_in_new_window && policy_decision.get_navigation_type() == LinkClicked {
                let url = policy_decision.get_request()
                    .and_then(|request| request.get_uri());
                if let Some(url) = url {
                    policy_decision.ignore();
                    self.model.open_in_new_window = false;
                    self.emit_new_window_event(&url);
                    return true;
                }
            }
        }
        false
    }

    /// Download file whose mime type is not supported.
    fn handle_response(&self, policy_decision: PolicyDecision) -> bool {
        let policy_decision = policy_decision.clone();
        if let Ok(policy_decision) = policy_decision.downcast::<ResponsePolicyDecision>() {
            if !policy_decision.is_mime_type_supported() {
                policy_decision.download();
                return true;
            }
        }
        false
    }

    /// Hide the hints.
    pub fn hide_hints(&self) -> AppResult<()> {
        self.server_send(HideHints())
    }

    /// Create the context and initialize the web extension.
    fn initialize_web_extension(config_dir: &ConfigDir) -> WebContext {
        let context = WebContext::get_default().unwrap();
        if cfg!(debug_assertions) {
            context.set_web_extensions_directory("titanium-web-extension/target/debug");
        }
        else {
            let install_path = env!("TITANIUM_EXTENSION_INSTALL_PATH");
            context.set_web_extensions_directory(install_path);
        }

        //let pid = unsafe { getpid() };
        //let server_name = format!("com.titanium.process{}", pid);

        context.set_web_extensions_initialization_user_data(&PATH.to_variant());

        let cookie_path = config_dir.data_file("cookies")
            .expect("cannot create data directory");
        let cookie_manager = context.get_cookie_manager().unwrap();
        cookie_manager.set_persistent_storage(cookie_path.to_str().unwrap(), CookiePersistentStorage::Sqlite);

        context
    }

    /// Open the specified URL.
    pub fn open(&self, url: &str) {
        self.view.load_uri(&url);
    }

    /// Print the current page.
    pub fn print(&self) {
        let print_operation = PrintOperation::new(&self.view);
        let window = self.view.get_toplevel()
            .and_then(|toplevel| toplevel.downcast::<Window>().ok());
        print_operation.run_dialog(window.as_ref());
    }

    /// Save a screenshot of the web view.
    pub fn screenshot(&self, path: &str) {
        let allocation = self.view.get_allocation();
        let surface = ImageSurface::create(Format::ARgb32, allocation.width, allocation.height);
        let context = Context::new(&surface);
        self.view.draw(&context);
        let mut file = File::create(path).unwrap();
        surface.write_to_png(&mut file).unwrap();
    }

    /// Search some text.
    pub fn search(&self, input: &str) {
        let default_options = FIND_OPTIONS_CASE_INSENSITIVE | FIND_OPTIONS_WRAP_AROUND;
        let other_options =
            if self.model.search_backwards {
                FIND_OPTIONS_BACKWARDS
            }
            else {
                FindOptions::empty()
            };
        let options = default_options | other_options;
        self.model.find_controller.search("", options.bits(), ::std::u32::MAX); // Clear previous search.
        self.model.find_controller.search(input, options.bits(), ::std::u32::MAX);
    }

    /// Search the next occurence of the search text.
    pub fn search_next(&self) {
        if self.model.search_backwards {
            self.model.find_controller.search_previous();
        }
        else {
            self.model.find_controller.search_next();
        }
    }

    /// Search the previous occurence of the search text.
    pub fn search_previous(&self) {
        if self.model.search_backwards {
            self.model.find_controller.search_next();
        }
        else {
            self.model.find_controller.search_previous();
        }
    }

    /// Set the value of an input[type="file"].
    pub fn select_file(&self, file: String) -> AppResult<()> {
        self.server_send(SelectFile(file))
    }

    pub fn set_follow_mode(&mut self, follow_mode: FollowMode) {
        self.model.follow_mode = follow_mode;
    }

    fn server_send(&self, message: Message) -> AppResult<()> {
        // TODO: rename widget_mut().
        self.model.message_server.widget_mut().send(self.model.client, message)
            .map_err(From::from)
    }

    /// Set open in new window boolean to true to indicate that the next follow link will open a
    /// new window.
    pub fn set_open_in_new_window(&mut self, in_new_window: bool) {
        self.model.open_in_new_window = in_new_window;
    }

    /// Set whether the search is backward or not.
    pub fn set_search_backward(&mut self, backward: bool) {
        self.model.search_backwards = backward;
    }

    /// Show the web inspector.
    pub fn show_inspector(&self) {
        static mut SHOWN: bool = false;
        if let Some(inspector) = self.view.get_inspector() {
            inspector.connect_attach(|inspector| {
                unsafe {
                    if !SHOWN {
                        inspector.detach();
                        SHOWN = true;
                        return true;
                    }
                    SHOWN = true;
                }
                false
            });
            inspector.connect_closed(|_| {
                unsafe {
                    SHOWN = false;
                }
            });
            inspector.show();
        }
    }

    /// Zoom in.
    pub fn zoom_in(&self) -> i32 {
        let level = self.view.get_zoom_level();
        self.view.set_zoom_level(level + 0.1);
        (self.view.get_zoom_level() * 100.0) as i32
    }

    /// Zoom back to 100%.
    pub fn zoom_normal(&self) -> i32 {
        self.view.set_zoom_level(1.0);
        100
    }

    /// Zoom out.
    pub fn zoom_out(&self) -> i32 {
        let level = self.view.get_zoom_level();
        self.view.set_zoom_level(level - 0.1);
        (self.view.get_zoom_level() * 100.0) as i32
    }
}

impl Deref for WebView {
    type Target = webkit2gtk::WebView;

    fn deref(&self) -> &webkit2gtk::WebView {
        &self.view
    }
}
<|endoftext|>


<commit_before>use crate::constants::*;
use itertools::Itertools;

// Highlight lines starting with "commit: "

pub fn format_commit_line(line: &str) -> String {
    let parts = line.split('(').collect::<Vec<_>>();
    if parts.len() == 1 {
        // Just "commit: 123abc", color it all yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let commit_part = parts[0].trim();
    let without_trailing_parenthesis = parts[1].strip_suffix(')');
    if without_trailing_parenthesis == None {
        // No final parenthesis, this is weird, fall back to showing everything
        // in yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let parenthesis_parts = without_trailing_parenthesis
        .unwrap()
        .split(", ")
        .collect_vec();
    let current_branch = compute_current_branch(&parenthesis_parts);

    let comma = format!("{}, {}", YELLOW, NORMAL);
    return format!(
        "{}{} ({}){}",
        YELLOW,
        commit_part,
        parenthesis_parts
            .iter()
            .map(|part| format_commit_part(part, &current_branch))
            .join(&comma),
        NORMAL
    );
}

fn format_commit_part(part: &str, current_branch: &Option<String>) -> String {
    if part.starts_with("tag: ") {
        return format!("{}{}{}{}", BOLD, YELLOW, part, NORMAL);
    }

    // FIXME: Can we do this without to_owned()? to_owned() cretes a new string,
    // but all we want to do is compare them. How to compare without allocating
    // a new string?
    if &Some(part.to_owned()) == current_branch {
        return format!("{}{}{}{}", BOLD, GREEN, part, NORMAL);
    }

    // Handle "HEAD -> current_branch"
    if let Some(head_branch) = part.strip_prefix("HEAD -> ") {
        return format!("{}{}HEAD -> {}{}{}", BOLD, CYAN, GREEN, head_branch, NORMAL);
    }

    // Assume this is a branch, but not the current one
    return format!("{}{}{}{}", BOLD, RED, part, NORMAL);
}

fn compute_current_branch(candidates: &Vec<&str>) -> Option<String> {
    // If we find multiple options, pick the one with the lowest number of
    // slashes
    let mut fewest_slashes: Vec<&str> = vec![];
    let mut lowest_slash_count = usize::MAX;
    for candidate in candidates {
        if candidate.starts_with("tag: ") {
            // This is not a branch name
            continue;
        }

        if let Some(headless) = candidate.strip_prefix("HEAD -> ") {
            // Found it, this is conclusive!
            return Some(headless.to_owned());
        }

        let candidate_slash_count = candidate.matches('/').count();
        if candidate_slash_count > lowest_slash_count {
            // This one is worse than what we already have, never mind
            continue;
        }

        if candidate_slash_count < lowest_slash_count {
            // This one is better, replace the others
            fewest_slashes.clear();
        }

        lowest_slash_count = candidate_slash_count;
        fewest_slashes.push(candidate);
    }

    if fewest_slashes.is_empty() {
        return None;
    }

    if fewest_slashes.len() == 1 {
        return Some(fewest_slashes[0].to_owned());
    }

    // Give up, we don't know
    return None;
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_compute_current_branch() {
        // git show 686f3d7aefe9597395020ff0219eebc90e363d47
        assert_eq!(
            None,
            compute_current_branch(&"tag: 2.15".split(", ").collect_vec())
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"HEAD -> master, tag: 2.20.0, origin/master, origin/HEAD"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"tag: 2.20.0, origin/master, origin/HEAD, master"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("walles/threaded")),
            compute_current_branch(
                &"origin/walles/threaded, walles/threaded"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("xeago-master")),
            compute_current_branch(&"xeago/master, xeago-master".split(", ").collect_vec())
        );
    }
}
<commit_msg>Remove unnecessary string cloning<commit_after>use crate::constants::*;
use itertools::Itertools;

// Highlight lines starting with "commit: "

pub fn format_commit_line(line: &str) -> String {
    let parts = line.split('(').collect::<Vec<_>>();
    if parts.len() == 1 {
        // Just "commit: 123abc", color it all yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let commit_part = parts[0].trim();
    let without_trailing_parenthesis = parts[1].strip_suffix(')');
    if without_trailing_parenthesis == None {
        // No final parenthesis, this is weird, fall back to showing everything
        // in yellow
        return format!("{}{}{}", YELLOW, line, NORMAL);
    }

    let parenthesis_parts = without_trailing_parenthesis
        .unwrap()
        .split(", ")
        .collect_vec();
    let current_branch = compute_current_branch(&parenthesis_parts);

    let comma = format!("{}, {}", YELLOW, NORMAL);
    return format!(
        "{}{} ({}){}",
        YELLOW,
        commit_part,
        parenthesis_parts
            .iter()
            .map(|part| format_commit_part(part, &current_branch))
            .join(&comma),
        NORMAL
    );
}

fn format_commit_part(part: &str, current_branch: &Option<String>) -> String {
    if part.starts_with("tag: ") {
        return format!("{}{}{}{}", BOLD, YELLOW, part, NORMAL);
    }

    // FIXME: Can we do this with one readable if-statement instead?
    if let Some(current_branch_4_realz) = current_branch {
        if current_branch_4_realz == part {
            return format!("{}{}{}{}", BOLD, GREEN, part, NORMAL);
        }
    }

    // Handle "HEAD -> current_branch"
    if let Some(head_branch) = part.strip_prefix("HEAD -> ") {
        return format!("{}{}HEAD -> {}{}{}", BOLD, CYAN, GREEN, head_branch, NORMAL);
    }

    // Assume this is a branch, but not the current one
    return format!("{}{}{}{}", BOLD, RED, part, NORMAL);
}

fn compute_current_branch(candidates: &Vec<&str>) -> Option<String> {
    // If we find multiple options, pick the one with the lowest number of
    // slashes
    let mut fewest_slashes: Vec<&str> = vec![];
    let mut lowest_slash_count = usize::MAX;
    for candidate in candidates {
        if candidate.starts_with("tag: ") {
            // This is not a branch name
            continue;
        }

        if let Some(headless) = candidate.strip_prefix("HEAD -> ") {
            // Found it, this is conclusive!
            return Some(headless.to_owned());
        }

        let candidate_slash_count = candidate.matches('/').count();
        if candidate_slash_count > lowest_slash_count {
            // This one is worse than what we already have, never mind
            continue;
        }

        if candidate_slash_count < lowest_slash_count {
            // This one is better, replace the others
            fewest_slashes.clear();
        }

        lowest_slash_count = candidate_slash_count;
        fewest_slashes.push(candidate);
    }

    if fewest_slashes.is_empty() {
        return None;
    }

    if fewest_slashes.len() == 1 {
        return Some(fewest_slashes[0].to_owned());
    }

    // Give up, we don't know
    return None;
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_compute_current_branch() {
        // git show 686f3d7aefe9597395020ff0219eebc90e363d47
        assert_eq!(
            None,
            compute_current_branch(&"tag: 2.15".split(", ").collect_vec())
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"HEAD -> master, tag: 2.20.0, origin/master, origin/HEAD"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("master")),
            compute_current_branch(
                &"tag: 2.20.0, origin/master, origin/HEAD, master"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("walles/threaded")),
            compute_current_branch(
                &"origin/walles/threaded, walles/threaded"
                    .split(", ")
                    .collect_vec()
            )
        );

        assert_eq!(
            Some(String::from("xeago-master")),
            compute_current_branch(&"xeago/master, xeago-master".split(", ").collect_vec())
        );
    }
}
<|endoftext|>


<commit_before>use comm::*;
use option::swap_unwrap;
use platform::osmain;
use osmain::{OSMain, AddKeyHandler};
use opts::{Opts, Screen, Png};
use engine::{Engine, ExitMsg, LoadURLMsg};
use resource::image_cache_task::ImageCacheTask;
use resource::resource_task::ResourceTask;

use url_to_str = std::net::url::to_str;
use util::url::make_url;

use pipes::{Port, Chan};

fn main() {
    let args = os::args();
    run(&opts::from_cmdline_args(args))
}

#[allow(non_implicitly_copyable_typarams)]
fn run(opts: &Opts) {
    match opts.render_mode {
      Screen => run_pipeline_screen(opts.urls),
      Png(outfile) => {
        assert opts.urls.is_not_empty();
        if opts.urls.len() > 1u {
            fail ~"servo asks that you stick to a single URL in PNG output mode"
        }
        run_pipeline_png(opts.urls.head(), outfile)
      }
    }
}

fn run_pipeline_screen(urls: &[~str]) {

    // The platform event handler thread
    let osmain = OSMain();

    // Send each file to render then wait for keypress
    let (keypress_to_engine, keypress_from_osmain) = pipes::stream();
    osmain.send(AddKeyHandler(keypress_to_engine));

    // Create a servo instance
    let resource_task = ResourceTask();
    let image_cache_task = ImageCacheTask(resource_task);
    let engine = Engine(osmain, resource_task, image_cache_task);
    let engine_chan = engine.start();

    for urls.each |filename| {
        let url = make_url(*filename, None);
        #debug["master: Sending url `%s`", url_to_str(url)];
        engine_chan.send(LoadURLMsg(url));
        #debug["master: Waiting for keypress"];

        match keypress_from_osmain.try_recv() {
          Some(*) => { }
          None => { #error("keypress stream closed unexpectedly") }
        };
    }

    // Shut everything down
    #debug["master: Shut down"];
    let (exit_chan, exit_response_from_engine) = pipes::stream();
    engine_chan.send(engine::ExitMsg(exit_chan));
    exit_response_from_engine.recv();

    osmain.send(osmain::Exit);
}

fn run_pipeline_png(+url: ~str, outfile: &str) {
    // Use a PNG encoder as the graphics compositor
    use gfx::png_compositor;
    use png_compositor::PngCompositor;
    use io::{Writer, buffered_file_writer};
    use resource::resource_task::ResourceTask;
    use resource::image_cache_task::SyncImageCacheTask;

    listen(|pngdata_from_compositor| {
        let compositor = PngCompositor(pngdata_from_compositor);
        let resource_task = ResourceTask();
        // For the PNG pipeline we are using a synchronous image task so that all images will be
        // fulfilled before the first paint.
        let image_cache_task = SyncImageCacheTask(resource_task);
        let engine_task = Engine(compositor, resource_task, image_cache_task);
        let engine_chan = engine_task.start();
        engine_chan.send(LoadURLMsg(make_url(url, None)));

        match buffered_file_writer(&Path(outfile)) {
          Ok(writer) => writer.write(pngdata_from_compositor.recv()),
          Err(e) => fail e
        }

        let (exit_chan, exit_response_from_engine) = pipes::stream();
        engine_chan.send(engine::ExitMsg(exit_chan));
        exit_response_from_engine.recv();
        compositor.send(png_compositor::Exit);
    })
}
<commit_msg>Eliminate some copy warnings<commit_after>use comm::*;
use option::swap_unwrap;
use platform::osmain;
use osmain::{OSMain, AddKeyHandler};
use opts::{Opts, Screen, Png};
use engine::{Engine, ExitMsg, LoadURLMsg};
use resource::image_cache_task::ImageCacheTask;
use resource::resource_task::ResourceTask;

use util::url::make_url;

use pipes::{Port, Chan};

fn main() {
    let args = os::args();
    run(&opts::from_cmdline_args(args))
}

#[allow(non_implicitly_copyable_typarams)]
fn run(opts: &Opts) {
    match opts.render_mode {
      Screen => run_pipeline_screen(opts.urls),
      Png(outfile) => {
        assert opts.urls.is_not_empty();
        if opts.urls.len() > 1u {
            fail ~"servo asks that you stick to a single URL in PNG output mode"
        }
        run_pipeline_png(opts.urls.head(), outfile)
      }
    }
}

fn run_pipeline_screen(urls: &[~str]) {

    // The platform event handler thread
    let osmain = OSMain();

    // Send each file to render then wait for keypress
    let (keypress_to_engine, keypress_from_osmain) = pipes::stream();
    osmain.send(AddKeyHandler(keypress_to_engine));

    // Create a servo instance
    let resource_task = ResourceTask();
    let image_cache_task = ImageCacheTask(resource_task);
    let engine = Engine(osmain, resource_task, image_cache_task);
    let engine_chan = engine.start();

    for urls.each |filename| {
        let url = make_url(copy *filename, None);
        #debug["master: Sending url `%s`", url.to_str()];
        engine_chan.send(LoadURLMsg(url));
        #debug["master: Waiting for keypress"];

        match keypress_from_osmain.try_recv() {
          Some(*) => { }
          None => { #error("keypress stream closed unexpectedly") }
        };
    }

    // Shut everything down
    #debug["master: Shut down"];
    let (exit_chan, exit_response_from_engine) = pipes::stream();
    engine_chan.send(engine::ExitMsg(exit_chan));
    exit_response_from_engine.recv();

    osmain.send(osmain::Exit);
}

fn run_pipeline_png(+url: ~str, outfile: &str) {
    // Use a PNG encoder as the graphics compositor
    use gfx::png_compositor;
    use png_compositor::PngCompositor;
    use io::{Writer, buffered_file_writer};
    use resource::resource_task::ResourceTask;
    use resource::image_cache_task::SyncImageCacheTask;

    listen(|pngdata_from_compositor| {
        let compositor = PngCompositor(pngdata_from_compositor);
        let resource_task = ResourceTask();
        // For the PNG pipeline we are using a synchronous image task so that all images will be
        // fulfilled before the first paint.
        let image_cache_task = SyncImageCacheTask(resource_task);
        let engine_task = Engine(compositor, resource_task, image_cache_task);
        let engine_chan = engine_task.start();
        engine_chan.send(LoadURLMsg(make_url(copy url, None)));

        match buffered_file_writer(&Path(outfile)) {
          Ok(writer) => writer.write(pngdata_from_compositor.recv()),
          Err(e) => fail e
        }

        let (exit_chan, exit_response_from_engine) = pipes::stream();
        engine_chan.send(engine::ExitMsg(exit_chan));
        exit_response_from_engine.recv();
        compositor.send(png_compositor::Exit);
    })
}
<|endoftext|>