<commit_before>use latch::{LatchProbe, SpinLatch};
#[allow(unused_imports)]
use log::Event::*;
use job::StackJob;
use registry::{self, WorkerThread};
use std::any::Any;
use unwind;

#[cfg(test)]
mod test;

/// The `join` function takes two closures and *potentially* runs them
/// in parallel. It returns a pair of the results from those closures.
///
/// Conceptually, calling `join()` is similar to spawning two threads,
/// one executing each of the two closures. However, the
/// implementation is quite different and incurs very low
/// overhead. The underlying technique is called "work stealing": the
/// Rayon runtime uses a fixed pool of worker threads and attempts to
/// only execute code in parallel when there are idle CPUs to handle
/// it.
///
/// ### Warning about blocking I/O
///
/// The assumption is that the closures given to `join()` are
/// CPU-bound tasks that to do not perform I/O or other blocking
/// operations. If you do perform I/O, and that I/O should block
/// (e.g., waiting for a network request), the overall performance may
/// be poor.  Moreover, if you cause one closure to be blocked waiting
/// on another (for example, using a channel), that could lead to a
/// deadlock.
///
/// ### Panics
///
/// No matter what happens, both closures will always be executed.  If
/// a single closure panics, whether it be the first or second
/// closure, that panic will be propagated and hence `join()` will
/// panic with the same panic value. If both closures panic, `join()`
/// will panic with the panic value from the first closure.
pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce() -> RA + Send,
          B: FnOnce() -> RB + Send,
          RA: Send,
          RB: Send
{
    registry::in_worker(|worker_thread| unsafe {
        log!(Join { worker: worker_thread.index() });

        // Create virtual wrapper for task b; this all has to be
        // done here so that the stack frame can keep it all live
        // long enough.
        let job_b = StackJob::new(oper_b, SpinLatch::new());
        let job_b_ref = job_b.as_job_ref();
        worker_thread.push(job_b_ref);

        // Execute task a; hopefully b gets stolen in the meantime.
        let result_a = match unwind::halt_unwinding(oper_a) {
            Ok(v) => v,
            Err(err) => join_recover_from_panic(worker_thread, &job_b.latch, err),
        };

        // Now that task A has finished, try to pop job B from the
        // local stack.  It may already have been popped by job A; it
        // may also have been stolen. There may also be some tasks
        // pushed on top of it in the stack, and we will have to pop
        // those off to get to it.
        while !job_b.latch.probe() {
            if let Some(job) = worker_thread.pop() {
                if job == job_b_ref {
                    // Found it! Let's run it.
                    //
                    // Note that this could panic, but it's ok if we unwind here.
                    log!(PoppedRhs { worker: worker_thread.index() });
                    let result_b = job_b.run_inline();
                    return (result_a, result_b);
                } else {
                    log!(PoppedJob { worker: worker_thread.index() });
                    worker_thread.execute(job);
                }
            } else {
                // Local deque is empty. Time to steal from other
                // threads.
                log!(LostJob { worker: worker_thread.index() });
                worker_thread.wait_until(&job_b.latch);
                debug_assert!(job_b.latch.probe());
                break;
            }
        }

        return (result_a, job_b.into_result());
    })
}

/// If job A panics, we still cannot return until we are sure that job
/// B is complete. This is because it may contain references into the
/// enclosing stack frame(s).
#[cold] // cold path
unsafe fn join_recover_from_panic(worker_thread: &WorkerThread,
                                  job_b_latch: &SpinLatch,
                                  err: Box<Any + Send>)
                                  -> !
{
    worker_thread.wait_until(job_b_latch);
    unwind::resume_unwinding(err)
}
<commit_msg>Document a little about how join blocks<commit_after>use latch::{LatchProbe, SpinLatch};
#[allow(unused_imports)]
use log::Event::*;
use job::StackJob;
use registry::{self, WorkerThread};
use std::any::Any;
use unwind;

#[cfg(test)]
mod test;

/// The `join` function takes two closures and *potentially* runs them
/// in parallel. It returns a pair of the results from those closures.
///
/// Conceptually, calling `join()` is similar to spawning two threads,
/// one executing each of the two closures. However, the
/// implementation is quite different and incurs very low
/// overhead. The underlying technique is called "work stealing": the
/// Rayon runtime uses a fixed pool of worker threads and attempts to
/// only execute code in parallel when there are idle CPUs to handle
/// it.
///
/// When `join` is called from outside the thread pool, the calling
/// thread will block while the closures execute in the pool.  When
/// `join` is called within the pool, the calling thread still actively
/// participates in the thread pool.  It will try to execute its own
/// closures first, but if one is stolen then it will look for other
/// work while waiting for that result to come back.
///
/// ### Warning about blocking I/O
///
/// The assumption is that the closures given to `join()` are
/// CPU-bound tasks that to do not perform I/O or other blocking
/// operations. If you do perform I/O, and that I/O should block
/// (e.g., waiting for a network request), the overall performance may
/// be poor.  Moreover, if you cause one closure to be blocked waiting
/// on another (for example, using a channel), that could lead to a
/// deadlock.
///
/// ### Panics
///
/// No matter what happens, both closures will always be executed.  If
/// a single closure panics, whether it be the first or second
/// closure, that panic will be propagated and hence `join()` will
/// panic with the same panic value. If both closures panic, `join()`
/// will panic with the panic value from the first closure.
pub fn join<A, B, RA, RB>(oper_a: A, oper_b: B) -> (RA, RB)
    where A: FnOnce() -> RA + Send,
          B: FnOnce() -> RB + Send,
          RA: Send,
          RB: Send
{
    registry::in_worker(|worker_thread| unsafe {
        log!(Join { worker: worker_thread.index() });

        // Create virtual wrapper for task b; this all has to be
        // done here so that the stack frame can keep it all live
        // long enough.
        let job_b = StackJob::new(oper_b, SpinLatch::new());
        let job_b_ref = job_b.as_job_ref();
        worker_thread.push(job_b_ref);

        // Execute task a; hopefully b gets stolen in the meantime.
        let result_a = match unwind::halt_unwinding(oper_a) {
            Ok(v) => v,
            Err(err) => join_recover_from_panic(worker_thread, &job_b.latch, err),
        };

        // Now that task A has finished, try to pop job B from the
        // local stack.  It may already have been popped by job A; it
        // may also have been stolen. There may also be some tasks
        // pushed on top of it in the stack, and we will have to pop
        // those off to get to it.
        while !job_b.latch.probe() {
            if let Some(job) = worker_thread.pop() {
                if job == job_b_ref {
                    // Found it! Let's run it.
                    //
                    // Note that this could panic, but it's ok if we unwind here.
                    log!(PoppedRhs { worker: worker_thread.index() });
                    let result_b = job_b.run_inline();
                    return (result_a, result_b);
                } else {
                    log!(PoppedJob { worker: worker_thread.index() });
                    worker_thread.execute(job);
                }
            } else {
                // Local deque is empty. Time to steal from other
                // threads.
                log!(LostJob { worker: worker_thread.index() });
                worker_thread.wait_until(&job_b.latch);
                debug_assert!(job_b.latch.probe());
                break;
            }
        }

        return (result_a, job_b.into_result());
    })
}

/// If job A panics, we still cannot return until we are sure that job
/// B is complete. This is because it may contain references into the
/// enclosing stack frame(s).
#[cold] // cold path
unsafe fn join_recover_from_panic(worker_thread: &WorkerThread,
                                  job_b_latch: &SpinLatch,
                                  err: Box<Any + Send>)
                                  -> !
{
    worker_thread.wait_until(job_b_latch);
    unwind::resume_unwinding(err)
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "nightly", feature(custom_derive, plugin))]
#![cfg_attr(feature = "nightly", plugin(rebind_plugins))]

#[macro_use] extern crate conrod;
extern crate find_folder;
extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use conrod::{
    Background,
    Color,
    Colorable,
    Frameable,
    Labelable,
    Positionable,
    Sizeable,
    Theme,
    Toggle,
    Ui,
    Widget,
};
use conrod::color::{black, grayscale, green, red};
use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use opengl_graphics::glyph_cache::GlyphCache;
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;
type RcGraphics = Rc<RefCell<GlGraphics>>;
type RcUi = Rc<RefCell<Ui<GlyphCache<'static>>>>;

struct App {
    window: RcWindow,
    graphics: RcGraphics,
    ui: RcUi,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    cursor: VirtualCursor,
    bg_color: Color
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.cursor.position = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs) {
        // We need to pass the window to update (and set the size here) because using
        // the update event from the window events queue is currently broken.
        self.translator.set_size(self.window.borrow().size());

        // Update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        // This demo translates and rebinds in a single screen, forcing a clone of the translator. This
        // limitation is a result of the necessity of providing a different interface for rebinding. One
        // way you could avoid a clone every update/render is to structure your application to pass the
        // translator between separate options and game screens.
        let mut rebind = self.translator.clone().into_rebind();

        let mut gl_graphics = self.graphics.borrow_mut();
        let ui = &mut *self.ui.borrow_mut();

        widget_ids! {
            X_INVERT_TOGGLE,
            Y_INVERT_TOGGLE
        }

        // Draw the ui
        gl_graphics.draw(args.viewport(), |c, gl| {
            Background::new().color(self.bg_color).set(ui);

            Toggle::new(rebind.get_x_motion_inverted())
                .xy(-350.0, 260.0)
                .dimensions(80.0, 40.0)
                .color(grayscale(0.4))
                .frame(1.0)
                .label(&"Invert X")
                .label_color(self.bg_color.plain_contrast())
                .label_font_size(16)
                .react(|b| rebind.set_x_motion_inverted(b))
                .set(X_INVERT_TOGGLE, ui);

            Toggle::new(rebind.get_y_motion_inverted())
                .xy(-350.0, 200.0)
                .dimensions(80.0, 40.0)
                .color(grayscale(0.4))
                .frame(1.0)
                .label(&"Invert Y")
                .label_color(self.bg_color.plain_contrast())
                .label_font_size(16)
                .react(|b| rebind.set_y_motion_inverted(b))
                .set(Y_INVERT_TOGGLE, ui);

            ui.draw(c, gl);
        });

        // Draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color.to_fsa(),
                                                                square,
                                                                c.transform,
                                                                gl));
        }

        // Draw the cursor dot
        {
            let dot = ellipse::circle(self.cursor.position[0],
                                      self.cursor.position[1],
                                      self.cursor.size);

            gl_graphics.draw(args.viewport(), |c, gl| ellipse(self.cursor.color.to_fsa(),
                                                              dot,
                                                              c.transform,
                                                              gl));
        }

        self.translator = rebind.into();
    }
}

struct Character {
    color: Color,
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: Color, tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

struct VirtualCursor {
    position: [f64; 2],
    color: Color,
    size: f64
}

impl VirtualCursor {
    fn new() -> Self {
        VirtualCursor {
            position: [0.0, 0.0],
            color: green(),
            size: 5.0
        }
    }
}

#[cfg_attr(feature = "nightly", derive(Action))]
#[cfg_attr(not(feature = "nightly"),
           derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd))]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

