<commit_before>#![feature(unboxed_closures)]
#![feature(globs)]
#![feature(phase)]
#![feature(macro_rules)]
#[phase(plugin, link)]
extern crate log;
#[phase(plugin)]
extern crate wtftw_core;

extern crate wtftw_core;

use wtftw_core::window_system::*;
use wtftw_core::window_manager::*;
use wtftw_core::handlers::default::*;
use wtftw_core::config::*;
use wtftw_core::util::*;
use wtftw_core::layout::*;
use wtftw_core::layout::Direction::*;
use wtftw_core::layout::LayoutMessage::*;
use wtftw_core::core::*;

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.spacing = 0;
    config.general.border_color = 0x3f3f4c;
    config.general.focus_border_color = 0x525263;
    config.general.terminal = (String::from_str("urxvt"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), ResizableTallLayout::new())),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), MirrorLayout::new(ResizableTallLayout::new()))),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), CenterLayout::new(ResizableTallLayout::new()))),
        NoBordersLayout::new(box FullLayout)));

    config.general.tags = (vec!("1: term", "2: web", "3: code",
                                "4: media", "5: steam", "6: latex",
                                "7: music", "8: im", "9: rest"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x|
x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm, |&: m, w, c| m.windows(w, c, |x| x.swap_master()));
    add_key_handler_str!(config, w, "c", modm, |&: m, w, c| m.kill_window(w).windows(w, c, |x| x.clone()));

    add_key_handler_str!(config, w, "t", modm, |&: m, w, c| {
        match m.workspaces.peek() {
            Some(window) => m.windows(w, c, |x| x.sink(window)),
            None => m.clone()
        }
    });

    // Layout messages
    add_key_handler_str!(config, w, "h",      modm, send_layout_message!(LayoutMessage::Decrease));
    add_key_handler_str!(config, w, "l",      modm, send_layout_message!(LayoutMessage::Increase));
    add_key_handler_str!(config, w, "comma",  modm, send_layout_message!(LayoutMessage::IncreaseMaster));
    add_key_handler_str!(config, w, "period", modm, send_layout_message!(LayoutMessage::DecreaseMaster));
    add_key_handler_str!(config, w, "space",  modm, send_layout_message!(LayoutMessage::Next));

    // Workspace switching and moving
    for i in range(1u, 10) {
        add_key_handler_str!(config, w, i.to_string().as_slice(), modm,
            move |&: m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, i.to_string().as_slice(), modm | SHIFTMASK,
            move |&: m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK, run!("amixer", Some("-q set Master 5%-")));
    add_key_handler_str!(config, w, "k", modm | CONTROLMASK, run!("amixer", Some("-q set Master 5%+")));

    add_key_handler_code!(config, 0x1008ff11, NONEMASK, run!("amixer", Some("-q set Master 5%-")));
    add_key_handler_code!(config, 0x1008ff13, NONEMASK, run!("amixer", Some("-q set Master 5%+")));

    add_key_handler_code!(config, 0x1008ff02, NONEMASK, run!("xbacklight", Some("+10")));
    add_key_handler_code!(config, 0x1008ff03, NONEMASK, run!("xbacklight", Some("-10")));

    add_mouse_handler!(config, BUTTON1, modm,
            |&: m, w, c, s| {
                m.focus(s, w, c).mouse_move_window(w, c, s).windows(w, c, |x| x.shift_master())
            });
    add_mouse_handler!(config, BUTTON3, modm,
            |&: m, w, c, s| {
                m.focus(s, w, c).mouse_resize_window(w, c, s).windows(w, c, |x| x.shift_master())
            });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |&: workspaces, window_system, window| {
                match window_system.get_class_name(window).as_slice() {
                    "MPlayer" => spawn_on(workspaces, window_system, window, 3),
                    "vlc"     => spawn_on(workspaces, window_system, window, 3),
                    _         => workspaces.clone()
                }
            });

    // Xmobar handling and formatting
    let mut xmobar = spawn_pipe(config, String::from_str("xmobar"),
                                Some(String::from_str("/home/rootnode/.xmonad/xmobar1.hs")));
    let tags = config.general.tags.clone();
    config.set_log_hook(box move |&mut: m, _| {
        let p = &mut xmobar;
        let tags = &tags;
        let workspaces = tags.clone().iter()
            .enumerate()
            .map(|(i, x)| if i as u32 == m.workspaces.current.workspace.id {
                format!("[<fc=#f07746>{}</fc>] ", x)
            } else {
                format!("[{}] ", x)
            })
            .fold(String::from_str(""), |a, x| {
                let mut r = a.clone();
                r.push_str(x.as_slice());
                r
            });

        let content = format!("{} {}", workspaces, m.workspaces.current.workspace.layout.description());
        match p.deref().write().deref_mut().stdin.as_mut() {
            Some(pin) => match pin.write_line(content.as_slice()) {
                _ => ()
            },
            _ => ()
        }
    });
}

<commit_msg>Don't attempt to start xmobar unless we know its config file exists and don't use hardcoded home directories.<commit_after>#![feature(unboxed_closures)]
#![feature(globs)]
#![feature(phase)]
#![feature(macro_rules)]
#[phase(plugin, link)]
extern crate log;
#[phase(plugin)]
extern crate wtftw_core;

extern crate wtftw_core;

use std::io::fs::PathExtensions;
use std::os::homedir;
use wtftw_core::window_system::*;
use wtftw_core::window_manager::*;
use wtftw_core::handlers::default::*;
use wtftw_core::config::*;
use wtftw_core::util::*;
use wtftw_core::layout::*;
use wtftw_core::layout::Direction::*;
use wtftw_core::layout::LayoutMessage::*;

#[no_mangle]
pub extern fn configure(_: &mut WindowManager, w: &WindowSystem, config: &mut Config) {
    let modm = MOD1MASK;

    config.general.mod_mask = modm;
    config.general.spacing = 0;
    config.general.border_color = 0x3f3f4c;
    config.general.focus_border_color = 0x525263;
    config.general.terminal = (String::from_str("urxvt"), String::from_str(""));
    config.general.layout = LayoutCollection::new(vec!(
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), ResizableTallLayout::new())),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), MirrorLayout::new(ResizableTallLayout::new()))),
        GapLayout::new(16, AvoidStrutsLayout::new(vec!(Direction::Up), CenterLayout::new(ResizableTallLayout::new()))),
        NoBordersLayout::new(box FullLayout)));

    config.general.tags = (vec!("1: term", "2: web", "3: code",
                                "4: media", "5: steam", "6: latex",
                                "7: music", "8: im", "9: rest"))
        .into_iter().map(String::from_str).collect();

    // Register key handlers

    // Some standard key handlers for starting, restarting, etc.
    add_key_handler_str!(config, w, "q",      modm | SHIFTMASK, exit);
    add_key_handler_str!(config, w, "q",      modm,             restart);
    add_key_handler_str!(config, w, "Return", modm | SHIFTMASK, start_terminal);
    add_key_handler_str!(config, w, "p",      modm,             start_launcher);

    // Focus and window movement
    add_key_handler_str!(config, w, "j", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_down()));
    add_key_handler_str!(config, w, "k", modm, |&: m, w, c| m.windows(w, c, |x| x.focus_up()));
    add_key_handler_str!(config, w, "j", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x|
x.swap_down()));
    add_key_handler_str!(config, w, "k", modm | SHIFTMASK, |&: m, w, c| m.windows(w, c, |x| x.swap_up()));
    add_key_handler_str!(config, w, "Return", modm, |&: m, w, c| m.windows(w, c, |x| x.swap_master()));
    add_key_handler_str!(config, w, "c", modm, |&: m, w, c| m.kill_window(w).windows(w, c, |x| x.clone()));

    add_key_handler_str!(config, w, "t", modm, |&: m, w, c| {
        match m.workspaces.peek() {
            Some(window) => m.windows(w, c, |x| x.sink(window)),
            None => m.clone()
        }
    });

    // Layout messages
    add_key_handler_str!(config, w, "h",      modm, send_layout_message!(LayoutMessage::Decrease));
    add_key_handler_str!(config, w, "l",      modm, send_layout_message!(LayoutMessage::Increase));
    add_key_handler_str!(config, w, "comma",  modm, send_layout_message!(LayoutMessage::IncreaseMaster));
    add_key_handler_str!(config, w, "period", modm, send_layout_message!(LayoutMessage::DecreaseMaster));
    add_key_handler_str!(config, w, "space",  modm, send_layout_message!(LayoutMessage::Next));

    // Workspace switching and moving
    for i in range(1u, 10) {
        add_key_handler_str!(config, w, i.to_string().as_slice(), modm,
            move |&: m, w, c| switch_to_workspace(m, w, c, i - 1));

        add_key_handler_str!(config, w, i.to_string().as_slice(), modm | SHIFTMASK,
            move |&: m, w, c| move_window_to_workspace(m, w, c, i - 1));
    }

    // Media keys
    add_key_handler_str!(config, w, "j", modm | CONTROLMASK, run!("amixer", Some("-q set Master 5%-")));
    add_key_handler_str!(config, w, "k", modm | CONTROLMASK, run!("amixer", Some("-q set Master 5%+")));

    add_key_handler_code!(config, 0x1008ff11, NONEMASK, run!("amixer", Some("-q set Master 5%-")));
    add_key_handler_code!(config, 0x1008ff13, NONEMASK, run!("amixer", Some("-q set Master 5%+")));

    add_key_handler_code!(config, 0x1008ff02, NONEMASK, run!("xbacklight", Some("+10")));
    add_key_handler_code!(config, 0x1008ff03, NONEMASK, run!("xbacklight", Some("-10")));

    add_mouse_handler!(config, BUTTON1, modm,
            |&: m, w, c, s| {
                m.focus(s, w, c).mouse_move_window(w, c, s).windows(w, c, |x| x.shift_master())
            });
    add_mouse_handler!(config, BUTTON3, modm,
            |&: m, w, c, s| {
                m.focus(s, w, c).mouse_resize_window(w, c, s).windows(w, c, |x| x.shift_master())
            });

    // Place specific applications on specific workspaces
    config.set_manage_hook(box |&: workspaces, window_system, window| {
                match window_system.get_class_name(window).as_slice() {
                    "MPlayer" => spawn_on(workspaces, window_system, window, 3),
                    "vlc"     => spawn_on(workspaces, window_system, window, 3),
                    _         => workspaces.clone()
                }
            });

    // Xmobar handling and formatting
    let home = homedir().unwrap().to_c_str();
    let xmobar_config = format!("{}/.wtftw/xmobar.hs", home);

    if Path::new(xmobar_config.clone()).is_file() {
        let mut xmobar = spawn_pipe(config, String::from_str("xmobar"),
                                    Some(xmobar_config));
        let tags = config.general.tags.clone();
        config.set_log_hook(box move |&mut: m, _| {
            let p = &mut xmobar;
            let tags = &tags;
            let workspaces = tags.clone().iter()
                .enumerate()
                .map(|(i, x)| if i as u32 == m.workspaces.current.workspace.id {
                    format!("[<fc=#f07746>{}</fc>] ", x)
                } else {
                    format!("[{}] ", x)
                })
                .fold(String::from_str(""), |a, x| {
                    let mut r = a.clone();
                    r.push_str(x.as_slice());
                    r
                });

            let content = format!("{} {}", workspaces, m.workspaces.current.workspace.layout.description());
            match p.deref().write().deref_mut().stdin.as_mut() {
                Some(pin) => match pin.write_line(content.as_slice()) {
                    _ => ()
                },
                _ => ()
            }
        });
    };
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate reqwest;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
extern crate toml;
extern crate uuid;
#[macro_use]
extern crate failure;
extern crate flate2;
extern crate git2;
extern crate tar;