#[cfg(not(feature = "nightly"))]
impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("rebind-example", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .ok()
        .expect("Could not create main window");

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = {
        use piston::input::keyboard::Key;
        use piston::input::Button::Keyboard;
        RebindBuilder::new(WINDOW_SIZE)
            .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
            .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
            .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
            .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
            .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
            .build_translator()
    };

    let character = {
        const INITIAL_CHARACTER_POS: [f64; 2] = [WINDOW_SIZE.0 as f64 / 20.0,
                                                 WINDOW_SIZE.1 as f64 * 0.85];
        Character::new(red(), INITIAL_CHARACTER_POS, 50.0)
    };

    let ui = {
        let glyph_cache = {
            let font_path = find_folder::Search::ParentsThenKids(3, 3)
                .for_folder("assets")
                .ok()
                .expect("Could not find assets folder")
                .join("fonts/NotoSans/NotoSans-Regular.ttf");

            GlyphCache::new(&font_path)
                .ok()
                .expect("Could not find font file within assets folder")
        };

        Ui::new(glyph_cache, Theme::default())
    };

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        ui: Rc::new(RefCell::new(ui)),
        translator: translator,
        character: character,
        cursor: VirtualCursor::new(),
        bg_color: black()
    };

    for e in app.window.clone().events() {
        app.ui.borrow_mut().handle_event(&e);
        match e {
            Event::Input(i) => { app.input(&i); },
            Event::Update(u) => { app.update(&u); },
            Event::Render(r) => { app.render(&r); },
            _ => { }
        }
    }
}<commit_msg>`Result::expect` has been stabilised, so remove calls to `ok()`<commit_after>#![cfg_attr(feature = "nightly", feature(custom_derive, plugin))]
#![cfg_attr(feature = "nightly", plugin(rebind_plugins))]

#[macro_use] extern crate conrod;
extern crate find_folder;
extern crate glutin_window;
extern crate graphics;
extern crate rebind;
extern crate piston;
extern crate opengl_graphics;
extern crate viewport;

use conrod::{
    Background,
    Color,
    Colorable,
    Frameable,
    Labelable,
    Positionable,
    Sizeable,
    Theme,
    Toggle,
    Ui,
    Widget,
};
use conrod::color::{black, grayscale, green, red};
use glutin_window::GlutinWindow;
use graphics::*;
use opengl_graphics::{GlGraphics, OpenGL};
use opengl_graphics::glyph_cache::GlyphCache;
use piston::event_loop::{EventMap, Events};
use piston::input::{
    Event,
    Input,
    Motion,
    RenderArgs,
    RenderEvent,
    UpdateArgs,
    UpdateEvent
};
use piston::window::{Window, WindowSettings};
use rebind::{Action, InputTranslator, RebindBuilder, Translated};
use std::cell::RefCell;
use std::rc::Rc;

type RcWindow = Rc<RefCell<GlutinWindow>>;
type RcGraphics = Rc<RefCell<GlGraphics>>;
type RcUi = Rc<RefCell<Ui<GlyphCache<'static>>>>;

struct App {
    window: RcWindow,
    graphics: RcGraphics,
    ui: RcUi,
    translator: InputTranslator<CharacterAction>,
    character: Character,
    cursor: VirtualCursor,
    bg_color: Color
}

impl App {
    fn input(&mut self, input: &Input) {
        if let Some(t) = self.translator.translate(input) {
            match t {
                Translated::Press(action) => {
                    const CHARACTER_WALK_SPEED_INCREMENT: f64 = 400.0;
                    match action {
                        CharacterAction::Jump => {
                            println!("You jumped! Yay!");
                        },
                        CharacterAction::MoveLeft => {
                            if self.character.current_velocity[0] > (self.character.max_velocity[0] * -1.0) {
                                self.character.current_velocity[0] -= CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        },
                        CharacterAction::MoveRight => {
                            if self.character.current_velocity[0] < self.character.max_velocity[0] {
                                self.character.current_velocity[0] += CHARACTER_WALK_SPEED_INCREMENT;
                            }
                        }
                    }
                },
                Translated::Release(_) => {
                    self.character.current_velocity = [0.0, 0.0];
                },
                Translated::Move(Motion::MouseCursor(x, y)) => {
                    self.cursor.position = [x, y];
                },
                _ => { }
            }
        }
    }

    fn update(&mut self, args: &UpdateArgs) {
        // We need to pass the window to update (and set the size here) because using
        // the update event from the window events queue is currently broken.
        self.translator.set_size(self.window.borrow().size());

        // Update the character's velocity
        let ctl = self.character.topleft;
        let v = self.character.current_velocity;

        self.character.topleft = [ctl[0] + (v[0] * args.dt), ctl[1] + (v[1] * args.dt)];
    }

    fn render(&mut self, args: &RenderArgs) {
        // This demo translates and rebinds in a single screen, forcing a clone of the translator. This
        // limitation is a result of the necessity of providing a different interface for rebinding. One
        // way you could avoid a clone every update/render is to structure your application to pass the
        // translator between separate options and game screens.
        let mut rebind = self.translator.clone().into_rebind();

        let mut gl_graphics = self.graphics.borrow_mut();
        let ui = &mut *self.ui.borrow_mut();

        widget_ids! {
            X_INVERT_TOGGLE,
            Y_INVERT_TOGGLE
        }

        // Draw the ui
        gl_graphics.draw(args.viewport(), |c, gl| {
            Background::new().color(self.bg_color).set(ui);

            Toggle::new(rebind.get_x_motion_inverted())
                .xy(-350.0, 260.0)
                .dimensions(80.0, 40.0)
                .color(grayscale(0.4))
                .frame(1.0)
                .label(&"Invert X")
                .label_color(self.bg_color.plain_contrast())
                .label_font_size(16)
                .react(|b| rebind.set_x_motion_inverted(b))
                .set(X_INVERT_TOGGLE, ui);

            Toggle::new(rebind.get_y_motion_inverted())
                .xy(-350.0, 200.0)
                .dimensions(80.0, 40.0)
                .color(grayscale(0.4))
                .frame(1.0)
                .label(&"Invert Y")
                .label_color(self.bg_color.plain_contrast())
                .label_font_size(16)
                .react(|b| rebind.set_y_motion_inverted(b))
                .set(Y_INVERT_TOGGLE, ui);

            ui.draw(c, gl);
        });

        // Draw the character
        {
            let square = rectangle::square(self.character.topleft[0],
                                           self.character.topleft[1],
                                           self.character.size);

            gl_graphics.draw(args.viewport(), |c, gl| rectangle(self.character.color.to_fsa(),
                                                                square,
                                                                c.transform,
                                                                gl));
        }

        // Draw the cursor dot
        {
            let dot = ellipse::circle(self.cursor.position[0],
                                      self.cursor.position[1],
                                      self.cursor.size);

            gl_graphics.draw(args.viewport(), |c, gl| ellipse(self.cursor.color.to_fsa(),
                                                              dot,
                                                              c.transform,
                                                              gl));
        }

        self.translator = rebind.into();
    }
}

struct Character {
    color: Color,
    topleft: [f64; 2],
    current_velocity: [f64; 2],
    max_velocity: [f64; 2],
    size: f64
}

impl Character {
    fn new(col: Color, tl: [f64; 2], sz: f64) -> Self {
        Character {
            color: col,
            topleft: tl,
            current_velocity: [0.0, 0.0],
            max_velocity: [800.0, 0.0],
            size: sz
        }
    }
}

struct VirtualCursor {
    position: [f64; 2],
    color: Color,
    size: f64
}

impl VirtualCursor {
    fn new() -> Self {
        VirtualCursor {
            position: [0.0, 0.0],
            color: green(),
            size: 5.0
        }
    }
}

#[cfg_attr(feature = "nightly", derive(Action))]
#[cfg_attr(not(feature = "nightly"),
           derive(Copy, Clone, Eq, Hash, Ord, PartialEq, PartialOrd))]
enum CharacterAction {
    Jump,
    MoveLeft,
    MoveRight
}

#[cfg(not(feature = "nightly"))]
impl Action for CharacterAction { }

fn main() {
    const OPENGL: OpenGL = OpenGL::V3_2;
    const WINDOW_SIZE: (u32, u32) = (800, 600);

    let window = WindowSettings::new("rebind-example", WINDOW_SIZE)
        .exit_on_esc(true)
        .fullscreen(false)
        .opengl(OPENGL)
        .vsync(true)
        .build()
        .expect("Could not create main window");

    let gl_graphics = GlGraphics::new(OPENGL);

    let translator = {
        use piston::input::keyboard::Key;
        use piston::input::Button::Keyboard;
        RebindBuilder::new(WINDOW_SIZE)
            .with_action_mapping(Keyboard(Key::Space), CharacterAction::Jump)
            .with_action_mapping(Keyboard(Key::Left),  CharacterAction::MoveLeft)
            .with_action_mapping(Keyboard(Key::A),     CharacterAction::MoveLeft)
            .with_action_mapping(Keyboard(Key::Right), CharacterAction::MoveRight)
            .with_action_mapping(Keyboard(Key::D),     CharacterAction::MoveRight)
            .build_translator()
    };

    let character = {
        const INITIAL_CHARACTER_POS: [f64; 2] = [WINDOW_SIZE.0 as f64 / 20.0,
                                                 WINDOW_SIZE.1 as f64 * 0.85];
        Character::new(red(), INITIAL_CHARACTER_POS, 50.0)
    };

    let ui = {
        let glyph_cache = {
            let font_path = find_folder::Search::ParentsThenKids(3, 3)
                .for_folder("assets")
                .expect("Could not find assets folder")
                .join("fonts/NotoSans/NotoSans-Regular.ttf");

            GlyphCache::new(&font_path)
                .expect("Could not find font file within assets folder")
        };

        Ui::new(glyph_cache, Theme::default())
    };

    let mut app = App {
        window: Rc::new(RefCell::new(window)),
        graphics: Rc::new(RefCell::new(gl_graphics)),
        ui: Rc::new(RefCell::new(ui)),
        translator: translator,
        character: character,
        cursor: VirtualCursor::new(),
        bg_color: black()
    };

    for e in app.window.clone().events() {
        app.ui.borrow_mut().handle_event(&e);
        match e {
            Event::Input(i) => { app.input(&i); },
            Event::Update(u) => { app.update(&u); },
            Event::Render(r) => { app.render(&r); },
            _ => { }
        }
    }
}<|endoftext|>


<commit_before>use lazy_static::lazy_static;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::time::Instant;
use std::{env, fs, usize};
use tree_sitter::{Language, Parser};
use tree_sitter_cli::loader::Loader;

include!("../src/tests/helpers/dirs.rs");

lazy_static! {
    static ref LANGUAGE_FILTER: Option<String> =
        env::var("TREE_SITTER_BENCHMARK_LANGUAGE_FILTER").ok();
    static ref EXAMPLE_FILTER: Option<String> =
        env::var("TREE_SITTER_BENCHMARK_EXAMPLE_FILTER").ok();
    static ref TEST_LOADER: Loader = Loader::new(SCRATCH_DIR.clone());
    static ref EXAMPLE_PATHS_BY_LANGUAGE_NAME: BTreeMap<String, Vec<PathBuf>> = {
        let mut result = BTreeMap::new();
        let grammar_dirs = fs::read_dir(&(*GRAMMARS_DIR)).unwrap();
        for grammar_dir in grammar_dirs {
            let grammar_dir = grammar_dir.unwrap();
            if !grammar_dir.path().is_dir() {
                continue;
            }

            let language_name = grammar_dir.file_name();
            let language_name = language_name.to_str().unwrap();
            if let Ok(example_files) = fs::read_dir(&grammar_dir.path().join("examples")) {
                result.insert(
                    language_name.to_string(),
                    example_files
                        .filter_map(|p| {
                            let p = p.unwrap().path();
                            if p.is_file() {
                                Some(p)
                            } else {
                                None
                            }
                        })
                        .collect(),
                );
            } else {
                result.insert(language_name.to_string(), Vec::new());
            }
        }

        result
    };
}

fn main() {
    let mut parser = Parser::new();
    let max_path_length = EXAMPLE_PATHS_BY_LANGUAGE_NAME
        .iter()
        .flat_map(|(_, paths)| paths.iter())
        .map(|p| p.file_name().unwrap().to_str().unwrap().chars().count())
        .max()
        .unwrap();

    let mut all_normal_speeds = Vec::new();
    let mut all_error_speeds = Vec::new();

    for (language_name, example_paths) in EXAMPLE_PATHS_BY_LANGUAGE_NAME.iter() {
        // TODO - remove after fixing slow error parsing HTML.
        if language_name == "html" {
            continue;
        }

        if let Some(filter) = LANGUAGE_FILTER.as_ref() {
            if language_name != filter.as_str() {
                continue;
            }
        }

        eprintln!("\nLanguage: {}", language_name);
        parser.set_language(get_language(language_name)).unwrap();

        eprintln!("  Normal examples:");
        let mut normal_speeds = Vec::new();
        for example_path in example_paths {
            if let Some(filter) = EXAMPLE_FILTER.as_ref() {
                if !example_path.to_str().unwrap().contains(filter.as_str()) {
                    continue;
                }
            }

            normal_speeds.push(parse(&mut parser, example_path, max_path_length));
        }

        eprintln!("  Error examples (mismatched languages):");
        let mut error_speeds = Vec::new();
        for (other_language_name, example_paths) in EXAMPLE_PATHS_BY_LANGUAGE_NAME.iter() {
            if other_language_name != language_name {
                for example_path in example_paths {
                    if let Some(filter) = EXAMPLE_FILTER.as_ref() {
                        if !example_path.to_str().unwrap().contains(filter.as_str()) {
                            continue;
                        }
                    }

                    error_speeds.push(parse(&mut parser, example_path, max_path_length));
                }
            }
        }

        if let Some((average_normal, worst_normal)) = aggregate(&normal_speeds) {
            eprintln!("  Average Speed (normal): {} bytes/ms", average_normal);
            eprintln!("  Worst Speed (normal):   {} bytes/ms", worst_normal);
        }

        if let Some((average_error, worst_error)) = aggregate(&error_speeds) {
            eprintln!("  Average Speed (errors): {} bytes/ms", average_error);
            eprintln!("  Worst Speed (errors):   {} bytes/ms", worst_error);
        }

        all_normal_speeds.extend(normal_speeds);
        all_error_speeds.extend(error_speeds);
    }

    eprintln!("\nOverall");
    if let Some((average_normal, worst_normal)) = aggregate(&all_normal_speeds) {
        eprintln!("  Average Speed (normal): {} bytes/ms", average_normal);
        eprintln!("  Worst Speed (normal):   {} bytes/ms", worst_normal);
    }

    if let Some((average_error, worst_error)) = aggregate(&all_error_speeds) {
        eprintln!("  Average Speed (errors): {} bytes/ms", average_error);
        eprintln!("  Worst Speed (errors):   {} bytes/ms", worst_error);
    }
    eprintln!("");
}

fn aggregate(speeds: &Vec<(usize)>) -> Option<(usize, usize)> {
    if speeds.is_empty() {
        return None;
    }
    let mut total = 0;
    let mut max = usize::MAX;
    for speed in speeds.iter().cloned() {
        total += speed;
        if speed < max {
            max = speed;
        }
    }
    Some((total / speeds.len(), max))
}

fn parse(parser: &mut Parser, example_path: &Path, max_path_length: usize) -> usize {
    eprint!(
        "    {:width$}\t",
        example_path.file_name().unwrap().to_str().unwrap(),
        width = max_path_length
    );

    let source_code = fs::read(example_path).unwrap();
    let time = Instant::now();
    let _tree = parser
        .parse(&source_code, None)
        .expect("Incompatible language version");
    let duration = time.elapsed();
    let duration_ms =
        duration.as_secs() as f64 * 1000.0 + duration.subsec_nanos() as f64 / 1000000.0;
    let speed = (source_code.len() as f64 / duration_ms) as usize;
    eprintln!("time {} ms\tspeed {} bytes/ms", duration_ms as usize, speed);
    speed
}

fn get_language(name: &str) -> Language {
    TEST_LOADER
        .load_language_at_path(name, &GRAMMARS_DIR.join(name).join("src"), &HEADER_DIR)
        .unwrap()
}
<commit_msg>benchmarks: Use fixture languages' own copies of parser.h when compiling them<commit_after>use lazy_static::lazy_static;
use std::collections::BTreeMap;
use std::path::{Path, PathBuf};
use std::time::Instant;
use std::{env, fs, usize};
use tree_sitter::{Language, Parser};
use tree_sitter_cli::loader::Loader;

include!("../src/tests/helpers/dirs.rs");

lazy_static! {
    static ref LANGUAGE_FILTER: Option<String> =
        env::var("TREE_SITTER_BENCHMARK_LANGUAGE_FILTER").ok();
    static ref EXAMPLE_FILTER: Option<String> =
        env::var("TREE_SITTER_BENCHMARK_EXAMPLE_FILTER").ok();
    static ref TEST_LOADER: Loader = Loader::new(SCRATCH_DIR.clone());
    static ref EXAMPLE_PATHS_BY_LANGUAGE_NAME: BTreeMap<String, Vec<PathBuf>> = {
        let mut result = BTreeMap::new();
        let grammar_dirs = fs::read_dir(&(*GRAMMARS_DIR)).unwrap();
        for grammar_dir in grammar_dirs {
            let grammar_dir = grammar_dir.unwrap();
            if !grammar_dir.path().is_dir() {
                continue;
            }

            let language_name = grammar_dir.file_name();
            let language_name = language_name.to_str().unwrap();
            if let Ok(example_files) = fs::read_dir(&grammar_dir.path().join("examples")) {
                result.insert(
                    language_name.to_string(),
                    example_files
                        .filter_map(|p| {
                            let p = p.unwrap().path();
                            if p.is_file() {
                                Some(p)
                            } else {
                                None
                            }
                        })
                        .collect(),
                );
            } else {
                result.insert(language_name.to_string(), Vec::new());
            }
        }

        result
    };
}

fn main() {
    let mut parser = Parser::new();
    let max_path_length = EXAMPLE_PATHS_BY_LANGUAGE_NAME
        .iter()
        .flat_map(|(_, paths)| paths.iter())
        .map(|p| p.file_name().unwrap().to_str().unwrap().chars().count())
        .max()
        .unwrap();

    let mut all_normal_speeds = Vec::new();
    let mut all_error_speeds = Vec::new();

    for (language_name, example_paths) in EXAMPLE_PATHS_BY_LANGUAGE_NAME.iter() {
        // TODO - remove after fixing slow error parsing HTML.
        if language_name == "html" {
            continue;
        }

        if let Some(filter) = LANGUAGE_FILTER.as_ref() {
            if language_name != filter.as_str() {
                continue;
            }
        }

        eprintln!("\nLanguage: {}", language_name);
        parser.set_language(get_language(language_name)).unwrap();

        eprintln!("  Normal examples:");
        let mut normal_speeds = Vec::new();
        for example_path in example_paths {
            if let Some(filter) = EXAMPLE_FILTER.as_ref() {
                if !example_path.to_str().unwrap().contains(filter.as_str()) {
                    continue;
                }
            }

            normal_speeds.push(parse(&mut parser, example_path, max_path_length));
        }

        eprintln!("  Error examples (mismatched languages):");
        let mut error_speeds = Vec::new();
        for (other_language_name, example_paths) in EXAMPLE_PATHS_BY_LANGUAGE_NAME.iter() {
            if other_language_name != language_name {
                for example_path in example_paths {
                    if let Some(filter) = EXAMPLE_FILTER.as_ref() {
                        if !example_path.to_str().unwrap().contains(filter.as_str()) {
                            continue;
                        }
                    }

                    error_speeds.push(parse(&mut parser, example_path, max_path_length));
                }
            }
        }

        if let Some((average_normal, worst_normal)) = aggregate(&normal_speeds) {
            eprintln!("  Average Speed (normal): {} bytes/ms", average_normal);
            eprintln!("  Worst Speed (normal):   {} bytes/ms", worst_normal);
        }

        if let Some((average_error, worst_error)) = aggregate(&error_speeds) {
            eprintln!("  Average Speed (errors): {} bytes/ms", average_error);
            eprintln!("  Worst Speed (errors):   {} bytes/ms", worst_error);
        }

        all_normal_speeds.extend(normal_speeds);
        all_error_speeds.extend(error_speeds);
    }

    eprintln!("\nOverall");
    if let Some((average_normal, worst_normal)) = aggregate(&all_normal_speeds) {
        eprintln!("  Average Speed (normal): {} bytes/ms", average_normal);
        eprintln!("  Worst Speed (normal):   {} bytes/ms", worst_normal);
    }

    if let Some((average_error, worst_error)) = aggregate(&all_error_speeds) {
        eprintln!("  Average Speed (errors): {} bytes/ms", average_error);
        eprintln!("  Worst Speed (errors):   {} bytes/ms", worst_error);
    }
    eprintln!("");
}

fn aggregate(speeds: &Vec<(usize)>) -> Option<(usize, usize)> {
    if speeds.is_empty() {
        return None;
    }
    let mut total = 0;
    let mut max = usize::MAX;
    for speed in speeds.iter().cloned() {
        total += speed;
        if speed < max {
            max = speed;
        }
    }
    Some((total / speeds.len(), max))
}

fn parse(parser: &mut Parser, example_path: &Path, max_path_length: usize) -> usize {
    eprint!(
        "    {:width$}\t",
        example_path.file_name().unwrap().to_str().unwrap(),
        width = max_path_length
    );

    let source_code = fs::read(example_path).unwrap();
    let time = Instant::now();
    let _tree = parser
        .parse(&source_code, None)
        .expect("Incompatible language version");
    let duration = time.elapsed();
    let duration_ms =
        duration.as_secs() as f64 * 1000.0 + duration.subsec_nanos() as f64 / 1000000.0;
    let speed = (source_code.len() as f64 / duration_ms) as usize;
    eprintln!("time {} ms\tspeed {} bytes/ms", duration_ms as usize, speed);
    speed
}

fn get_language(name: &str) -> Language {
    let src_dir = GRAMMARS_DIR.join(name).join("src");
    TEST_LOADER
        .load_language_at_path(name, &src_dir, &src_dir)
        .unwrap()
}
<|endoftext|>