mod cmd;
mod fetch_configlet;
mod utils;

use clap::{App, Arg, ArgMatches, SubCommand};
use cmd::{configure, generate, update};

// Creates a new CLI app with appropriate matches
// and returns the initialized matches.
fn init_app<'a>() -> ArgMatches<'a> {
    App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .subcommand(
            SubCommand::with_name("generate")
                .about("Generates new exercise")
                .arg(Arg::with_name("exercise_name").required(true).help("The name of the generated exercise"))
                .arg(Arg::with_name("configure").long("configure").short("c").help(
                    "If set, the command will edit the config.json file after generating the exercise",
                ))
                .arg(Arg::with_name("use_maplit").long("use-maplit").short("m").help("Use the maplit crate to improve the readability of the generated test suite")),
        )
        .subcommand(
            SubCommand::with_name("update")
                .about("Updates the specified exercise")
                .arg(Arg::with_name("exercise_name").help("The name of the updated exercise"))
                .arg(Arg::with_name("use_maplit").long("use-maplit").short("m").help("Use the maplit crate to improve the readability of the updated test suite"))
                .arg(Arg::with_name("configure").long("configure").short("c").help(
                    "If set, the command will edit the config.json file after updating the exercise",
                ))
        )
        .subcommand(
            SubCommand::with_name("configure")
                .about("Edits config.json for the specified exercise")
                .arg(Arg::with_name("exercise_name").required(true).help("The name of the configured exercise")),
        )
        .get_matches()
}

// Determine which subcommand was used
// and call the appropriate function.
fn process_matches(matches: &ArgMatches) {
    match matches.subcommand() {
        ("generate", Some(generate_matches)) => {
            let exercise_name = generate_matches.value_of("exercise_name").unwrap();

            let run_configure = generate_matches.is_present("configure");

            let use_maplit = generate_matches.is_present("use_maplit");

            generate::generate_exercise(exercise_name, use_maplit);

            if run_configure {
                configure::configure_exercise(exercise_name);
            }
        },

        ("update", Some(update_matches)) => {
            let exercise_name = update_matches.value_of("exercise_name").unwrap();

            let run_configure = update_matches.is_present("configure");

            let use_maplit = update_matches.is_present("use_maplit");

            update::update_exercise(exercise_name, use_maplit);

            if run_configure {
                configure::configure_exercise(exercise_name);
            }
        },

        ("configure", Some(configure_matches)) => {
            configure::configure_exercise(configure_matches.value_of("exercise_name").unwrap())
        }

        ("", None) => {
            println!("No subcommand was used.\nUse init_exercise --help to learn about the possible subcommands.")
        }

        _ => unreachable!(),
    }
}

fn main() {
    let matches = init_app();

    process_matches(&matches);
}
<commit_msg>exercise: Added fetch_configlet subcommand<commit_after>extern crate clap;
extern crate reqwest;
#[macro_use]
extern crate serde_json;
#[macro_use]
extern crate lazy_static;
extern crate toml;
extern crate uuid;
#[macro_use]
extern crate failure;
extern crate flate2;
extern crate git2;
extern crate tar;

mod cmd;
mod fetch_configlet;
mod utils;

use clap::{App, Arg, ArgMatches, SubCommand};
use cmd::{configure, generate, update};

// Creates a new CLI app with appropriate matches
// and returns the initialized matches.
fn init_app<'a>() -> ArgMatches<'a> {
    App::new(env!("CARGO_PKG_NAME"))
        .version(env!("CARGO_PKG_VERSION"))
        .about(env!("CARGO_PKG_DESCRIPTION"))
        .subcommand(
            SubCommand::with_name("generate")
                .about("Generates new exercise")
                .arg(Arg::with_name("exercise_name").required(true).help("The name of the generated exercise"))
                .arg(Arg::with_name("configure").long("configure").short("c").help(
                    "If set, the command will edit the config.json file after generating the exercise",
                ))
                .arg(Arg::with_name("use_maplit").long("use-maplit").short("m").help("Use the maplit crate to improve the readability of the generated test suite")),
        )
        .subcommand(
            SubCommand::with_name("update")
                .about("Updates the specified exercise")
                .arg(Arg::with_name("exercise_name").help("The name of the updated exercise"))
                .arg(Arg::with_name("use_maplit").long("use-maplit").short("m").help("Use the maplit crate to improve the readability of the updated test suite"))
                .arg(Arg::with_name("configure").long("configure").short("c").help(
                    "If set, the command will edit the config.json file after updating the exercise",
                ))
        )
        .subcommand(
            SubCommand::with_name("configure")
                .about("Edits config.json for the specified exercise")
                .arg(Arg::with_name("exercise_name").required(true).help("The name of the configured exercise")),
        )
        .subcommand(
            SubCommand::with_name("fetch_configlet")
                .about("Downloads and extracts configlet utility into the repo's /bin directory")
        )
        .get_matches()
}

// Determine which subcommand was used
// and call the appropriate function.
fn process_matches(matches: &ArgMatches) {
    match matches.subcommand() {
        ("generate", Some(generate_matches)) => {
            let exercise_name = generate_matches.value_of("exercise_name").unwrap();

            let run_configure = generate_matches.is_present("configure");

            let use_maplit = generate_matches.is_present("use_maplit");

            generate::generate_exercise(exercise_name, use_maplit);

            if run_configure {
                configure::configure_exercise(exercise_name);
            }
        },

        ("update", Some(update_matches)) => {
            let exercise_name = update_matches.value_of("exercise_name").unwrap();

            let run_configure = update_matches.is_present("configure");

            let use_maplit = update_matches.is_present("use_maplit");

            update::update_exercise(exercise_name, use_maplit);

            if run_configure {
                configure::configure_exercise(exercise_name);
            }
        },

        ("configure", Some(configure_matches)) => {
            configure::configure_exercise(configure_matches.value_of("exercise_name").unwrap())
        }

        ("fetch_configlet", Some(_fetch_configlet_matches)) => {
            if let Ok(fetch_path) = fetch_configlet::download() {
                println!("Downloaded and moved the configlet utility to the {:?} path", fetch_path);
            } else {
                println!("Failed to fetch the configlet utility");
            }
        }

        ("", None) => {
            println!("No subcommand was used.\nUse init_exercise --help to learn about the possible subcommands.")
        }

        _ => unreachable!(),
    }
}

fn main() {
    let matches = init_app();

    process_matches(&matches);
}
<|endoftext|>


<commit_before>use super::camera::*;
use super::event_manager::*;
use glium::backend::glutin_backend::GlutinFacade;
use glium::glutin::{CursorState, ElementState, Event, MouseButton, VirtualKeyCode};

pub struct Ghost {
    cam: Camera,
    context: GlutinFacade,
    forward: bool,
    backward: bool,
    left: bool,
    right: bool,
    up: bool,
    down: bool,
    mouselock: bool,
}

impl Ghost {
    pub fn new(context: GlutinFacade) -> Self {
        Ghost {
            cam: Camera::default(),
            context: context,
            forward: false,
            backward: false,
            left: false,
            right: false,
            up: false,
            down: false,
            mouselock: false,
        }
    }
    pub fn update(&mut self) {
        if self.forward {
            self.cam.move_forward(1.0);
        }
        if self.backward {
            self.cam.move_backward(1.0);
        }
        if self.left {
            self.cam.move_left(1.0);
        }
        if self.right {
            self.cam.move_right(1.0);
        }
        if self.up {
            self.cam.move_up(1.0);
        }
        if self.down {
            self.cam.move_down(1.0);
        }
    }

    pub fn get_camera(&self) -> Camera {
        self.cam
    }
}