<commit_before>use crate::Error;
use csv;
use failure::ResultExt;
use flate2::read::GzDecoder;
use mimir::rubber::{IndexSettings, IndexVisibility, Rubber};
use mimir::Addr;
use par_map::ParMap;
use serde::de::DeserializeOwned;
use slog_scope::{error, info, warn};
use std::fs::File;
use std::io::Read;
use std::marker::{Send, Sync};
use std::path::PathBuf;

fn import_addresses<T, F>(
    rubber: &mut Rubber,
    nb_threads: usize,
    index_settings: IndexSettings,
    dataset: &str,
    addresses: impl IntoIterator<Item = T>,
    into_addr: F,
) -> Result<(), Error>
where
    F: Fn(T) -> Result<Addr, Error> + Send + Sync + 'static,
    T: DeserializeOwned + Send + 'static,
{
    let addr_index = rubber
        .make_index(dataset, &index_settings)
        .with_context(|_| format!("Error occurred when making index {}", dataset))?;

    info!("Add data in elasticsearch db.");
    let iter = addresses
        .into_iter()
        .with_nb_threads(nb_threads)
        .par_map(into_addr)
        .filter_map(|ra| match ra {
            Ok(a) => {
                if a.street.name.is_empty() {
                    warn!("Address {} has no street name and has been ignored.", a.id);
                    None
                } else {
                    Some(a)
                }
            }
            Err(err) => {
                warn!("Address Error ignored: {}", err);
                None
            }
        });

    let nb = rubber
        .bulk_index(&addr_index, iter)
        .with_context(|err| format!("failed to bulk insert: {}", err))?;
    info!("importing addresses: {} addresses added.", nb);

    rubber
        .publish_index(dataset, addr_index, IndexVisibility::Public)
        .context("Error while publishing the index")?;
    Ok(())
}

pub fn import_addresses_from_streams<T, F>(
    rubber: &mut Rubber,
    has_headers: bool,
    nb_threads: usize,
    index_settings: IndexSettings,
    dataset: &str,
    streams: impl IntoIterator<Item = impl Read>,
    into_addr: F,
) -> Result<(), Error>
where
    F: Fn(T) -> Result<Addr, Error> + Send + Sync + 'static,
    T: DeserializeOwned + Send + 'static,
{
    let iter = streams
        .into_iter()
        .flat_map(|stream| {
            csv::ReaderBuilder::new()
                .has_headers(has_headers)
                .from_reader(stream)
                .into_deserialize()
        })
        .filter_map(|line| {
            line.map_err(|e| warn!("Impossible to read line, error: {}", e))
                .ok()
        });

    import_addresses(rubber, nb_threads, index_settings, dataset, iter, into_addr)
}

pub fn import_addresses_from_files<T, F>(
    rubber: &mut Rubber,
    has_headers: bool,
    nb_threads: usize,
    index_settings: IndexSettings,
    dataset: &str,
    files: impl IntoIterator<Item = PathBuf>,
    into_addr: F,
) -> Result<(), Error>
where
    F: Fn(T) -> Result<Addr, Error> + Send + Sync + 'static,
    T: DeserializeOwned + Send + 'static,
{
    let streams = files.into_iter().filter_map(|path| {
        info!("importing {:?}...", &path);

        let with_gzip = path
            .extension()
            .and_then(|ext| ext.to_str())
            .map(|ext| ext == "gz")
            .unwrap_or(false);

        File::open(&path)
            .map_err(|err| error!("Impossible to read file {:?}, error: {}", path, err))
            .map(|file| {
                if with_gzip {
                    let decoder = GzDecoder::new(file);
                    Box::new(decoder) as Box<dyn Read>
                } else {
                    Box::new(file) as Box<dyn Read>
                }
            })
            .ok()
    });

    import_addresses_from_streams(
        rubber,
        has_headers,
        nb_threads,
        index_settings,
        dataset,
        streams,
        into_addr,
    )
}
<commit_msg>compute statistics about count of addresses per country<commit_after>use crate::Error;
use csv;
use failure::ResultExt;
use flate2::read::GzDecoder;
use mimir::rubber::{IndexSettings, IndexVisibility, Rubber};
use mimir::Addr;
use par_map::ParMap;
use serde::de::DeserializeOwned;
use slog_scope::{error, info, warn};
use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::marker::{Send, Sync};
use std::path::PathBuf;

fn import_addresses<T, F>(
    rubber: &mut Rubber,
    nb_threads: usize,
    index_settings: IndexSettings,
    dataset: &str,
    addresses: impl IntoIterator<Item = T>,
    into_addr: F,
) -> Result<(), Error>
where
    F: Fn(T) -> Result<Addr, Error> + Send + Sync + 'static,
    T: DeserializeOwned + Send + 'static,
{
    let addr_index = rubber
        .make_index(dataset, &index_settings)
        .with_context(|_| format!("Error occurred when making index {}", dataset))?;

    info!("Add data in elasticsearch db.");

    let mut country_stats = HashMap::new();

    let iter = addresses
        .into_iter()
        .with_nb_threads(nb_threads)
        .par_map(into_addr)
        .filter_map(|ra| match ra {
            Ok(a) => {
                if a.street.name.is_empty() {
                    warn!("Address {} has no street name and has been ignored.", a.id);
                    None
                } else {
                    Some(a)
                }
            }
            Err(err) => {
                warn!("Address Error ignored: {}", err);
                None
            }
        })
        .inspect(|addr| {
            let country_code = addr
                .country_codes
                .first()
                .map(|string| string.as_str())
                .unwrap_or("other");

            if let Some(count) = country_stats.get_mut(country_code) {
                *count += 1;
            } else {
                country_stats.insert(country_code.to_string(), 1);
            }
        });

    let nb = rubber
        .bulk_index(&addr_index, iter)
        .with_context(|err| format!("failed to bulk insert: {}", err))?;
    info!("importing addresses: {} addresses added.", nb);
    rubber
        .publish_index(dataset, addr_index, IndexVisibility::Public)
        .context("Error while publishing the index")?;

    info!("Addresses imported per country:");
    let mut country_stats: Vec<_> = country_stats.into_iter().collect();
    country_stats.sort_unstable_by_key(|(_, count)| *count);

    for (country, count) in country_stats.into_iter().rev() {
        info!("{:>10} {}", country, count);
    }

    Ok(())
}

pub fn import_addresses_from_streams<T, F>(
    rubber: &mut Rubber,
    has_headers: bool,
    nb_threads: usize,
    index_settings: IndexSettings,
    dataset: &str,
    streams: impl IntoIterator<Item = impl Read>,
    into_addr: F,
) -> Result<(), Error>
where
    F: Fn(T) -> Result<Addr, Error> + Send + Sync + 'static,
    T: DeserializeOwned + Send + 'static,
{
    let iter = streams
        .into_iter()
        .flat_map(|stream| {
            csv::ReaderBuilder::new()
                .has_headers(has_headers)
                .from_reader(stream)
                .into_deserialize()
        })
        .filter_map(|line| {
            line.map_err(|e| warn!("Impossible to read line, error: {}", e))
                .ok()
        });

    import_addresses(rubber, nb_threads, index_settings, dataset, iter, into_addr)
}

pub fn import_addresses_from_files<T, F>(
    rubber: &mut Rubber,
    has_headers: bool,
    nb_threads: usize,
    index_settings: IndexSettings,
    dataset: &str,
    files: impl IntoIterator<Item = PathBuf>,
    into_addr: F,
) -> Result<(), Error>
where
    F: Fn(T) -> Result<Addr, Error> + Send + Sync + 'static,
    T: DeserializeOwned + Send + 'static,
{
    let streams = files.into_iter().filter_map(|path| {
        info!("importing {:?}...", &path);

        let with_gzip = path
            .extension()
            .and_then(|ext| ext.to_str())
            .map(|ext| ext == "gz")
            .unwrap_or(false);

        File::open(&path)
            .map_err(|err| error!("Impossible to read file {:?}, error: {}", path, err))
            .map(|file| {
                if with_gzip {
                    let decoder = GzDecoder::new(file);
                    Box::new(decoder) as Box<dyn Read>
                } else {
                    Box::new(file) as Box<dyn Read>
                }
            })
            .ok()
    });

    import_addresses_from_streams(
        rubber,
        has_headers,
        nb_threads,
        index_settings,
        dataset,
        streams,
        into_addr,
    )
}
<|endoftext|>


<commit_before>//! A hangman game to guess the hidden word.
// To run this example, use the command: cargo run --features unstable --example hangman
#[cfg(all(not(feature = "unstable")))]
compile_error!("This example relies on unstable features. Run with `--features unstable`");

use std::io::{Error, Write};

use graphic::HangmanDesign;
use turtle::{rand::RandomSlice, Drawing, Point, Speed, Turtle};

// A small word list to choose from
const WORDS: &[&str; 28] = &[
    "poetry",
    "formal",
    "type",
    "expresses",
    "personal",
    "emotions",
    "feelings",
    "typically",
    "spoken",
    "equivalent",
    "lyrics",
    "term",
    "derives",
    "literature",
    "defined",
    "musical",
    "accompaniment",
    "usually",
    "stringed",
    "instrument",
    "term",
    "importance",
    "literary",
    "theory",
    "division",
    "developed",
    "between",
    "categories",
];

fn main() {
    let mut drawing = Drawing::new();

    // the turtle that is responsible for drawing the hangman design
    let mut hangman = drawing.add_turtle();

    // the turtle that is responsible for drawing the lines on which the letters appear.
    let mut lines = drawing.add_turtle();

    // the turtle that is responsible for drawing the Won or lost smiley.
    let mut wl = drawing.add_turtle();
    wl.hide();

    // the secret word that is to be guessed
    let secret = *WORDS.choose().unwrap();

    // this list stores all the steps of the hangman so that they can be drawn from a for loop or an index.
    let hangman_design = HangmanDesign::default();
    graphic::setup(&mut hangman);

    let mut all_guesses = String::new();

    // Loop over each step basically there are two ways to do this either one loops over the steps or one loops over guesses. Both need two nested loops.
    // HangmanDesign has the [`Iterator`] trait implemented which is why we can loop it directly.
    // The `'won:` syntax is to be able to conviniently `break` from both nested loops.
    'won: for step in hangman_design {
        // for each step we only draw it after a wron guess. So the following loops guesses and only `break`s to the for loop on a wrong guess. If all characters have been guessed correctly then it `break`s both loops and continues with a won or lost smiley.
        loop {
            draw_lines(&mut lines, secret, &all_guesses);
            let mut new_guess = String::new();
            ask_for_letter(&mut new_guess).expect("Failed to read letter");
            if secret.contains(&new_guess) {
                println!("This was correct!");
                all_guesses += &new_guess;
                if secret.chars().all(|c| all_guesses.contains(c)) {
                    break 'won;
                }
            } else {
                println!("No that letter is not in the word");
                break;
            }
        }
        step(&mut hangman);
    }
    // Redraw the lines else they could be outdated.
    draw_lines(&mut lines, secret, &all_guesses);
    wl.set_speed(Speed::instant());
    // Draw a circle
    wl.arc_right(100.0, 360.0);
    // Draw the left eye
    teleport_relative(&mut wl, Point { x: 55.0, y: 40.0 });
    wl.arc_right(5.0, 360.0);
    // Draw the right eye
    teleport_relative(&mut wl, Point { x: 85.0, y: 0.0 });
    wl.arc_right(5.0, 360.0);
    // Go to the start of the mouth
    teleport_relative(&mut wl, Point { x: -85.0, y: -80.0 });
    wl.right(90.0);
    // Either draw it smiling or sad
    if secret.chars().all(|c| all_guesses.contains(c)) {
        println!("You Won!");
        wl.right(45.0);
        wl.arc_left(65.0, 90.0);
    } else {
        println!("You Lost!");
        wl.left(45.0);
        wl.arc_right(65.0, 90.0);
    }
}

/// A function to ask the user for a letter
fn ask_for_letter(guess: &mut String) -> Result<(), Error> {
    print!("Please guess a letter: ");
    std::io::stdout().flush()?;
    std::io::stdin().read_line(guess)?;

    // remove trailing newline characters
    let to_trim = guess.trim_end_matches(&['\r', '\n'][..]).len();
    guess.truncate(to_trim);

    println!("You guessed: {}", &guess);
    Ok(())
}

/// A function to conveniently teleport the turtle relative to its current position.
fn teleport_relative(turtle: &mut Turtle, direction: Point) {
    let cur = turtle.position();
    let new: Point = cur + direction;
    turtle.pen_up();
    turtle.go_to(new);
    turtle.pen_down();
}

/// Draw the lines one for each character. If the character has been guessed already correctly draw a green circle above the line.
fn draw_lines(lines: &mut Turtle, secret: &str, all_guesses: &str) {
    // hide the lines-turtle and go th the startigpoint of the lines.
    lines.reset();
    lines.hide();
    lines.set_speed(Speed::instant());
    lines.right(90.0);
    teleport_relative(lines, Point { x: -300.0, y: 200.0 });

    // draw a line for every character in the secret.
    for letter in secret.chars() {
        if all_guesses.contains(letter) {
            teleport_relative(lines, Point { x: 2.5, y: 10.0 });

            // as turtle is not yet able to draw text draw a green replacement circle.
            lines.left(90.0);
            lines.set_fill_color("green");
            lines.set_pen_color("green");
            lines.begin_fill();
            lines.arc_right(7.5, 360.0);
            lines.end_fill();
            lines.set_pen_color("black");
            lines.right(90.0);

            teleport_relative(lines, Point { x: -2.5, y: -10.0 });

            // print to the terminal
            print!(" {} ", letter);
        } else {
            print!(" _ ");
        }
        lines.forward(20.0);
        teleport_relative(lines, Point { x: 20.0, y: 0.0 });
    }
    println!(); // to switch to the next line and flush the output.
}

mod graphic {
    //! This module contains the drawing logic for the hangman graphic
    //!
    //! It is used by creating an instance of a [`HangmanDesign`] struct using [`HangmanDesign::default()`].
    //! Therein is contained a list of all the steps neccessary to draw the hangman in chronologic order.

    use turtle::{Point, Speed, Turtle};

    use crate::teleport_relative;

    pub struct HangmanDesign {
        state: usize,
        steps: &'static [fn(&mut turtle::Turtle); 9],
    }

    impl Default for HangmanDesign {
        fn default() -> Self {
            Self {
                state: 0,
                steps: &[hill, mast, bar, support, rope, head, arms, body, legs],
            }
        }
    }

    impl Iterator for HangmanDesign {
        type Item = &'static fn(&mut turtle::Turtle);

        fn next(&mut self) -> Option<Self::Item> {
            self.state += 1;
            self.steps.get(self.state - 1)
        }
    }

    pub fn setup(hangman: &mut Turtle) {
        // hide the hangman-turtle and go th the startigpoint of the graphic.
        hangman.hide();
        hangman.set_speed(Speed::instant());
        hangman.set_pen_size(10.0);
        teleport_relative(hangman, Point { x: -300.0, y: -300.0 });
    }

    fn hill(hangman: &mut Turtle) {
        // start drawing
        hangman.arc_right(100.0, 180.0);
        hangman.left(180.0);
        hangman.arc_left(100.0, 90.0);
        hangman.right(90.0);
    }

    fn mast(hangman: &mut Turtle) {
        hangman.forward(300.0);
    }

    fn bar(hangman: &mut Turtle) {
        hangman.right(90.0);
        hangman.forward(150.0);
    }

    fn support(hangman: &mut Turtle) {
        hangman.backward(100.0);
        hangman.right(135.0);
        hangman.forward(70.710678119);
        hangman.backward(70.710678119);
        hangman.left(135.0);
        hangman.forward(100.0);
    }

    fn rope(hangman: &mut Turtle) {
        hangman.set_pen_size(3.0);
        hangman.right(90.0);
        hangman.forward(70.0);
    }

    fn head(hangman: &mut Turtle) {
        hangman.left(90.0);
        hangman.arc_right(30.0, 540.0);
    }

    fn arms(hangman: &mut Turtle) {
        hangman.left(60.0);
        hangman.forward(100.0);
        hangman.backward(100.0);
        hangman.left(60.0);
        hangman.forward(100.0);
        hangman.backward(100.0);
        hangman.right(30.0);
    }

    fn body(hangman: &mut Turtle) {
        hangman.forward(100.0);
    }

    fn legs(hangman: &mut Turtle) {
        hangman.right(20.0);
        hangman.forward(120.0);
        hangman.backward(120.0);
        hangman.left(40.0);
        hangman.forward(120.0);
        hangman.backward(120.0);
        hangman.right(20.0);
    }
}
<commit_msg>remove the module<commit_after>//! A hangman game to guess the hidden word.
// To run this example, use the command: cargo run --features unstable --example hangman
#[cfg(all(not(feature = "unstable")))]
compile_error!("This example relies on unstable features. Run with `--features unstable`");

use std::io::{Error, Write};

use turtle::{rand::RandomSlice, Drawing, Point, Speed, Turtle};

// A small word list to choose from
const WORDS: &[&str; 28] = &[
    "poetry",
    "formal",
    "type",
    "expresses",
    "personal",
    "emotions",
    "feelings",
    "typically",
    "spoken",
    "equivalent",
    "lyrics",
    "term",
    "derives",
    "literature",
    "defined",
    "musical",
    "accompaniment",
    "usually",
    "stringed",
    "instrument",
    "term",
    "importance",
    "literary",
    "theory",
    "division",
    "developed",
    "between",
    "categories",
];

fn main() {
    let mut drawing = Drawing::new();

    // the turtle that is responsible for drawing the hangman design
    let mut hangman = drawing.add_turtle();

    // the turtle that is responsible for drawing the lines on which the letters appear.
    let mut lines = drawing.add_turtle();

    // the turtle that is responsible for drawing the Won or lost smiley.
    let mut wl = drawing.add_turtle();
    wl.hide();

    // the secret word that is to be guessed
    let secret = *WORDS.choose().unwrap();

    // this list stores all the steps of the hangman so that they can be drawn from a for loop or an index.
    let hangman_design = HangmanDesign::default();
    graphic::setup(&mut hangman);

    let mut all_guesses = String::new();

    // Loop over each step basically there are two ways to do this either one loops over the steps or one loops over guesses. Both need two nested loops.
    // HangmanDesign has the [`Iterator`] trait implemented which is why we can loop it directly.
    // The `'won:` syntax is to be able to conviniently `break` from both nested loops.
    'won: for step in hangman_design {
        // for each step we only draw it after a wron guess. So the following loops guesses and only `break`s to the for loop on a wrong guess. If all characters have been guessed correctly then it `break`s both loops and continues with a won or lost smiley.
        loop {
            draw_lines(&mut lines, secret, &all_guesses);
            let mut new_guess = String::new();
            ask_for_letter(&mut new_guess).expect("Failed to read letter");
            if secret.contains(&new_guess) {
                println!("This was correct!");
                all_guesses += &new_guess;
                if secret.chars().all(|c| all_guesses.contains(c)) {
                    break 'won;
                }
            } else {
                println!("No that letter is not in the word");
                break;
            }
        }
        step(&mut hangman);
    }
    // Redraw the lines else they could be outdated.
    draw_lines(&mut lines, secret, &all_guesses);
    wl.set_speed(Speed::instant());
    // Draw a circle
    wl.arc_right(100.0, 360.0);
    // Draw the left eye
    teleport_relative(&mut wl, Point { x: 55.0, y: 40.0 });
    wl.arc_right(5.0, 360.0);
    // Draw the right eye
    teleport_relative(&mut wl, Point { x: 85.0, y: 0.0 });
    wl.arc_right(5.0, 360.0);
    // Go to the start of the mouth
    teleport_relative(&mut wl, Point { x: -85.0, y: -80.0 });
    wl.right(90.0);
    // Either draw it smiling or sad
    if secret.chars().all(|c| all_guesses.contains(c)) {
        println!("You Won!");
        wl.right(45.0);
        wl.arc_left(65.0, 90.0);
    } else {
        println!("You Lost!");
        wl.left(45.0);
        wl.arc_right(65.0, 90.0);
    }
}

/// A function to ask the user for a letter
fn ask_for_letter(guess: &mut String) -> Result<(), Error> {
    print!("Please guess a letter: ");
    std::io::stdout().flush()?;
    std::io::stdin().read_line(guess)?;

    // remove trailing newline characters
    let to_trim = guess.trim_end_matches(&['\r', '\n'][..]).len();
    guess.truncate(to_trim);

    println!("You guessed: {}", &guess);
    Ok(())
}

/// A function to conveniently teleport the turtle relative to its current position.
fn teleport_relative(turtle: &mut Turtle, direction: Point) {
    let cur = turtle.position();
    let new: Point = cur + direction;
    turtle.pen_up();
    turtle.go_to(new);
    turtle.pen_down();
}

/// Draw the lines one for each character. If the character has been guessed already correctly draw a green circle above the line.
fn draw_lines(lines: &mut Turtle, secret: &str, all_guesses: &str) {
    // hide the lines-turtle and go th the startigpoint of the lines.
    lines.reset();
    lines.hide();
    lines.set_speed(Speed::instant());
    lines.right(90.0);
    teleport_relative(lines, Point { x: -300.0, y: 200.0 });

    // draw a line for every character in the secret.
    for letter in secret.chars() {
        if all_guesses.contains(letter) {
            teleport_relative(lines, Point { x: 2.5, y: 10.0 });

            // as turtle is not yet able to draw text draw a green replacement circle.
            lines.left(90.0);
            lines.set_fill_color("green");
            lines.set_pen_color("green");
            lines.begin_fill();
            lines.arc_right(7.5, 360.0);
            lines.end_fill();
            lines.set_pen_color("black");
            lines.right(90.0);

            teleport_relative(lines, Point { x: -2.5, y: -10.0 });

            // print to the terminal
            print!(" {} ", letter);
        } else {
            print!(" _ ");
        }
        lines.forward(20.0);
        teleport_relative(lines, Point { x: 20.0, y: 0.0 });
    }
    println!(); // to switch to the next line and flush the output.
}