/// **Implements Controls**
/// *W => FORWARD
/// *A => LEFT
/// *S => BACKWARDS
/// *D => RIGHT
/// *'Space' => UP
/// *'LControl' => DOWN (only accepts one keystroke (cannot hold LControl to go
/// down))
/// *'C' => DOWN (as a replacement for 'LControl')

/// MouseMovement for changing the direction the camera looks
impl EventHandler for Ghost {
    fn handle_event(&mut self, e: &Event) -> EventResponse {
        match *e {
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::W)) => {
                self.forward = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::W)) => {
                self.forward = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::S)) => {
                self.backward = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::S)) => {
                self.backward = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::A)) => {
                self.left = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::A)) => {
                self.left = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::D)) => {
                self.right = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::D)) => {
                self.right = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Space)) => {
                self.up = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::Space)) => {
                self.up = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::C)) => {
                self.down = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::C)) => {
                self.down = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::LControl)) => {
                self.down = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::LControl)) => {
                self.down = false;
                EventResponse::Continue
            }

            Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                if !self.mouselock {
                    self.mouselock = true;
                    if let Some(window) = self.context.get_window() {
                        let res = window.set_cursor_state(CursorState::Hide);
                        warn!("{:?} a", res);
                    } else {
                        warn!("Failed to obtain window from facade");
                    }
                } else if self.mouselock {
                    self.mouselock = false;

                    if let Some(window) = self.context.get_window() {
                        let res = window.set_cursor_state(CursorState::Normal);
                        warn!("{:?} b", res);
                    } else {
                        warn!("Failed to obtain window from facade");
                    }
                }

                EventResponse::Continue
            }

            Event::MouseMoved(x, y) => {
                if self.mouselock {
                    if let Some(window) = self.context.get_window() {
                        // Possibility of mouse being outside of window without it resetting to the
                        // middle?
                        if let Some(middle) = window.get_inner_size_pixels() {
                            let middle_x = (middle.0 as i32) / 2;
                            let middle_y = (middle.1 as i32) / 2;
                            let x_diff = x - middle_x;
                            let y_diff = y - middle_y;
                            self.cam.change_dir(y_diff as f32 / 300.0, -x_diff as f32 / 300.0);
                            let res = window.set_cursor_position(middle_x as i32, middle_y as i32);
                            warn!("{:?} c", res);
                        }
                    }
                }
                EventResponse::Continue
            }
            _ => EventResponse::NotHandled,
        }
    }
}
<commit_msg>remove 'C' from Controls because LControl works<commit_after>use super::camera::*;
use super::event_manager::*;
use glium::backend::glutin_backend::GlutinFacade;
use glium::glutin::{CursorState, ElementState, Event, MouseButton, VirtualKeyCode};

pub struct Ghost {
    cam: Camera,
    context: GlutinFacade,
    forward: bool,
    backward: bool,
    left: bool,
    right: bool,
    up: bool,
    down: bool,
    mouselock: bool,
}

impl Ghost {
    pub fn new(context: GlutinFacade) -> Self {
        Ghost {
            cam: Camera::default(),
            context: context,
            forward: false,
            backward: false,
            left: false,
            right: false,
            up: false,
            down: false,
            mouselock: false,
        }
    }
    pub fn update(&mut self) {
        if self.forward {
            self.cam.move_forward(1.0);
        }
        if self.backward {
            self.cam.move_backward(1.0);
        }
        if self.left {
            self.cam.move_left(1.0);
        }
        if self.right {
            self.cam.move_right(1.0);
        }
        if self.up {
            self.cam.move_up(1.0);
        }
        if self.down {
            self.cam.move_down(1.0);
        }
    }

    pub fn get_camera(&self) -> Camera {
        self.cam
    }
}

/// **Implements Controls**
/// *W => FORWARD
/// *A => LEFT
/// *S => BACKWARDS
/// *D => RIGHT
/// *'Space' => UP
/// *'LControl' => DOWN (only accepts one keystroke (cannot hold LControl to go
/// down))
/// *'C' => DOWN (as a replacement for 'LControl')

/// MouseMovement for changing the direction the camera looks
impl EventHandler for Ghost {
    fn handle_event(&mut self, e: &Event) -> EventResponse {
        match *e {
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::W)) => {
                self.forward = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::W)) => {
                self.forward = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::S)) => {
                self.backward = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::S)) => {
                self.backward = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::A)) => {
                self.left = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::A)) => {
                self.left = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::D)) => {
                self.right = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::D)) => {
                self.right = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Space)) => {
                self.up = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::Space)) => {
                self.up = false;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::LControl)) => {
                self.down = true;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::LControl)) => {
                self.down = false;
                EventResponse::Continue
            }

            Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                if !self.mouselock {
                    self.mouselock = true;
                    if let Some(window) = self.context.get_window() {
                        let res = window.set_cursor_state(CursorState::Hide);
                        warn!("{:?} a", res);
                    } else {
                        warn!("Failed to obtain window from facade");
                    }
                } else if self.mouselock {
                    self.mouselock = false;

                    if let Some(window) = self.context.get_window() {
                        let res = window.set_cursor_state(CursorState::Normal);
                        warn!("{:?} b", res);
                    } else {
                        warn!("Failed to obtain window from facade");
                    }
                }

                EventResponse::Continue
            }

            Event::MouseMoved(x, y) => {
                if self.mouselock {
                    if let Some(window) = self.context.get_window() {
                        // Possibility of mouse being outside of window without it resetting to the
                        // middle?
                        if let Some(middle) = window.get_inner_size_pixels() {
                            let middle_x = (middle.0 as i32) / 2;
                            let middle_y = (middle.1 as i32) / 2;
                            let x_diff = x - middle_x;
                            let y_diff = y - middle_y;
                            self.cam.change_dir(y_diff as f32 / 300.0, -x_diff as f32 / 300.0);
                            window.set_cursor_position(middle_x as i32, middle_y as i32)
                                .expect("setting cursor position failed");
                        }
                    }
                }
                EventResponse::Continue
            }
            _ => EventResponse::NotHandled,
        }
    }
}
<|endoftext|>


<commit_before>use bot::LalafellBot;
use commands::*;
use error::*;
use database::TimeoutUser;

use discord::builders::EmbedBuilder;
use discord::model::{PublicChannel, UserId};
use discord::model::permissions;

use chrono::prelude::*;

use std::sync::Arc;

const USAGE: &'static str = "!timeout <who> <length>";

pub struct TimeoutCommand {
  bot: Arc<LalafellBot>
}

impl TimeoutCommand {
  pub fn new(bot: Arc<LalafellBot>) -> TimeoutCommand {
    TimeoutCommand {
      bot: bot
    }
  }

  fn parse_duration(duration: &str) -> Result<u64> {
    let mut str_length = 0;
    let mut total_time = 0;
    while str_length < duration.len() {
      let numbers: String = duration.chars()
        .skip(str_length)
        .take_while(|c| c.is_numeric())
        .collect();
      str_length += numbers.len();
      let length: u64 = numbers.parse().chain_err(|| "could not parse duration length")?;
      let units: String = duration.chars()
        .skip(str_length)
        .take_while(|c| c.is_alphabetic() || c.is_whitespace())
        .collect();
      str_length += units.len();
      let multiplier = match units.trim().to_lowercase().as_ref() {
        "" if total_time == 0 => 1,
        "s" | "sec" | "secs" | "second" | "seconds" => 1,
        "m" | "min" | "mins" | "minute" | "minutes" => 60,
        "h" | "hr" | "hrs" | "hour" | "hours" => 3600,
        "d" | "ds" | "day" | "days" => 86400,
        _ => return Err("invalid unit".into())
      };
      total_time += length * multiplier;
    }
    Ok(total_time)
  }
}

impl HasBot for TimeoutCommand {
  fn bot(&self) -> Arc<LalafellBot> {
    self.bot.clone()
  }
}

impl<'a> PublicChannelCommand<'a> for TimeoutCommand {
  fn run(&self, message: &Message, channel: &PublicChannel, params: &[&str]) -> CommandResult<'a> {
    let server_id = channel.server_id;
    let state_option = self.bot.state.read().unwrap();
    let state = state_option.as_ref().unwrap();
    let server = match state.servers().iter().find(|x| x.id == server_id) {
      Some(s) => s,
      None => {
        let err: error::Error = "could not find server for channel".into();
        return Err(err.into());
      }
    };
    let can_manage_roles = server.permissions_for(channel.id, message.author.id).contains(permissions::MANAGE_ROLES);
    if !can_manage_roles {
      return Err(ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .title("Not enough permissions.")
          .description("You don't have enough permissions to use this command."))
        .wrap());
    }

    if params.len() < 2 {
      return Err(ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .title("Not enough parameters.")
          .description(USAGE))
        .wrap());
    }
    let server_id = channel.server_id;
    let who = params[0];
    let who = if !who.starts_with("<@") && !who.ends_with('>') && message.mentions.len() != 1 {
      who.parse::<u64>().map(UserId).map_err(|_| ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .title("Invalid target.")
          .description("The target was not a mention, and it was not a user ID."))
        .wrap())?
    } else {
      message.mentions[0].id
    };

    let role_id = {
      let state_option = self.bot.state.read().unwrap();
      let state = state_option.as_ref().unwrap();
      let live_server = match state.servers().iter().find(|s| s.id == server_id) {
        Some(s) => s,
        None => return Err(ExternalCommandFailure::default()
          .message(|e: EmbedBuilder| e
            .description("Could not find the server in the bot state. This is a bug."))
          .wrap())
      };

      match timeout::set_up_timeouts(self.bot.as_ref(), live_server) {
        Ok(r) => {
          if let Err(e) = self.bot.discord.add_user_to_role(live_server.id, who, r) {
            warn!("could not add user {} to timeout role: {}", who.0, e);
          }
          r
        },
        Err(e) => {
          warn!("could not set up timeouts for {}: {}", live_server.id.0, e);
          return Err(ExternalCommandFailure::default()
            .message(|e: EmbedBuilder| e
              .description("Could not set up timeouts for this server. Do I have enough permissions?"))
            .wrap());
        }
      }
    };

    let duration = match TimeoutCommand::parse_duration(&params[1..].to_vec().into_iter().collect::<String>()) {
      Ok(d) => d,
      Err(_) => return Err(ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .description("Invalid time length. Try \"15m\" or \"3 hours\" for example."))
        .wrap())
    };

    let mut database = self.bot.database.write().unwrap();
    if database.timeouts.iter().any(|u| u.user_id == who.0 && u.server_id == server_id.0) {
      return Err(ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .description("That user is already timed out."))
        .wrap());
    }

    let timeout_user = TimeoutUser::new(server_id.0, who.0, role_id.0, duration as i64, UTC::now().timestamp());
    database.timeouts.push(timeout_user);
    Ok(CommandSuccess::default())
  }
}
<commit_msg>fix: mention who is already timed out<commit_after>use bot::LalafellBot;
use commands::*;
use error::*;
use database::TimeoutUser;