// This module contains the drawing logic for the hangman graphic
//
// It is used by creating an instance of a [`HangmanDesign`] struct using [`HangmanDesign::default()`].
// Therein is contained a list of all the steps neccessary to draw the hangman in chronologic order.

use turtle::{Point, Speed, Turtle};

use crate::teleport_relative;

pub struct HangmanDesign {
    state: usize,
    steps: &'static [fn(&mut turtle::Turtle); 9],
}

impl Default for HangmanDesign {
    fn default() -> Self {
        Self {
            state: 0,
            steps: &[hill, mast, bar, support, rope, head, arms, body, legs],
        }
    }
}

impl Iterator for HangmanDesign {
    type Item = &'static fn(&mut turtle::Turtle);

    fn next(&mut self) -> Option<Self::Item> {
        self.state += 1;
        self.steps.get(self.state - 1)
    }
}

pub fn setup(hangman: &mut Turtle) {
    // hide the hangman-turtle and go th the startigpoint of the graphic.
    hangman.hide();
    hangman.set_speed(Speed::instant());
    hangman.set_pen_size(10.0);
    teleport_relative(hangman, Point { x: -300.0, y: -300.0 });
}

fn hill(hangman: &mut Turtle) {
    // start drawing
    hangman.arc_right(100.0, 180.0);
    hangman.left(180.0);
    hangman.arc_left(100.0, 90.0);
    hangman.right(90.0);
}

fn mast(hangman: &mut Turtle) {
    hangman.forward(300.0);
}

fn bar(hangman: &mut Turtle) {
    hangman.right(90.0);
    hangman.forward(150.0);
}

fn support(hangman: &mut Turtle) {
    hangman.backward(100.0);
    hangman.right(135.0);
    hangman.forward(70.710678119);
    hangman.backward(70.710678119);
    hangman.left(135.0);
    hangman.forward(100.0);
}

fn rope(hangman: &mut Turtle) {
    hangman.set_pen_size(3.0);
    hangman.right(90.0);
    hangman.forward(70.0);
}

fn head(hangman: &mut Turtle) {
    hangman.left(90.0);
    hangman.arc_right(30.0, 540.0);
}

fn arms(hangman: &mut Turtle) {
    hangman.left(60.0);
    hangman.forward(100.0);
    hangman.backward(100.0);
    hangman.left(60.0);
    hangman.forward(100.0);
    hangman.backward(100.0);
    hangman.right(30.0);
}

fn body(hangman: &mut Turtle) {
    hangman.forward(100.0);
}

fn legs(hangman: &mut Turtle) {
    hangman.right(20.0);
    hangman.forward(120.0);
    hangman.backward(120.0);
    hangman.left(40.0);
    hangman.forward(120.0);
    hangman.backward(120.0);
    hangman.right(20.0);
}
<|endoftext|>


<commit_before>extern crate android_glue;
extern crate native;

use libc;
use self::native::NativeTaskBuilder;
use {Event, WindowBuilder};

pub struct Window {
    display: ffi::egl::types::EGLDisplay,
    context: ffi::egl::types::EGLContext,
    surface: ffi::egl::types::EGLSurface,
}

pub struct MonitorID;

mod ffi;

compile_warning!("The Android implementation is not fully working yet")

pub fn get_available_monitors() -> Vec<MonitorID> {
    vec![ MonitorID ]
}

pub fn get_primary_monitor() -> MonitorID {
    MonitorID
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        Some("Primary".to_string())
    }

    pub fn get_dimensions(&self) -> (uint, uint) {
        unimplemented!()
    }
}

impl Window {
    pub fn new(_builder: WindowBuilder) -> Result<Window, String> {
        use std::{mem, ptr};
        use std::task::TaskBuilder;

        let native_window = unsafe { android_glue::get_native_window() };
        if native_window.is_null() {
            return Err(format!("Android's native window is null"));
        }

        let display = unsafe {
            let display = ffi::egl::GetDisplay(mem::transmute(ffi::egl::DEFAULT_DISPLAY));
            if display.is_null() {
                return Err("No EGL display connection available".to_string());
            }
            display
        };

        android_glue::write_log("eglGetDisplay succeeded");

        let (_major, _minor) = unsafe {
            let mut major: ffi::egl::types::EGLint = mem::uninitialized();
            let mut minor: ffi::egl::types::EGLint = mem::uninitialized();

            if ffi::egl::Initialize(display, &mut major, &mut minor) == 0 {
                return Err(format!("eglInitialize failed"))
            }

            (major, minor)
        };

        android_glue::write_log("eglInitialize succeeded");

        let config = unsafe {
            let attribute_list = [
                ffi::egl::RED_SIZE as i32, 1,
                ffi::egl::GREEN_SIZE as i32, 1,
                ffi::egl::BLUE_SIZE as i32, 1,
                ffi::egl::NONE as i32
            ];

            let mut num_config: ffi::egl::types::EGLint = mem::uninitialized();
            let mut config: ffi::egl::types::EGLConfig = mem::uninitialized();
            if ffi::egl::ChooseConfig(display, attribute_list.as_ptr(), &mut config, 1,
                &mut num_config) == 0
            {
                return Err(format!("eglChooseConfig failed"))
            }

            if num_config <= 0 {
                return Err(format!("eglChooseConfig returned no available config"))
            }

            config
        };

        android_glue::write_log("eglChooseConfig succeeded");

        let context = unsafe {
            let context = ffi::egl::CreateContext(display, config, ptr::null(), ptr::null());
            if context.is_null() {
                return Err(format!("eglCreateContext failed"))
            }
            context
        };

        android_glue::write_log("eglCreateContext succeeded");

        let surface = unsafe {
            let surface = ffi::egl::CreateWindowSurface(display, config, native_window, ptr::null());
            if surface.is_null() {
                return Err(format!("eglCreateWindowSurface failed"))
            }
            surface
        };

        android_glue::write_log("eglCreateWindowSurface succeeded");

        Ok(Window {
            display: display,
            context: context,
            surface: surface,
        })
    }

    pub fn is_closed(&self) -> bool {
        false
    }

    pub fn set_title(&self, _: &str) {
    }

    pub fn get_position(&self) -> Option<(int, int)> {
        None
    }

    pub fn set_position(&self, _x: int, _y: int) {
    }

    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        let native_window = unsafe { android_glue::get_native_window() };

        if native_window.is_null() {
            None
        } else {
            Some((
                unsafe { ffi::ANativeWindow_getWidth(native_window) } as uint,
                unsafe { ffi::ANativeWindow_getHeight(native_window) } as uint
            ))
        }
    }

    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        self.get_inner_size()
    }

    pub fn set_inner_size(&self, _x: uint, _y: uint) {
    }

    pub fn poll_events(&self) -> Vec<Event> {
        use std::time::Duration;
        use std::io::timer;
        timer::sleep(Duration::milliseconds(16));
        Vec::new()
    }

    pub fn wait_events(&self) -> Vec<Event> {
        use std::time::Duration;
        use std::io::timer;
        timer::sleep(Duration::milliseconds(16));
        Vec::new()
    }

    pub fn make_current(&self) {
        unsafe {
            ffi::egl::MakeCurrent(self.display, self.surface, self.surface, self.context);
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;

        unsafe {
            addr.with_c_str(|s| {
                ffi::egl::GetProcAddress(s) as *const ()
            })
        }
    }

    pub fn swap_buffers(&self) {
        unsafe {
            ffi::egl::SwapBuffers(self.display, self.surface);
        }
    }
}

#[unsafe_destructor]
impl Drop for Window {
    fn drop(&mut self) {
        use std::ptr;

        unsafe {
            android_glue::write_log("Destroying gl-init window");
            ffi::egl::MakeCurrent(self.display, ptr::null(), ptr::null(), ptr::null());
            ffi::egl::DestroySurface(self.display, self.surface);
            ffi::egl::DestroyContext(self.display, self.context);
            ffi::egl::Terminate(self.display);
        }
    }
}
<commit_msg>Remove some unused imports<commit_after>extern crate android_glue;
extern crate native;

use {Event, WindowBuilder};

pub struct Window {
    display: ffi::egl::types::EGLDisplay,
    context: ffi::egl::types::EGLContext,
    surface: ffi::egl::types::EGLSurface,
}

pub struct MonitorID;

mod ffi;

compile_warning!("The Android implementation is not fully working yet")

pub fn get_available_monitors() -> Vec<MonitorID> {
    vec![ MonitorID ]
}

pub fn get_primary_monitor() -> MonitorID {
    MonitorID
}

impl MonitorID {
    pub fn get_name(&self) -> Option<String> {
        Some("Primary".to_string())
    }

    pub fn get_dimensions(&self) -> (uint, uint) {
        unimplemented!()
    }
}

impl Window {
    pub fn new(_builder: WindowBuilder) -> Result<Window, String> {
        use std::{mem, ptr};

        let native_window = unsafe { android_glue::get_native_window() };
        if native_window.is_null() {
            return Err(format!("Android's native window is null"));
        }

        let display = unsafe {
            let display = ffi::egl::GetDisplay(mem::transmute(ffi::egl::DEFAULT_DISPLAY));
            if display.is_null() {
                return Err("No EGL display connection available".to_string());
            }
            display
        };

        android_glue::write_log("eglGetDisplay succeeded");

        let (_major, _minor) = unsafe {
            let mut major: ffi::egl::types::EGLint = mem::uninitialized();
            let mut minor: ffi::egl::types::EGLint = mem::uninitialized();

            if ffi::egl::Initialize(display, &mut major, &mut minor) == 0 {
                return Err(format!("eglInitialize failed"))
            }

            (major, minor)
        };

        android_glue::write_log("eglInitialize succeeded");

        let config = unsafe {
            let attribute_list = [
                ffi::egl::RED_SIZE as i32, 1,
                ffi::egl::GREEN_SIZE as i32, 1,
                ffi::egl::BLUE_SIZE as i32, 1,
                ffi::egl::NONE as i32
            ];

            let mut num_config: ffi::egl::types::EGLint = mem::uninitialized();
            let mut config: ffi::egl::types::EGLConfig = mem::uninitialized();
            if ffi::egl::ChooseConfig(display, attribute_list.as_ptr(), &mut config, 1,
                &mut num_config) == 0
            {
                return Err(format!("eglChooseConfig failed"))
            }

            if num_config <= 0 {
                return Err(format!("eglChooseConfig returned no available config"))
            }

            config
        };

        android_glue::write_log("eglChooseConfig succeeded");

        let context = unsafe {
            let context = ffi::egl::CreateContext(display, config, ptr::null(), ptr::null());
            if context.is_null() {
                return Err(format!("eglCreateContext failed"))
            }
            context
        };

        android_glue::write_log("eglCreateContext succeeded");

        let surface = unsafe {
            let surface = ffi::egl::CreateWindowSurface(display, config, native_window, ptr::null());
            if surface.is_null() {
                return Err(format!("eglCreateWindowSurface failed"))
            }
            surface
        };

        android_glue::write_log("eglCreateWindowSurface succeeded");

        Ok(Window {
            display: display,
            context: context,
            surface: surface,
        })
    }

    pub fn is_closed(&self) -> bool {
        false
    }

    pub fn set_title(&self, _: &str) {
    }

    pub fn get_position(&self) -> Option<(int, int)> {
        None
    }

    pub fn set_position(&self, _x: int, _y: int) {
    }

    pub fn get_inner_size(&self) -> Option<(uint, uint)> {
        let native_window = unsafe { android_glue::get_native_window() };

        if native_window.is_null() {
            None
        } else {
            Some((
                unsafe { ffi::ANativeWindow_getWidth(native_window) } as uint,
                unsafe { ffi::ANativeWindow_getHeight(native_window) } as uint
            ))
        }
    }

    pub fn get_outer_size(&self) -> Option<(uint, uint)> {
        self.get_inner_size()
    }

    pub fn set_inner_size(&self, _x: uint, _y: uint) {
    }

    pub fn poll_events(&self) -> Vec<Event> {
        use std::time::Duration;
        use std::io::timer;
        timer::sleep(Duration::milliseconds(16));
        Vec::new()
    }

    pub fn wait_events(&self) -> Vec<Event> {
        use std::time::Duration;
        use std::io::timer;
        timer::sleep(Duration::milliseconds(16));
        Vec::new()
    }

    pub fn make_current(&self) {
        unsafe {
            ffi::egl::MakeCurrent(self.display, self.surface, self.surface, self.context);
        }
    }

    pub fn get_proc_address(&self, addr: &str) -> *const () {
        use std::c_str::ToCStr;

        unsafe {
            addr.with_c_str(|s| {
                ffi::egl::GetProcAddress(s) as *const ()
            })
        }
    }

    pub fn swap_buffers(&self) {
        unsafe {
            ffi::egl::SwapBuffers(self.display, self.surface);
        }
    }
}

#[unsafe_destructor]
impl Drop for Window {
    fn drop(&mut self) {
        use std::ptr;

        unsafe {
            android_glue::write_log("Destroying gl-init window");
            ffi::egl::MakeCurrent(self.display, ptr::null(), ptr::null(), ptr::null());
            ffi::egl::DestroySurface(self.display, self.surface);
            ffi::egl::DestroyContext(self.display, self.context);
            ffi::egl::Terminate(self.display);
        }
    }
}
<|endoftext|>


<commit_before>//! VFS stands for Virtual File System.
//!
//! When doing analysis, we don't want to do any IO, we want to keep all source
//! code in memory. However, the actual source code is stored on disk, so you
//! need to get it into the memory in the first place somehow. VFS is the
//! component which does this.
//!
//! It is also responsible for watching the disk for changes, and for merging
//! editor state (modified, unsaved files) with disk state.
//! TODO: Some LSP clients support watching the disk, so this crate should
//! to support custom watcher events (related to https://github.com/rust-analyzer/rust-analyzer/issues/131)
//!
//! VFS is based on a concept of roots: a set of directories on the file system
//! whihc are watched for changes. Typically, there will be a root for each
//! Cargo package.
mod arena;
mod io;

use std::{
    fmt,
    mem,
    thread,
    cmp::Reverse,
    path::{Path, PathBuf},
    ffi::OsStr,
    sync::Arc,
    fs,
};

use rustc_hash::{FxHashMap, FxHashSet};
use relative_path::RelativePathBuf;
use crossbeam_channel::Receiver;
use walkdir::DirEntry;
use thread_worker::WorkerHandle;

use crate::{
    arena::{ArenaId, Arena},
};

pub use crate::io::TaskResult as VfsTask;

/// `RootFilter` is a predicate that checks if a file can belong to a root. If
/// several filters match a file (nested dirs), the most nested one wins.
struct RootFilter {
    root: PathBuf,
    file_filter: fn(&Path) -> bool,
}

impl RootFilter {
    fn new(root: PathBuf) -> RootFilter {
        RootFilter {
            root,
            file_filter: has_rs_extension,
        }
    }
    /// Check if this root can contain `path`. NB: even if this returns
    /// true, the `path` might actually be conained in some nested root.
    fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> {
        if !(self.file_filter)(path) {
            return None;
        }
        if !(path.starts_with(&self.root)) {
            return None;
        }
        let path = path.strip_prefix(&self.root).unwrap();
        let path = RelativePathBuf::from_path(path).unwrap();
        Some(path)
    }
}