use discord::builders::EmbedBuilder;
use discord::model::{PublicChannel, UserId};
use discord::model::permissions;

use chrono::prelude::*;

use std::sync::Arc;

const USAGE: &'static str = "!timeout <who> <length>";

pub struct TimeoutCommand {
  bot: Arc<LalafellBot>
}

impl TimeoutCommand {
  pub fn new(bot: Arc<LalafellBot>) -> TimeoutCommand {
    TimeoutCommand {
      bot: bot
    }
  }

  fn parse_duration(duration: &str) -> Result<u64> {
    let mut str_length = 0;
    let mut total_time = 0;
    while str_length < duration.len() {
      let numbers: String = duration.chars()
        .skip(str_length)
        .take_while(|c| c.is_numeric())
        .collect();
      str_length += numbers.len();
      let length: u64 = numbers.parse().chain_err(|| "could not parse duration length")?;
      let units: String = duration.chars()
        .skip(str_length)
        .take_while(|c| c.is_alphabetic() || c.is_whitespace())
        .collect();
      str_length += units.len();
      let multiplier = match units.trim().to_lowercase().as_ref() {
        "" if total_time == 0 => 1,
        "s" | "sec" | "secs" | "second" | "seconds" => 1,
        "m" | "min" | "mins" | "minute" | "minutes" => 60,
        "h" | "hr" | "hrs" | "hour" | "hours" => 3600,
        "d" | "ds" | "day" | "days" => 86400,
        _ => return Err("invalid unit".into())
      };
      total_time += length * multiplier;
    }
    Ok(total_time)
  }
}

impl HasBot for TimeoutCommand {
  fn bot(&self) -> Arc<LalafellBot> {
    self.bot.clone()
  }
}

impl<'a> PublicChannelCommand<'a> for TimeoutCommand {
  fn run(&self, message: &Message, channel: &PublicChannel, params: &[&str]) -> CommandResult<'a> {
    let server_id = channel.server_id;
    let state_option = self.bot.state.read().unwrap();
    let state = state_option.as_ref().unwrap();
    let server = match state.servers().iter().find(|x| x.id == server_id) {
      Some(s) => s,
      None => {
        let err: error::Error = "could not find server for channel".into();
        return Err(err.into());
      }
    };
    let can_manage_roles = server.permissions_for(channel.id, message.author.id).contains(permissions::MANAGE_ROLES);
    if !can_manage_roles {
      return Err(ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .title("Not enough permissions.")
          .description("You don't have enough permissions to use this command."))
        .wrap());
    }

    if params.len() < 2 {
      return Err(ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .title("Not enough parameters.")
          .description(USAGE))
        .wrap());
    }
    let server_id = channel.server_id;
    let who = params[0];
    let who = if !who.starts_with("<@") && !who.ends_with('>') && message.mentions.len() != 1 {
      who.parse::<u64>().map(UserId).map_err(|_| ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .title("Invalid target.")
          .description("The target was not a mention, and it was not a user ID."))
        .wrap())?
    } else {
      message.mentions[0].id
    };

    let role_id = {
      let state_option = self.bot.state.read().unwrap();
      let state = state_option.as_ref().unwrap();
      let live_server = match state.servers().iter().find(|s| s.id == server_id) {
        Some(s) => s,
        None => return Err(ExternalCommandFailure::default()
          .message(|e: EmbedBuilder| e
            .description("Could not find the server in the bot state. This is a bug."))
          .wrap())
      };

      match timeout::set_up_timeouts(self.bot.as_ref(), live_server) {
        Ok(r) => {
          if let Err(e) = self.bot.discord.add_user_to_role(live_server.id, who, r) {
            warn!("could not add user {} to timeout role: {}", who.0, e);
          }
          r
        },
        Err(e) => {
          warn!("could not set up timeouts for {}: {}", live_server.id.0, e);
          return Err(ExternalCommandFailure::default()
            .message(|e: EmbedBuilder| e
              .description("Could not set up timeouts for this server. Do I have enough permissions?"))
            .wrap());
        }
      }
    };

    let duration = match TimeoutCommand::parse_duration(&params[1..].to_vec().into_iter().collect::<String>()) {
      Ok(d) => d,
      Err(_) => return Err(ExternalCommandFailure::default()
        .message(|e: EmbedBuilder| e
          .description("Invalid time length. Try \"15m\" or \"3 hours\" for example."))
        .wrap())
    };

    let mut database = self.bot.database.write().unwrap();
    if database.timeouts.iter().any(|u| u.user_id == who.0 && u.server_id == server_id.0) {
      return Err(ExternalCommandFailure::default()
        .message(move |e: EmbedBuilder| e
          .description(&format!("{} is already timed out.", who.mention())))
        .wrap());
    }

    let timeout_user = TimeoutUser::new(server_id.0, who.0, role_id.0, duration as i64, UTC::now().timestamp());
    database.timeouts.push(timeout_user);
    Ok(CommandSuccess::default())
  }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{BorderRadius, ClipMode, HitTestItem, HitTestResult, ItemTag, PrimitiveFlags};
use api::{PipelineId, ApiHitTester, ClipId};
use api::units::*;
use crate::clip::{ClipItemKind, ClipStore, ClipNode, rounded_rectangle_contains_point};
use crate::clip::{polygon_contains_point};
use crate::prim_store::PolygonKey;
use crate::scene_builder_thread::Interners;
use crate::spatial_tree::{SpatialNodeIndex, SpatialTree, get_external_scroll_offset};
use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo};
use std::ops;
use std::sync::{Arc, Mutex};
use crate::util::{LayoutToWorldFastTransform, VecHelper};

pub struct SharedHitTester {
    // We don't really need a mutex here. We could do with some sort of
    // atomic-atomic-ref-counted pointer (an Arc which would let the pointer
    // be swapped atomically like an AtomicPtr).
    // In practive this shouldn't cause performance issues, though.
    hit_tester: Mutex<Arc<HitTester>>,
}

impl SharedHitTester {
    pub fn new() -> Self {
        SharedHitTester {
            hit_tester: Mutex::new(Arc::new(HitTester::empty())),
        }
    }

    pub fn get_ref(&self) -> Arc<HitTester> {
        let guard = self.hit_tester.lock().unwrap();
        Arc::clone(&*guard)
    }

    pub(crate) fn update(&self, new_hit_tester: Arc<HitTester>) {
        let mut guard = self.hit_tester.lock().unwrap();
        *guard = new_hit_tester;
    }
}

impl ApiHitTester for SharedHitTester {
    fn hit_test(&self,
        point: WorldPoint,
    ) -> HitTestResult {
        self.get_ref().hit_test(HitTest::new(point))
    }
}

/// A copy of important spatial node data to use during hit testing. This a copy of
/// data from the SpatialTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
#[derive(MallocSizeOf)]
struct HitTestSpatialNode {
    /// The pipeline id of this node.
    pipeline_id: PipelineId,

    /// World transform for content transformed by this node.
    world_content_transform: LayoutToWorldFastTransform,

    /// World viewport transform for content transformed by this node.
    world_viewport_transform: LayoutToWorldFastTransform,

    /// The accumulated external scroll offset for this spatial node.
    external_scroll_offset: LayoutVector2D,
}

#[derive(MallocSizeOf)]
struct HitTestClipNode {
    /// A particular point must be inside all of these regions to be considered clipped in
    /// for the purposes of a hit test.
    region: HitTestRegion,
    /// The positioning node for this clip
    spatial_node_index: SpatialNodeIndex,
}

impl HitTestClipNode {
    fn new(
        node: ClipNode,
        spatial_node_index: SpatialNodeIndex,
        interners: &Interners,
    ) -> Self {
        let region = match node.item.kind {
            ClipItemKind::Rectangle { rect, mode } => {
                HitTestRegion::Rectangle(rect, mode)
            }
            ClipItemKind::RoundedRectangle { rect, radius, mode } => {
                HitTestRegion::RoundedRectangle(rect, radius, mode)
            }
            ClipItemKind::Image { rect, polygon_handle, .. } => {
                if let Some(handle) = polygon_handle {
                    // Retrieve the polygon data from the interner.
                    let polygon = &interners.polygon[handle];
                    HitTestRegion::Polygon(rect, *polygon)
                } else {
                    HitTestRegion::Rectangle(rect, ClipMode::Clip)
                }
            }
            ClipItemKind::BoxShadow { .. } => HitTestRegion::Invalid,
        };

        HitTestClipNode {
            region,
            spatial_node_index,
        }
    }
}

#[derive(Clone, MallocSizeOf)]
struct HitTestingItem {
    rect: LayoutRect,
    clip_rect: LayoutRect,
    tag: ItemTag,
    is_backface_visible: bool,
    spatial_node_index: SpatialNodeIndex,
    #[ignore_malloc_size_of = "Range"]
    clip_nodes_range: ops::Range<ClipNodeIndex>,
}

impl HitTestingItem {
    fn new(
        tag: ItemTag,
        info: &LayoutPrimitiveInfo,
        spatial_node_index: SpatialNodeIndex,
        clip_nodes_range: ops::Range<ClipNodeIndex>,
    ) -> HitTestingItem {
        HitTestingItem {
            rect: info.rect,
            clip_rect: info.clip_rect,
            tag,
            is_backface_visible: info.flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE),
            spatial_node_index,
            clip_nodes_range,
        }
    }
}

/// Statistics about allocation sizes of current hit tester,
/// used to pre-allocate size of the next hit tester.
pub struct HitTestingSceneStats {
    pub clip_nodes_count: usize,
    pub items_count: usize,
}

impl HitTestingSceneStats {
    pub fn empty() -> Self {
        HitTestingSceneStats {
            clip_nodes_count: 0,
            items_count: 0,
        }
    }
}

#[derive(MallocSizeOf, Debug, Copy, Clone)]
pub struct ClipNodeIndex(u32);

/// Defines the immutable part of a hit tester for a given scene.
/// The hit tester is recreated each time a frame is built, since
/// it relies on the current values of the spatial tree.
/// However, the clip chain and item definitions don't change,
/// so they are created once per scene, and shared between
/// hit tester instances via Arc.
#[derive(MallocSizeOf)]
pub struct HitTestingScene {
    /// Packed array of all hit test clip nodes
    clip_nodes: Vec<HitTestClipNode>,

    /// List of hit testing primitives.
    items: Vec<HitTestingItem>,

    /// Current stack of clip ids from stacking context
    #[ignore_malloc_size_of = "ClipId"]
    clip_id_stack: Vec<ClipId>,

    /// Last cached clip id, useful for scenes with a lot
    /// of hit-test items that reference the same clip
    #[ignore_malloc_size_of = "simple"]
    cached_clip_id: Option<(ClipId, ops::Range<ClipNodeIndex>)>,

    /// Temporary buffer used to de-duplicate clip ids when creating hit
    /// test clip nodes.
    #[ignore_malloc_size_of = "ClipId"]
    seen_clips: FastHashSet<ClipId>,
}

impl HitTestingScene {
    /// Construct a new hit testing scene, pre-allocating to size
    /// provided by previous scene stats.
    pub fn new(stats: &HitTestingSceneStats) -> Self {
        HitTestingScene {
            clip_nodes: Vec::with_capacity(stats.clip_nodes_count),
            items: Vec::with_capacity(stats.items_count),
            clip_id_stack: Vec::with_capacity(8),
            cached_clip_id: None,
            seen_clips: FastHashSet::default(),
        }
    }

    /// Get stats about the current scene allocation sizes.
    pub fn get_stats(&self) -> HitTestingSceneStats {
        HitTestingSceneStats {
            clip_nodes_count: self.clip_nodes.len(),
            items_count: self.items.len(),
        }
    }

    /// Add a hit testing primitive.
    pub fn add_item(
        &mut self,
        tag: ItemTag,
        info: &LayoutPrimitiveInfo,
        spatial_node_index: SpatialNodeIndex,
        clip_id: ClipId,
        clip_store: &ClipStore,
        interners: &Interners,
    ) {
        let clip_range = match self.cached_clip_id {
            Some((cached_clip_id, ref range)) if cached_clip_id == clip_id => {
                range.clone()
            }
            Some(_) | None => {
                let start = ClipNodeIndex(self.clip_nodes.len() as u32);

                // Clear the set of which clip ids have been encountered for this item
                self.seen_clips.clear();

                // Flatten all clips from the stacking context hierarchy
                for clip_id in &self.clip_id_stack {
                    add_clips(
                        *clip_id,
                        clip_store,
                        &mut self.clip_nodes,
                        &mut self.seen_clips,
                        interners,
                    );
                }

                // Add the primitive clip
                add_clips(
                    clip_id,
                    clip_store,
                    &mut self.clip_nodes,
                    &mut self.seen_clips,
                    interners,
                );

                let end = ClipNodeIndex(self.clip_nodes.len() as u32);

                let range = ops::Range {
                    start,
                    end,
                };

                self.cached_clip_id = Some((clip_id, range.clone()));

                range
            }
        };

        let item = HitTestingItem::new(
            tag,
            info,
            spatial_node_index,
            clip_range,
        );

        self.items.push(item);
    }

    /// Push a clip onto the current stack
    pub fn push_clip(
        &mut self,
        clip_id: ClipId,
    ) {
        // Invalidate the cache since the stack may affect the produced hit test clip struct
        self.cached_clip_id = None;

        self.clip_id_stack.push(clip_id);
    }

    /// Pop a clip from the current stack
    pub fn pop_clip(
        &mut self,
    ) {
        // Invalidate the cache since the stack may affect the produced hit test clip struct
        self.cached_clip_id = None;

        self.clip_id_stack.pop().unwrap();
    }
}

#[derive(MallocSizeOf)]
enum HitTestRegion {
    Invalid,
    Rectangle(LayoutRect, ClipMode),
    RoundedRectangle(LayoutRect, BorderRadius, ClipMode),
    Polygon(LayoutRect, PolygonKey),
}

impl HitTestRegion {
    fn contains(&self, point: &LayoutPoint) -> bool {
        match *self {
            HitTestRegion::Rectangle(ref rectangle, ClipMode::Clip) =>
                rectangle.contains(*point),
            HitTestRegion::Rectangle(ref rectangle, ClipMode::ClipOut) =>
                !rectangle.contains(*point),
            HitTestRegion::RoundedRectangle(rect, radii, ClipMode::Clip) =>
                rounded_rectangle_contains_point(point, &rect, &radii),
            HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
                !rounded_rectangle_contains_point(point, &rect, &radii),
            HitTestRegion::Polygon(rect, polygon) =>
                polygon_contains_point(point, &rect, &polygon),
            HitTestRegion::Invalid => true,
        }
    }
}

#[derive(MallocSizeOf)]
pub struct HitTester {
    #[ignore_malloc_size_of = "Arc"]
    scene: Arc<HitTestingScene>,
    spatial_nodes: FastHashMap<SpatialNodeIndex, HitTestSpatialNode>,
}

impl HitTester {
    pub fn empty() -> Self {
        HitTester {
            scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())),
            spatial_nodes: FastHashMap::default(),
        }
    }

    pub fn new(
        scene: Arc<HitTestingScene>,
        spatial_tree: &SpatialTree,
    ) -> HitTester {
        let mut hit_tester = HitTester {
            scene,
            spatial_nodes: FastHashMap::default(),
        };
        hit_tester.read_spatial_tree(spatial_tree);
        hit_tester
    }

    fn read_spatial_tree(
        &mut self,
        spatial_tree: &SpatialTree,
    ) {
        self.spatial_nodes.clear();
        self.spatial_nodes.reserve(spatial_tree.spatial_node_count());

        spatial_tree.visit_nodes(|index, node| {
            //TODO: avoid inverting more than necessary:
            //  - if the coordinate system is non-invertible, no need to try any of these concrete transforms
            //  - if there are other places where inversion is needed, let's not repeat the step

            self.spatial_nodes.insert(index, HitTestSpatialNode {
                pipeline_id: node.pipeline_id,
                world_content_transform: spatial_tree
                    .get_world_transform(index)
                    .into_fast_transform(),
                world_viewport_transform: spatial_tree
                    .get_world_viewport_transform(index)
                    .into_fast_transform(),
                external_scroll_offset: get_external_scroll_offset(spatial_tree, index),
            });
        });
    }

    pub fn hit_test(&self, test: HitTest) -> HitTestResult {
        let mut result = HitTestResult::default();

        let mut current_spatial_node_index = SpatialNodeIndex::INVALID;
        let mut point_in_layer = None;

        // For each hit test primitive
        for item in self.scene.items.iter().rev() {
            let scroll_node = &self.spatial_nodes[&item.spatial_node_index];
            let pipeline_id = scroll_node.pipeline_id;

            // Update the cached point in layer space, if the spatial node
            // changed since last primitive.
            if item.spatial_node_index != current_spatial_node_index {
                point_in_layer = scroll_node
                    .world_content_transform
                    .inverse()
                    .and_then(|inverted| inverted.transform_point2d(test.point));
                current_spatial_node_index = item.spatial_node_index;
            }

            // Only consider hit tests on transformable layers.
            let point_in_layer = match point_in_layer {
                Some(p) => p,
                None => continue,
            };

            // If the item's rect or clip rect don't contain this point, it's
            // not a valid hit.
            if !item.rect.contains(point_in_layer) {
                continue;
            }

            if !item.clip_rect.contains(point_in_layer) {
                continue;
            }

            // See if any of the clips for this primitive cull out the item.
            let mut is_valid = true;
            let clip_nodes = &self.scene.clip_nodes[item.clip_nodes_range.start.0 as usize .. item.clip_nodes_range.end.0 as usize];
            for clip_node in clip_nodes {
                let transform = self
                    .spatial_nodes[&clip_node.spatial_node_index]
                    .world_content_transform;
                let transformed_point = match transform
                    .inverse()
                    .and_then(|inverted| inverted.transform_point2d(test.point))
                {
                    Some(point) => point,
                    None => continue,
                };
                if !clip_node.region.contains(&transformed_point) {
                    is_valid = false;
                    break;
                }
            }
            if !is_valid {
                continue;
            }

            // Don't hit items with backface-visibility:hidden if they are facing the back.
            if !item.is_backface_visible && scroll_node.world_content_transform.is_backface_visible() {
                continue;
            }

            result.items.push(HitTestItem {
                pipeline: pipeline_id,
                tag: item.tag,
            });
        }

        result.items.dedup();
        result
    }
}