fn has_rs_extension(p: &Path) -> bool {
    p.extension() == Some(OsStr::new("rs"))
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct VfsRoot(pub u32);

impl ArenaId for VfsRoot {
    fn from_u32(idx: u32) -> VfsRoot {
        VfsRoot(idx)
    }
    fn to_u32(self) -> u32 {
        self.0
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct VfsFile(pub u32);

impl ArenaId for VfsFile {
    fn from_u32(idx: u32) -> VfsFile {
        VfsFile(idx)
    }
    fn to_u32(self) -> u32 {
        self.0
    }
}

struct VfsFileData {
    root: VfsRoot,
    path: RelativePathBuf,
    text: Arc<String>,
}

pub struct Vfs {
    roots: Arena<VfsRoot, RootFilter>,
    files: Arena<VfsFile, VfsFileData>,
    root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>,
    pending_changes: Vec<VfsChange>,
    worker: io::Worker,
    worker_handle: WorkerHandle,
}

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

impl Vfs {
    pub fn new(mut roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
        let (worker, worker_handle) = io::start();

        let mut res = Vfs {
            roots: Arena::default(),
            files: Arena::default(),
            root2files: FxHashMap::default(),
            worker,
            worker_handle,
            pending_changes: Vec::new(),
        };

        // A hack to make nesting work.
        roots.sort_by_key(|it| Reverse(it.as_os_str().len()));
        for (i, path) in roots.iter().enumerate() {
            let root = res.roots.alloc(RootFilter::new(path.clone()));
            res.root2files.insert(root, Default::default());
            let nested = roots[..i]
                .iter()
                .filter(|it| it.starts_with(path))
                .map(|it| it.clone())
                .collect::<Vec<_>>();
            let filter = move |entry: &DirEntry| {
                if entry.file_type().is_file() {
                    has_rs_extension(entry.path())
                } else {
                    nested.iter().all(|it| it != entry.path())
                }
            };
            let task = io::Task {
                root,
                path: path.clone(),
                filter: Box::new(filter),
            };
            res.worker.inp.send(task);
        }
        let roots = res.roots.iter().map(|(id, _)| id).collect();
        (res, roots)
    }

    pub fn root2path(&self, root: VfsRoot) -> PathBuf {
        self.roots[root].root.clone()
    }

    pub fn path2file(&self, path: &Path) -> Option<VfsFile> {
        if let Some((_root, _path, Some(file))) = self.find_root(path) {
            return Some(file);
        }
        None
    }

    pub fn file2path(&self, file: VfsFile) -> PathBuf {
        let rel_path = &self.files[file].path;
        let root_path = &self.roots[self.files[file].root].root;
        rel_path.to_path(root_path)
    }

    pub fn file_for_path(&self, path: &Path) -> Option<VfsFile> {
        if let Some((_root, _path, Some(file))) = self.find_root(path) {
            return Some(file);
        }
        None
    }

    pub fn load(&mut self, path: &Path) -> Option<VfsFile> {
        if let Some((root, rel_path, file)) = self.find_root(path) {
            return if let Some(file) = file {
                Some(file)
            } else {
                let text = fs::read_to_string(path).unwrap_or_default();
                let text = Arc::new(text);
                let file = self.add_file(root, rel_path.clone(), Arc::clone(&text));
                let change = VfsChange::AddFile {
                    file,
                    text,
                    root,
                    path: rel_path,
                };
                self.pending_changes.push(change);
                Some(file)
            };
        }
        None
    }

    pub fn task_receiver(&self) -> &Receiver<io::TaskResult> {
        &self.worker.out
    }

    pub fn handle_task(&mut self, task: io::TaskResult) {
        let mut files = Vec::new();
        // While we were scanning the root in the backgound, a file might have
        // been open in the editor, so we need to account for that.
        let exising = self.root2files[&task.root]
            .iter()
            .map(|&file| (self.files[file].path.clone(), file))
            .collect::<FxHashMap<_, _>>();
        for (path, text) in task.files {
            if let Some(&file) = exising.get(&path) {
                let text = Arc::clone(&self.files[file].text);
                files.push((file, path, text));
                continue;
            }
            let text = Arc::new(text);
            let file = self.add_file(task.root, path.clone(), Arc::clone(&text));
            files.push((file, path, text));
        }

        let change = VfsChange::AddRoot {
            root: task.root,
            files,
        };
        self.pending_changes.push(change);
    }

    pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> {
        let mut res = None;
        if let Some((root, path, file)) = self.find_root(path) {
            let text = Arc::new(text);
            let change = if let Some(file) = file {
                res = Some(file);
                self.change_file(file, Arc::clone(&text));
                VfsChange::ChangeFile { file, text }
            } else {
                let file = self.add_file(root, path.clone(), Arc::clone(&text));
                res = Some(file);
                VfsChange::AddFile {
                    file,
                    text,
                    root,
                    path,
                }
            };
            self.pending_changes.push(change);
        }
        res
    }

    pub fn change_file_overlay(&mut self, path: &Path, new_text: String) {
        if let Some((_root, _path, file)) = self.find_root(path) {
            let file = file.expect("can't change a file which wasn't added");
            let text = Arc::new(new_text);
            self.change_file(file, Arc::clone(&text));
            let change = VfsChange::ChangeFile { file, text };
            self.pending_changes.push(change);
        }
    }

    pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> {
        let mut res = None;
        if let Some((root, path, file)) = self.find_root(path) {
            let file = file.expect("can't remove a file which wasn't added");
            res = Some(file);
            let full_path = path.to_path(&self.roots[root].root);
            let change = if let Ok(text) = fs::read_to_string(&full_path) {
                let text = Arc::new(text);
                self.change_file(file, Arc::clone(&text));
                VfsChange::ChangeFile { file, text }
            } else {
                self.remove_file(file);
                VfsChange::RemoveFile { root, file, path }
            };
            self.pending_changes.push(change);
        }
        res
    }

    pub fn commit_changes(&mut self) -> Vec<VfsChange> {
        mem::replace(&mut self.pending_changes, Vec::new())
    }

    /// Sutdown the VFS and terminate the background watching thread.
    pub fn shutdown(self) -> thread::Result<()> {
        let _ = self.worker.shutdown();
        self.worker_handle.shutdown()
    }

    fn add_file(&mut self, root: VfsRoot, path: RelativePathBuf, text: Arc<String>) -> VfsFile {
        let data = VfsFileData { root, path, text };
        let file = self.files.alloc(data);
        self.root2files.get_mut(&root).unwrap().insert(file);
        file
    }

    fn change_file(&mut self, file: VfsFile, new_text: Arc<String>) {
        self.files[file].text = new_text;
    }

    fn remove_file(&mut self, file: VfsFile) {
        //FIXME: use arena with removal
        self.files[file].text = Default::default();
        self.files[file].path = Default::default();
        let root = self.files[file].root;
        let removed = self.root2files.get_mut(&root).unwrap().remove(&file);
        assert!(removed);
    }

    fn find_root(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf, Option<VfsFile>)> {
        let (root, path) = self
            .roots
            .iter()
            .find_map(|(root, data)| data.can_contain(path).map(|it| (root, it)))?;
        let file = self.root2files[&root]
            .iter()
            .map(|&it| it)
            .find(|&file| self.files[file].path == path);
        Some((root, path, file))
    }
}

#[derive(Debug, Clone)]
pub enum VfsChange {
    AddRoot {
        root: VfsRoot,
        files: Vec<(VfsFile, RelativePathBuf, Arc<String>)>,
    },
    AddFile {
        root: VfsRoot,
        file: VfsFile,
        path: RelativePathBuf,
        text: Arc<String>,
    },
    RemoveFile {
        root: VfsRoot,
        file: VfsFile,
        path: RelativePathBuf,
    },
    ChangeFile {
        file: VfsFile,
        text: Arc<String>,
    },
}
<commit_msg>Remove some unnecessary unwraps by using the `Result::ok` combinatoric<commit_after>//! VFS stands for Virtual File System.
//!
//! When doing analysis, we don't want to do any IO, we want to keep all source
//! code in memory. However, the actual source code is stored on disk, so you
//! need to get it into the memory in the first place somehow. VFS is the
//! component which does this.
//!
//! It is also responsible for watching the disk for changes, and for merging
//! editor state (modified, unsaved files) with disk state.
//! TODO: Some LSP clients support watching the disk, so this crate should
//! to support custom watcher events (related to https://github.com/rust-analyzer/rust-analyzer/issues/131)
//!
//! VFS is based on a concept of roots: a set of directories on the file system
//! whihc are watched for changes. Typically, there will be a root for each
//! Cargo package.
mod arena;
mod io;

use std::{
    fmt,
    mem,
    thread,
    cmp::Reverse,
    path::{Path, PathBuf},
    ffi::OsStr,
    sync::Arc,
    fs,
};

use rustc_hash::{FxHashMap, FxHashSet};
use relative_path::RelativePathBuf;
use crossbeam_channel::Receiver;
use walkdir::DirEntry;
use thread_worker::WorkerHandle;

use crate::{
    arena::{ArenaId, Arena},
};

pub use crate::io::TaskResult as VfsTask;

/// `RootFilter` is a predicate that checks if a file can belong to a root. If
/// several filters match a file (nested dirs), the most nested one wins.
struct RootFilter {
    root: PathBuf,
    file_filter: fn(&Path) -> bool,
}

impl RootFilter {
    fn new(root: PathBuf) -> RootFilter {
        RootFilter {
            root,
            file_filter: has_rs_extension,
        }
    }
    /// Check if this root can contain `path`. NB: even if this returns
    /// true, the `path` might actually be conained in some nested root.
    fn can_contain(&self, path: &Path) -> Option<RelativePathBuf> {
        if !(self.file_filter)(path) {
            return None;
        }
        let path = path.strip_prefix(&self.root).ok()?;
        RelativePathBuf::from_path(path).ok()
    }
}

fn has_rs_extension(p: &Path) -> bool {
    p.extension() == Some(OsStr::new("rs"))
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct VfsRoot(pub u32);

impl ArenaId for VfsRoot {
    fn from_u32(idx: u32) -> VfsRoot {
        VfsRoot(idx)
    }
    fn to_u32(self) -> u32 {
        self.0
    }
}

#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
pub struct VfsFile(pub u32);

impl ArenaId for VfsFile {
    fn from_u32(idx: u32) -> VfsFile {
        VfsFile(idx)
    }
    fn to_u32(self) -> u32 {
        self.0
    }
}

struct VfsFileData {
    root: VfsRoot,
    path: RelativePathBuf,
    text: Arc<String>,
}

pub struct Vfs {
    roots: Arena<VfsRoot, RootFilter>,
    files: Arena<VfsFile, VfsFileData>,
    root2files: FxHashMap<VfsRoot, FxHashSet<VfsFile>>,
    pending_changes: Vec<VfsChange>,
    worker: io::Worker,
    worker_handle: WorkerHandle,
}

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

impl Vfs {
    pub fn new(mut roots: Vec<PathBuf>) -> (Vfs, Vec<VfsRoot>) {
        let (worker, worker_handle) = io::start();

        let mut res = Vfs {
            roots: Arena::default(),
            files: Arena::default(),
            root2files: FxHashMap::default(),
            worker,
            worker_handle,
            pending_changes: Vec::new(),
        };

        // A hack to make nesting work.
        roots.sort_by_key(|it| Reverse(it.as_os_str().len()));
        for (i, path) in roots.iter().enumerate() {
            let root = res.roots.alloc(RootFilter::new(path.clone()));
            res.root2files.insert(root, Default::default());
            let nested = roots[..i]
                .iter()
                .filter(|it| it.starts_with(path))
                .map(|it| it.clone())
                .collect::<Vec<_>>();
            let filter = move |entry: &DirEntry| {
                if entry.file_type().is_file() {
                    has_rs_extension(entry.path())
                } else {
                    nested.iter().all(|it| it != entry.path())
                }
            };
            let task = io::Task {
                root,
                path: path.clone(),
                filter: Box::new(filter),
            };
            res.worker.inp.send(task);
        }
        let roots = res.roots.iter().map(|(id, _)| id).collect();
        (res, roots)
    }

    pub fn root2path(&self, root: VfsRoot) -> PathBuf {
        self.roots[root].root.clone()
    }

    pub fn path2file(&self, path: &Path) -> Option<VfsFile> {
        if let Some((_root, _path, Some(file))) = self.find_root(path) {
            return Some(file);
        }
        None
    }

    pub fn file2path(&self, file: VfsFile) -> PathBuf {
        let rel_path = &self.files[file].path;
        let root_path = &self.roots[self.files[file].root].root;
        rel_path.to_path(root_path)
    }

    pub fn file_for_path(&self, path: &Path) -> Option<VfsFile> {
        if let Some((_root, _path, Some(file))) = self.find_root(path) {
            return Some(file);
        }
        None
    }

    pub fn load(&mut self, path: &Path) -> Option<VfsFile> {
        if let Some((root, rel_path, file)) = self.find_root(path) {
            return if let Some(file) = file {
                Some(file)
            } else {
                let text = fs::read_to_string(path).unwrap_or_default();
                let text = Arc::new(text);
                let file = self.add_file(root, rel_path.clone(), Arc::clone(&text));
                let change = VfsChange::AddFile {
                    file,
                    text,
                    root,
                    path: rel_path,
                };
                self.pending_changes.push(change);
                Some(file)
            };
        }
        None
    }

    pub fn task_receiver(&self) -> &Receiver<io::TaskResult> {
        &self.worker.out
    }

    pub fn handle_task(&mut self, task: io::TaskResult) {
        let mut files = Vec::new();
        // While we were scanning the root in the backgound, a file might have
        // been open in the editor, so we need to account for that.
        let exising = self.root2files[&task.root]
            .iter()
            .map(|&file| (self.files[file].path.clone(), file))
            .collect::<FxHashMap<_, _>>();
        for (path, text) in task.files {
            if let Some(&file) = exising.get(&path) {
                let text = Arc::clone(&self.files[file].text);
                files.push((file, path, text));
                continue;
            }
            let text = Arc::new(text);
            let file = self.add_file(task.root, path.clone(), Arc::clone(&text));
            files.push((file, path, text));
        }

        let change = VfsChange::AddRoot {
            root: task.root,
            files,
        };
        self.pending_changes.push(change);
    }

    pub fn add_file_overlay(&mut self, path: &Path, text: String) -> Option<VfsFile> {
        let mut res = None;
        if let Some((root, path, file)) = self.find_root(path) {
            let text = Arc::new(text);
            let change = if let Some(file) = file {
                res = Some(file);
                self.change_file(file, Arc::clone(&text));
                VfsChange::ChangeFile { file, text }
            } else {
                let file = self.add_file(root, path.clone(), Arc::clone(&text));
                res = Some(file);
                VfsChange::AddFile {
                    file,
                    text,
                    root,
                    path,
                }
            };
            self.pending_changes.push(change);
        }
        res
    }

    pub fn change_file_overlay(&mut self, path: &Path, new_text: String) {
        if let Some((_root, _path, file)) = self.find_root(path) {
            let file = file.expect("can't change a file which wasn't added");
            let text = Arc::new(new_text);
            self.change_file(file, Arc::clone(&text));
            let change = VfsChange::ChangeFile { file, text };
            self.pending_changes.push(change);
        }
    }

    pub fn remove_file_overlay(&mut self, path: &Path) -> Option<VfsFile> {
        let mut res = None;
        if let Some((root, path, file)) = self.find_root(path) {
            let file = file.expect("can't remove a file which wasn't added");
            res = Some(file);
            let full_path = path.to_path(&self.roots[root].root);
            let change = if let Ok(text) = fs::read_to_string(&full_path) {
                let text = Arc::new(text);
                self.change_file(file, Arc::clone(&text));
                VfsChange::ChangeFile { file, text }
            } else {
                self.remove_file(file);
                VfsChange::RemoveFile { root, file, path }
            };
            self.pending_changes.push(change);
        }
        res
    }

    pub fn commit_changes(&mut self) -> Vec<VfsChange> {
        mem::replace(&mut self.pending_changes, Vec::new())
    }

    /// Sutdown the VFS and terminate the background watching thread.
    pub fn shutdown(self) -> thread::Result<()> {
        let _ = self.worker.shutdown();
        self.worker_handle.shutdown()
    }

    fn add_file(&mut self, root: VfsRoot, path: RelativePathBuf, text: Arc<String>) -> VfsFile {
        let data = VfsFileData { root, path, text };
        let file = self.files.alloc(data);
        self.root2files.get_mut(&root).unwrap().insert(file);
        file
    }

    fn change_file(&mut self, file: VfsFile, new_text: Arc<String>) {
        self.files[file].text = new_text;
    }

    fn remove_file(&mut self, file: VfsFile) {
        //FIXME: use arena with removal
        self.files[file].text = Default::default();
        self.files[file].path = Default::default();
        let root = self.files[file].root;
        let removed = self.root2files.get_mut(&root).unwrap().remove(&file);
        assert!(removed);
    }

    fn find_root(&self, path: &Path) -> Option<(VfsRoot, RelativePathBuf, Option<VfsFile>)> {
        let (root, path) = self
            .roots
            .iter()
            .find_map(|(root, data)| data.can_contain(path).map(|it| (root, it)))?;
        let file = self.root2files[&root]
            .iter()
            .map(|&it| it)
            .find(|&file| self.files[file].path == path);
        Some((root, path, file))
    }
}

#[derive(Debug, Clone)]
pub enum VfsChange {
    AddRoot {
        root: VfsRoot,
        files: Vec<(VfsFile, RelativePathBuf, Arc<String>)>,
    },
    AddFile {
        root: VfsRoot,
        file: VfsFile,
        path: RelativePathBuf,
        text: Arc<String>,
    },
    RemoveFile {
        root: VfsRoot,
        file: VfsFile,
        path: RelativePathBuf,
    },
    ChangeFile {
        file: VfsFile,
        text: Arc<String>,
    },
}
<|endoftext|>