#[derive(MallocSizeOf)]
pub struct HitTest {
    point: WorldPoint,
}

impl HitTest {
    pub fn new(
        point: WorldPoint,
    ) -> HitTest {
        HitTest {
            point,
        }
    }
}

/// Collect clips for a given ClipId, convert and add them to the hit testing
/// scene, if not already present.
fn add_clips(
    clip_id: ClipId,
    clip_store: &ClipStore,
    clip_nodes: &mut Vec<HitTestClipNode>,
    seen_clips: &mut FastHashSet<ClipId>,
    interners: &Interners,
) {
    // If this clip-id has already been added to this hit-test item, skip it
    if seen_clips.contains(&clip_id) {
        return;
    }
    seen_clips.insert(clip_id);

    let template = &clip_store.templates[&clip_id];
    let instances = &clip_store.instances[template.clips.start as usize .. template.clips.end as usize];

    for clip in instances {
        clip_nodes.alloc().init(
            HitTestClipNode::new(
                clip.key.into(),
                clip.key.spatial_node_index,
                interners,
            )
        );
    }

    // The ClipId parenting is terminated when we reach the root ClipId
    if clip_id != template.parent {
        add_clips(
            template.parent,
            clip_store,
            clip_nodes,
            seen_clips,
            interners,
        );
    }
}
<commit_msg>Bug 1747409 - Use iter().all() in clip nodes during hit testing rather than a nested loop. r=gfx-reviewers,bradwerth,kvark<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{BorderRadius, ClipMode, HitTestItem, HitTestResult, ItemTag, PrimitiveFlags};
use api::{PipelineId, ApiHitTester, ClipId};
use api::units::*;
use crate::clip::{ClipItemKind, ClipStore, ClipNode, rounded_rectangle_contains_point};
use crate::clip::{polygon_contains_point};
use crate::prim_store::PolygonKey;
use crate::scene_builder_thread::Interners;
use crate::spatial_tree::{SpatialNodeIndex, SpatialTree, get_external_scroll_offset};
use crate::internal_types::{FastHashMap, FastHashSet, LayoutPrimitiveInfo};
use std::ops;
use std::sync::{Arc, Mutex};
use crate::util::{LayoutToWorldFastTransform, VecHelper};

pub struct SharedHitTester {
    // We don't really need a mutex here. We could do with some sort of
    // atomic-atomic-ref-counted pointer (an Arc which would let the pointer
    // be swapped atomically like an AtomicPtr).
    // In practive this shouldn't cause performance issues, though.
    hit_tester: Mutex<Arc<HitTester>>,
}

impl SharedHitTester {
    pub fn new() -> Self {
        SharedHitTester {
            hit_tester: Mutex::new(Arc::new(HitTester::empty())),
        }
    }

    pub fn get_ref(&self) -> Arc<HitTester> {
        let guard = self.hit_tester.lock().unwrap();
        Arc::clone(&*guard)
    }

    pub(crate) fn update(&self, new_hit_tester: Arc<HitTester>) {
        let mut guard = self.hit_tester.lock().unwrap();
        *guard = new_hit_tester;
    }
}

impl ApiHitTester for SharedHitTester {
    fn hit_test(&self,
        point: WorldPoint,
    ) -> HitTestResult {
        self.get_ref().hit_test(HitTest::new(point))
    }
}

/// A copy of important spatial node data to use during hit testing. This a copy of
/// data from the SpatialTree that will persist as a new frame is under construction,
/// allowing hit tests consistent with the currently rendered frame.
#[derive(MallocSizeOf)]
struct HitTestSpatialNode {
    /// The pipeline id of this node.
    pipeline_id: PipelineId,

    /// World transform for content transformed by this node.
    world_content_transform: LayoutToWorldFastTransform,

    /// World viewport transform for content transformed by this node.
    world_viewport_transform: LayoutToWorldFastTransform,

    /// The accumulated external scroll offset for this spatial node.
    external_scroll_offset: LayoutVector2D,
}

#[derive(MallocSizeOf)]
struct HitTestClipNode {
    /// A particular point must be inside all of these regions to be considered clipped in
    /// for the purposes of a hit test.
    region: HitTestRegion,
    /// The positioning node for this clip
    spatial_node_index: SpatialNodeIndex,
}

impl HitTestClipNode {
    fn new(
        node: ClipNode,
        spatial_node_index: SpatialNodeIndex,
        interners: &Interners,
    ) -> Self {
        let region = match node.item.kind {
            ClipItemKind::Rectangle { rect, mode } => {
                HitTestRegion::Rectangle(rect, mode)
            }
            ClipItemKind::RoundedRectangle { rect, radius, mode } => {
                HitTestRegion::RoundedRectangle(rect, radius, mode)
            }
            ClipItemKind::Image { rect, polygon_handle, .. } => {
                if let Some(handle) = polygon_handle {
                    // Retrieve the polygon data from the interner.
                    let polygon = &interners.polygon[handle];
                    HitTestRegion::Polygon(rect, *polygon)
                } else {
                    HitTestRegion::Rectangle(rect, ClipMode::Clip)
                }
            }
            ClipItemKind::BoxShadow { .. } => HitTestRegion::Invalid,
        };

        HitTestClipNode {
            region,
            spatial_node_index,
        }
    }
}

#[derive(Clone, MallocSizeOf)]
struct HitTestingItem {
    rect: LayoutRect,
    clip_rect: LayoutRect,
    tag: ItemTag,
    is_backface_visible: bool,
    spatial_node_index: SpatialNodeIndex,
    #[ignore_malloc_size_of = "Range"]
    clip_nodes_range: ops::Range<ClipNodeIndex>,
}

impl HitTestingItem {
    fn new(
        tag: ItemTag,
        info: &LayoutPrimitiveInfo,
        spatial_node_index: SpatialNodeIndex,
        clip_nodes_range: ops::Range<ClipNodeIndex>,
    ) -> HitTestingItem {
        HitTestingItem {
            rect: info.rect,
            clip_rect: info.clip_rect,
            tag,
            is_backface_visible: info.flags.contains(PrimitiveFlags::IS_BACKFACE_VISIBLE),
            spatial_node_index,
            clip_nodes_range,
        }
    }
}

/// Statistics about allocation sizes of current hit tester,
/// used to pre-allocate size of the next hit tester.
pub struct HitTestingSceneStats {
    pub clip_nodes_count: usize,
    pub items_count: usize,
}

impl HitTestingSceneStats {
    pub fn empty() -> Self {
        HitTestingSceneStats {
            clip_nodes_count: 0,
            items_count: 0,
        }
    }
}

#[derive(MallocSizeOf, Debug, Copy, Clone)]
pub struct ClipNodeIndex(u32);

/// Defines the immutable part of a hit tester for a given scene.
/// The hit tester is recreated each time a frame is built, since
/// it relies on the current values of the spatial tree.
/// However, the clip chain and item definitions don't change,
/// so they are created once per scene, and shared between
/// hit tester instances via Arc.
#[derive(MallocSizeOf)]
pub struct HitTestingScene {
    /// Packed array of all hit test clip nodes
    clip_nodes: Vec<HitTestClipNode>,

    /// List of hit testing primitives.
    items: Vec<HitTestingItem>,

    /// Current stack of clip ids from stacking context
    #[ignore_malloc_size_of = "ClipId"]
    clip_id_stack: Vec<ClipId>,

    /// Last cached clip id, useful for scenes with a lot
    /// of hit-test items that reference the same clip
    #[ignore_malloc_size_of = "simple"]
    cached_clip_id: Option<(ClipId, ops::Range<ClipNodeIndex>)>,

    /// Temporary buffer used to de-duplicate clip ids when creating hit
    /// test clip nodes.
    #[ignore_malloc_size_of = "ClipId"]
    seen_clips: FastHashSet<ClipId>,
}

impl HitTestingScene {
    /// Construct a new hit testing scene, pre-allocating to size
    /// provided by previous scene stats.
    pub fn new(stats: &HitTestingSceneStats) -> Self {
        HitTestingScene {
            clip_nodes: Vec::with_capacity(stats.clip_nodes_count),
            items: Vec::with_capacity(stats.items_count),
            clip_id_stack: Vec::with_capacity(8),
            cached_clip_id: None,
            seen_clips: FastHashSet::default(),
        }
    }

    /// Get stats about the current scene allocation sizes.
    pub fn get_stats(&self) -> HitTestingSceneStats {
        HitTestingSceneStats {
            clip_nodes_count: self.clip_nodes.len(),
            items_count: self.items.len(),
        }
    }

    /// Add a hit testing primitive.
    pub fn add_item(
        &mut self,
        tag: ItemTag,
        info: &LayoutPrimitiveInfo,
        spatial_node_index: SpatialNodeIndex,
        clip_id: ClipId,
        clip_store: &ClipStore,
        interners: &Interners,
    ) {
        let clip_range = match self.cached_clip_id {
            Some((cached_clip_id, ref range)) if cached_clip_id == clip_id => {
                range.clone()
            }
            Some(_) | None => {
                let start = ClipNodeIndex(self.clip_nodes.len() as u32);

                // Clear the set of which clip ids have been encountered for this item
                self.seen_clips.clear();

                // Flatten all clips from the stacking context hierarchy
                for clip_id in &self.clip_id_stack {
                    add_clips(
                        *clip_id,
                        clip_store,
                        &mut self.clip_nodes,
                        &mut self.seen_clips,
                        interners,
                    );
                }

                // Add the primitive clip
                add_clips(
                    clip_id,
                    clip_store,
                    &mut self.clip_nodes,
                    &mut self.seen_clips,
                    interners,
                );

                let end = ClipNodeIndex(self.clip_nodes.len() as u32);

                let range = ops::Range {
                    start,
                    end,
                };

                self.cached_clip_id = Some((clip_id, range.clone()));

                range
            }
        };

        let item = HitTestingItem::new(
            tag,
            info,
            spatial_node_index,
            clip_range,
        );

        self.items.push(item);
    }

    /// Push a clip onto the current stack
    pub fn push_clip(
        &mut self,
        clip_id: ClipId,
    ) {
        // Invalidate the cache since the stack may affect the produced hit test clip struct
        self.cached_clip_id = None;

        self.clip_id_stack.push(clip_id);
    }

    /// Pop a clip from the current stack
    pub fn pop_clip(
        &mut self,
    ) {
        // Invalidate the cache since the stack may affect the produced hit test clip struct
        self.cached_clip_id = None;

        self.clip_id_stack.pop().unwrap();
    }
}

#[derive(MallocSizeOf)]
enum HitTestRegion {
    Invalid,
    Rectangle(LayoutRect, ClipMode),
    RoundedRectangle(LayoutRect, BorderRadius, ClipMode),
    Polygon(LayoutRect, PolygonKey),
}

impl HitTestRegion {
    fn contains(&self, point: &LayoutPoint) -> bool {
        match *self {
            HitTestRegion::Rectangle(ref rectangle, ClipMode::Clip) =>
                rectangle.contains(*point),
            HitTestRegion::Rectangle(ref rectangle, ClipMode::ClipOut) =>
                !rectangle.contains(*point),
            HitTestRegion::RoundedRectangle(rect, radii, ClipMode::Clip) =>
                rounded_rectangle_contains_point(point, &rect, &radii),
            HitTestRegion::RoundedRectangle(rect, radii, ClipMode::ClipOut) =>
                !rounded_rectangle_contains_point(point, &rect, &radii),
            HitTestRegion::Polygon(rect, polygon) =>
                polygon_contains_point(point, &rect, &polygon),
            HitTestRegion::Invalid => true,
        }
    }
}

#[derive(MallocSizeOf)]
pub struct HitTester {
    #[ignore_malloc_size_of = "Arc"]
    scene: Arc<HitTestingScene>,
    spatial_nodes: FastHashMap<SpatialNodeIndex, HitTestSpatialNode>,
}

impl HitTester {
    pub fn empty() -> Self {
        HitTester {
            scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())),
            spatial_nodes: FastHashMap::default(),
        }
    }

    pub fn new(
        scene: Arc<HitTestingScene>,
        spatial_tree: &SpatialTree,
    ) -> HitTester {
        let mut hit_tester = HitTester {
            scene,
            spatial_nodes: FastHashMap::default(),
        };
        hit_tester.read_spatial_tree(spatial_tree);
        hit_tester
    }

    fn read_spatial_tree(
        &mut self,
        spatial_tree: &SpatialTree,
    ) {
        self.spatial_nodes.clear();
        self.spatial_nodes.reserve(spatial_tree.spatial_node_count());

        spatial_tree.visit_nodes(|index, node| {
            //TODO: avoid inverting more than necessary:
            //  - if the coordinate system is non-invertible, no need to try any of these concrete transforms
            //  - if there are other places where inversion is needed, let's not repeat the step

            self.spatial_nodes.insert(index, HitTestSpatialNode {
                pipeline_id: node.pipeline_id,
                world_content_transform: spatial_tree
                    .get_world_transform(index)
                    .into_fast_transform(),
                world_viewport_transform: spatial_tree
                    .get_world_viewport_transform(index)
                    .into_fast_transform(),
                external_scroll_offset: get_external_scroll_offset(spatial_tree, index),
            });
        });
    }

    pub fn hit_test(&self, test: HitTest) -> HitTestResult {
        let mut result = HitTestResult::default();

        let mut current_spatial_node_index = SpatialNodeIndex::INVALID;
        let mut point_in_layer = None;

        // For each hit test primitive
        for item in self.scene.items.iter().rev() {
            let scroll_node = &self.spatial_nodes[&item.spatial_node_index];
            let pipeline_id = scroll_node.pipeline_id;

            // Update the cached point in layer space, if the spatial node
            // changed since last primitive.
            if item.spatial_node_index != current_spatial_node_index {
                point_in_layer = scroll_node
                    .world_content_transform
                    .inverse()
                    .and_then(|inverted| inverted.transform_point2d(test.point));
                current_spatial_node_index = item.spatial_node_index;
            }

            // Only consider hit tests on transformable layers.
            let point_in_layer = match point_in_layer {
                Some(p) => p,
                None => continue,
            };

            // If the item's rect or clip rect don't contain this point, it's
            // not a valid hit.
            if !item.rect.contains(point_in_layer) {
                continue;
            }

            if !item.clip_rect.contains(point_in_layer) {
                continue;
            }

            // See if any of the clips for this primitive cull out the item.
            let clip_nodes = &self.scene.clip_nodes[item.clip_nodes_range.start.0 as usize .. item.clip_nodes_range.end.0 as usize];
            let is_valid = clip_nodes.iter().all(|clip_node| {
                let transform = self
                    .spatial_nodes[&clip_node.spatial_node_index]
                    .world_content_transform;
                let transformed_point = match transform
                    .inverse()
                    .and_then(|inverted| inverted.transform_point2d(test.point))
                {
                    Some(point) => point,
                    // XXX This `return true` is a bit sketchy, but matches
                    // pre-existing behavior.
                    None => return true,
                };
                clip_node.region.contains(&transformed_point)
            });

            if !is_valid {
                continue;
            }

            // Don't hit items with backface-visibility:hidden if they are facing the back.
            if !item.is_backface_visible && scroll_node.world_content_transform.is_backface_visible() {
                continue;
            }

            result.items.push(HitTestItem {
                pipeline: pipeline_id,
                tag: item.tag,
            });
        }

        result.items.dedup();
        result
    }
}

#[derive(MallocSizeOf)]
pub struct HitTest {
    point: WorldPoint,
}

impl HitTest {
    pub fn new(
        point: WorldPoint,
    ) -> HitTest {
        HitTest {
            point,
        }
    }
}

/// Collect clips for a given ClipId, convert and add them to the hit testing
/// scene, if not already present.
fn add_clips(
    clip_id: ClipId,
    clip_store: &ClipStore,
    clip_nodes: &mut Vec<HitTestClipNode>,
    seen_clips: &mut FastHashSet<ClipId>,
    interners: &Interners,
) {
    // If this clip-id has already been added to this hit-test item, skip it
    if seen_clips.contains(&clip_id) {
        return;
    }
    seen_clips.insert(clip_id);

    let template = &clip_store.templates[&clip_id];
    let instances = &clip_store.instances[template.clips.start as usize .. template.clips.end as usize];

    for clip in instances {
        clip_nodes.alloc().init(
            HitTestClipNode::new(
                clip.key.into(),
                clip.key.spatial_node_index,
                interners,
            )
        );
    }

    // The ClipId parenting is terminated when we reach the root ClipId
    if clip_id != template.parent {
        add_clips(
            template.parent,
            clip_store,
            clip_nodes,
            seen_clips,
            interners,
        );
    }
}
<|endoftext|>


<commit_before>//! This crate provides a convenient concise way to write unit tests for
//! implementations of [`Serialize`] and [`Deserialize`].
//!
//! [`Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
//! [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
//!
//! The `Serialize` impl for a value can be characterized by the sequence of
//! [`Serializer`] calls that are made in the course of serializing the value,
//! so `serde_test` provides a [`Token`] abstraction which corresponds roughly
//! to `Serializer` method calls. There is an [`assert_ser_tokens`] function to
//! test that a value serializes to a particular sequence of method calls, an
//! [`assert_de_tokens`] function to test that a value can be deserialized from
//! a particular sequence of method calls, and an [`assert_tokens`] function to
//! test both directions. There are also functions to test expected failure
//! conditions.
//!
//! [`Serializer`]: https://docs.serde.rs/serde/ser/trait.Serializer.html
//! [`Token`]: https://docs.serde.rs/serde_test/enum.Token.html
//! [`assert_ser_tokens`]: https://docs.serde.rs/serde_test/fn.assert_ser_tokens.html
//! [`assert_de_tokens`]: https://docs.serde.rs/serde_test/fn.assert_de_tokens.html
//! [`assert_tokens`]: https://docs.serde.rs/serde_test/fn.assert_tokens.html
//!
//! Here is an example from the [`linked-hash-map`] crate.
//!
//! [`linked-hash-map`]: https://github.com/contain-rs/linked-hash-map
//!
//! ```edition2018
//! # const IGNORE: &str = stringify! {
//! use linked_hash_map::LinkedHashMap;
//! # };
//! use serde_test::{Token, assert_tokens};
//!
//! # use std::fmt;
//! # use std::marker::PhantomData;
//! #
//! # use serde::ser::{Serialize, Serializer, SerializeMap};
//! # use serde::de::{Deserialize, Deserializer, Visitor, MapAccess};
//! #
//! # // Dumb imitation of LinkedHashMap.
//! # #[derive(PartialEq, Debug)]
//! # struct LinkedHashMap<K, V>(Vec<(K, V)>);
//! #
//! # impl<K, V> LinkedHashMap<K, V> {
//! #     fn new() -> Self {
//! #         LinkedHashMap(Vec::new())
//! #     }
//! #
//! #     fn insert(&mut self, k: K, v: V) {
//! #         self.0.push((k, v));
//! #     }
//! # }
//! #
//! # impl<K, V> Serialize for LinkedHashMap<K, V>
//! # where
//! #     K: Serialize,
//! #     V: Serialize,
//! # {
//! #     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
//! #     where
//! #         S: Serializer,
//! #     {
//! #         let mut map = serializer.serialize_map(Some(self.0.len()))?;
//! #         for &(ref k, ref v) in &self.0 {
//! #             map.serialize_entry(k, v)?;
//! #         }
//! #         map.end()
//! #     }
//! # }
//! #
//! # struct LinkedHashMapVisitor<K, V>(PhantomData<(K, V)>);
//! #
//! # impl<'de, K, V> Visitor<'de> for LinkedHashMapVisitor<K, V>
//! # where
//! #     K: Deserialize<'de>,
//! #     V: Deserialize<'de>,
//! # {
//! #     type Value = LinkedHashMap<K, V>;
//! #
//! #     fn expecting(&self, _: &mut fmt::Formatter) -> fmt::Result {
//! #         unimplemented!()
//! #     }
//! #
//! #     fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
//! #     where
//! #         M: MapAccess<'de>,
//! #     {
//! #         let mut map = LinkedHashMap::new();
//! #         while let Some((key, value)) = access.next_entry()? {
//! #             map.insert(key, value);
//! #         }
//! #         Ok(map)
//! #     }
//! # }
//! #
//! # impl<'de, K, V> Deserialize<'de> for LinkedHashMap<K, V>
//! # where
//! #     K: Deserialize<'de>,
//! #     V: Deserialize<'de>,
//! # {
//! #     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
//! #     where
//! #         D: Deserializer<'de>,
//! #     {
//! #         deserializer.deserialize_map(LinkedHashMapVisitor(PhantomData))
//! #     }
//! # }
//! #
//! #[test]
//! # fn not_a_test_ser_de_empty() {}
//! fn test_ser_de_empty() {
//!     let map = LinkedHashMap::<char, u32>::new();
//!
//!     assert_tokens(&map, &[
//!         Token::Map { len: Some(0) },
//!         Token::MapEnd,
//!     ]);
//! }
//!
//! #[test]
//! # fn not_a_test_ser_de() {}
//! fn test_ser_de() {
//!     let mut map = LinkedHashMap::new();
//!     map.insert('b', 20);
//!     map.insert('a', 10);
//!     map.insert('c', 30);
//!
//!     assert_tokens(&map, &[
//!         Token::Map { len: Some(3) },
//!         Token::Char('b'),
//!         Token::I32(20),
//!
//!         Token::Char('a'),
//!         Token::I32(10),
//!
//!         Token::Char('c'),
//!         Token::I32(30),
//!         Token::MapEnd,
//!     ]);
//! }
//! #
//! # fn main() {
//! #     test_ser_de_empty();
//! #     test_ser_de();
//! # }
//! ```

#![doc(html_root_url = "https://docs.rs/serde_test/1.0.121")]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Ignored clippy lints
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, needless_doctest_main))]
// Ignored clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        empty_line_after_outer_attr,
        missing_docs_in_private_items,
        module_name_repetitions,
        must_use_candidate,
        redundant_field_names,
        use_debug,
        use_self
    )
)]

#[macro_use]
extern crate serde;

mod de;
mod error;
mod ser;

mod assert;
mod configure;
mod token;

pub use assert::{
    assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, assert_ser_tokens_error,
    assert_tokens,
};
pub use token::Token;

pub use configure::{Compact, Configure, Readable};

// Not public API.
#[doc(hidden)]
pub use de::Deserializer;
<commit_msg>Ignore too_many_lines clippy pedantic lint in serde_test<commit_after>//! This crate provides a convenient concise way to write unit tests for
//! implementations of [`Serialize`] and [`Deserialize`].
//!
//! [`Serialize`]: https://docs.serde.rs/serde/ser/trait.Serialize.html
//! [`Deserialize`]: https://docs.serde.rs/serde/de/trait.Deserialize.html
//!
//! The `Serialize` impl for a value can be characterized by the sequence of
//! [`Serializer`] calls that are made in the course of serializing the value,
//! so `serde_test` provides a [`Token`] abstraction which corresponds roughly
//! to `Serializer` method calls. There is an [`assert_ser_tokens`] function to
//! test that a value serializes to a particular sequence of method calls, an
//! [`assert_de_tokens`] function to test that a value can be deserialized from
//! a particular sequence of method calls, and an [`assert_tokens`] function to
//! test both directions. There are also functions to test expected failure
//! conditions.
//!
//! [`Serializer`]: https://docs.serde.rs/serde/ser/trait.Serializer.html
//! [`Token`]: https://docs.serde.rs/serde_test/enum.Token.html
//! [`assert_ser_tokens`]: https://docs.serde.rs/serde_test/fn.assert_ser_tokens.html
//! [`assert_de_tokens`]: https://docs.serde.rs/serde_test/fn.assert_de_tokens.html
//! [`assert_tokens`]: https://docs.serde.rs/serde_test/fn.assert_tokens.html
//!
//! Here is an example from the [`linked-hash-map`] crate.
//!
//! [`linked-hash-map`]: https://github.com/contain-rs/linked-hash-map
//!
//! ```edition2018
//! # const IGNORE: &str = stringify! {
//! use linked_hash_map::LinkedHashMap;
//! # };
//! use serde_test::{Token, assert_tokens};
//!
//! # use std::fmt;
//! # use std::marker::PhantomData;
//! #
//! # use serde::ser::{Serialize, Serializer, SerializeMap};
//! # use serde::de::{Deserialize, Deserializer, Visitor, MapAccess};
//! #
//! # // Dumb imitation of LinkedHashMap.
//! # #[derive(PartialEq, Debug)]
//! # struct LinkedHashMap<K, V>(Vec<(K, V)>);
//! #
//! # impl<K, V> LinkedHashMap<K, V> {
//! #     fn new() -> Self {
//! #         LinkedHashMap(Vec::new())
//! #     }
//! #
//! #     fn insert(&mut self, k: K, v: V) {
//! #         self.0.push((k, v));
//! #     }
//! # }
//! #
//! # impl<K, V> Serialize for LinkedHashMap<K, V>
//! # where
//! #     K: Serialize,
//! #     V: Serialize,
//! # {
//! #     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
//! #     where
//! #         S: Serializer,
//! #     {
//! #         let mut map = serializer.serialize_map(Some(self.0.len()))?;
//! #         for &(ref k, ref v) in &self.0 {
//! #             map.serialize_entry(k, v)?;
//! #         }
//! #         map.end()
//! #     }
//! # }
//! #
//! # struct LinkedHashMapVisitor<K, V>(PhantomData<(K, V)>);
//! #
//! # impl<'de, K, V> Visitor<'de> for LinkedHashMapVisitor<K, V>
//! # where
//! #     K: Deserialize<'de>,
//! #     V: Deserialize<'de>,
//! # {
//! #     type Value = LinkedHashMap<K, V>;
//! #
//! #     fn expecting(&self, _: &mut fmt::Formatter) -> fmt::Result {
//! #         unimplemented!()
//! #     }
//! #
//! #     fn visit_map<M>(self, mut access: M) -> Result<Self::Value, M::Error>
//! #     where
//! #         M: MapAccess<'de>,
//! #     {
//! #         let mut map = LinkedHashMap::new();
//! #         while let Some((key, value)) = access.next_entry()? {
//! #             map.insert(key, value);
//! #         }
//! #         Ok(map)
//! #     }
//! # }
//! #
//! # impl<'de, K, V> Deserialize<'de> for LinkedHashMap<K, V>
//! # where
//! #     K: Deserialize<'de>,
//! #     V: Deserialize<'de>,
//! # {
//! #     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
//! #     where
//! #         D: Deserializer<'de>,
//! #     {
//! #         deserializer.deserialize_map(LinkedHashMapVisitor(PhantomData))
//! #     }
//! # }
//! #
//! #[test]
//! # fn not_a_test_ser_de_empty() {}
//! fn test_ser_de_empty() {
//!     let map = LinkedHashMap::<char, u32>::new();
//!
//!     assert_tokens(&map, &[
//!         Token::Map { len: Some(0) },
//!         Token::MapEnd,
//!     ]);
//! }
//!
//! #[test]
//! # fn not_a_test_ser_de() {}
//! fn test_ser_de() {
//!     let mut map = LinkedHashMap::new();
//!     map.insert('b', 20);
//!     map.insert('a', 10);
//!     map.insert('c', 30);
//!
//!     assert_tokens(&map, &[
//!         Token::Map { len: Some(3) },
//!         Token::Char('b'),
//!         Token::I32(20),
//!
//!         Token::Char('a'),
//!         Token::I32(10),
//!
//!         Token::Char('c'),
//!         Token::I32(30),
//!         Token::MapEnd,
//!     ]);
//! }
//! #
//! # fn main() {
//! #     test_ser_de_empty();
//! #     test_ser_de();
//! # }
//! ```

#![doc(html_root_url = "https://docs.rs/serde_test/1.0.121")]
#![cfg_attr(feature = "cargo-clippy", allow(renamed_and_removed_lints))]
#![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))]
// Ignored clippy lints
#![cfg_attr(feature = "cargo-clippy", allow(float_cmp, needless_doctest_main))]
// Ignored clippy_pedantic lints
#![cfg_attr(
    feature = "cargo-clippy",
    allow(
        empty_line_after_outer_attr,
        missing_docs_in_private_items,
        module_name_repetitions,
        must_use_candidate,
        redundant_field_names,
        too_many_lines,
        use_debug,
        use_self
    )
)]

#[macro_use]
extern crate serde;

mod de;
mod error;
mod ser;

mod assert;
mod configure;
mod token;

pub use assert::{
    assert_de_tokens, assert_de_tokens_error, assert_ser_tokens, assert_ser_tokens_error,
    assert_tokens,
};
pub use token::Token;

pub use configure::{Compact, Configure, Readable};

// Not public API.
#[doc(hidden)]
pub use de::Deserializer;
<|endoftext|>