<commit_before>/*
 * Copyright (C) 2015-2017 The imgui-rs Developers
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::time::{Duration, Instant};

use glium::glutin;
use glium::{Display, Surface};
use imgui::{ImGui, Ui};
use imgui_glium_renderer::Renderer;

const FRAME_DURATION: Duration = Duration::from_millis(50);

#[derive(Copy, Clone, PartialEq, Debug, Default)]
struct MouseState {
    pos: (i32, i32),
    pressed: (bool, bool, bool),
    wheel: f32,
}

pub fn run<F: FnMut(&Ui, (f32, f32)) -> bool>(
    title: String,
    dimensions: (u32, u32),
    clear_color: [f32; 4],
    mut events_loop: glutin::EventsLoop,
    mut run_ui: F,
) {
    let window = glutin::WindowBuilder::new()
        .with_title(title)
        .with_dimensions(dimensions.0, dimensions.1);
    let context = glutin::ContextBuilder::new().with_vsync(true);
    let display = Display::new(window, context, &events_loop).unwrap();

    let mut imgui = ImGui::init();
    let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");

    imgui.set_ini_filename(None);

    let mut last_frame = Instant::now();
    let mut mouse_state = MouseState::default();

    let mut render = |imgui: &mut ImGui, last_frame: &mut Instant| {
        let now = Instant::now();
        let delta = now - *last_frame;
        let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0;
        *last_frame = now;

        let gl_window = display.gl_window();
        let size_pixels = gl_window.get_inner_size().unwrap();
        let hidpi = gl_window.hidpi_factor();
        let size_points = (
            (size_pixels.0 as f32 / hidpi) as u32,
            (size_pixels.1 as f32 / hidpi) as u32,
        );

        let ui = imgui.frame(size_points, size_pixels, delta_s);
        if !run_ui(&ui, (size_points.0 as f32, size_points.1 as f32)) {
            return false;
        }

        let mut target = display.draw();
        target.clear_color(
            clear_color[0],
            clear_color[1],
            clear_color[2],
            clear_color[3],
        );
        renderer.render(&mut target, ui).expect("Rendering failed");
        target.finish().unwrap();
        true
    };

    // Render one frame before the event loop so the screen isn't empty
    render(&mut imgui, &mut last_frame);

    events_loop.run_forever(|event| {
        use glium::glutin::ElementState::Pressed;
        use glium::glutin::WindowEvent::*;
        use glium::glutin::{ControlFlow, Event, MouseButton, VirtualKeyCode};

        if let Event::Awakened = event {
            // Render twice to immediately show the new AI move
            if !render(&mut imgui, &mut last_frame) {
                return ControlFlow::Break;
            }
            if !render(&mut imgui, &mut last_frame) {
                return ControlFlow::Break;
            }
        } else if let Event::WindowEvent { event, .. } = event {
            match event {
                Closed => return ControlFlow::Break,
                KeyboardInput { input, .. } => {
                    if let Some(VirtualKeyCode::Q) = input.virtual_keycode {
                        if cfg!(target_os = "macos") && input.modifiers.logo {
                            return ControlFlow::Break;
                        }
                    }
                }
                Resized(_, _) | HiDPIFactorChanged(_) => {
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                CursorMoved {
                    position: (x, y), ..
                } => {
                    mouse_state.pos = (x as i32, y as i32);
                    update_mouse(&mut imgui, &mut mouse_state);

                    if Instant::now() - last_frame < FRAME_DURATION {
                        return ControlFlow::Continue;
                    } else if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                MouseInput { state, button, .. } => if MouseButton::Left == button {
                    mouse_state.pressed.0 = state == Pressed;
                    update_mouse(&mut imgui, &mut mouse_state);

                    // Render twice to immediately show the results of the click
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                },
                _ => (),
            }
        }
        ControlFlow::Continue
    });
}

fn update_mouse(imgui: &mut ImGui, mouse_state: &mut MouseState) {
    let scale = imgui.display_framebuffer_scale();
    imgui.set_mouse_pos(
        mouse_state.pos.0 as f32 / scale.0,
        mouse_state.pos.1 as f32 / scale.1,
    );
    imgui.set_mouse_down(&[
        mouse_state.pressed.0,
        mouse_state.pressed.1,
        mouse_state.pressed.2,
        false,
        false,
    ]);
    imgui.set_mouse_wheel(mouse_state.wheel / scale.1);
    mouse_state.wheel = 0.0;
}
<commit_msg>Redraw window on Refresh event<commit_after>/*
 * Copyright (C) 2015-2017 The imgui-rs Developers
 * Copyright (C) 2017-2018 Ryan Huang
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

use std::time::{Duration, Instant};

use glium::glutin;
use glium::{Display, Surface};
use imgui::{ImGui, Ui};
use imgui_glium_renderer::Renderer;

const FRAME_DURATION: Duration = Duration::from_millis(50);

#[derive(Copy, Clone, PartialEq, Debug, Default)]
struct MouseState {
    pos: (i32, i32),
    pressed: (bool, bool, bool),
    wheel: f32,
}

pub fn run<F: FnMut(&Ui, (f32, f32)) -> bool>(
    title: String,
    dimensions: (u32, u32),
    clear_color: [f32; 4],
    mut events_loop: glutin::EventsLoop,
    mut run_ui: F,
) {
    let window = glutin::WindowBuilder::new()
        .with_title(title)
        .with_dimensions(dimensions.0, dimensions.1);
    let context = glutin::ContextBuilder::new().with_vsync(true);
    let display = Display::new(window, context, &events_loop).unwrap();

    let mut imgui = ImGui::init();
    let mut renderer = Renderer::init(&mut imgui, &display).expect("Failed to initialize renderer");

    imgui.set_ini_filename(None);

    let mut last_frame = Instant::now();
    let mut mouse_state = MouseState::default();

    let mut render = |imgui: &mut ImGui, last_frame: &mut Instant| {
        let now = Instant::now();
        let delta = now - *last_frame;
        let delta_s = delta.as_secs() as f32 + delta.subsec_nanos() as f32 / 1_000_000_000.0;
        *last_frame = now;

        let gl_window = display.gl_window();
        let size_pixels = gl_window.get_inner_size().unwrap();
        let hidpi = gl_window.hidpi_factor();
        let size_points = (
            (size_pixels.0 as f32 / hidpi) as u32,
            (size_pixels.1 as f32 / hidpi) as u32,
        );

        let ui = imgui.frame(size_points, size_pixels, delta_s);
        if !run_ui(&ui, (size_points.0 as f32, size_points.1 as f32)) {
            return false;
        }

        let mut target = display.draw();
        target.clear_color(
            clear_color[0],
            clear_color[1],
            clear_color[2],
            clear_color[3],
        );
        renderer.render(&mut target, ui).expect("Rendering failed");
        target.finish().unwrap();
        true
    };

    // Render one frame before the event loop so the screen isn't empty
    render(&mut imgui, &mut last_frame);

    events_loop.run_forever(|event| {
        use glium::glutin::ElementState::Pressed;
        use glium::glutin::WindowEvent::*;
        use glium::glutin::{ControlFlow, Event, MouseButton, VirtualKeyCode};

        if let Event::Awakened = event {
            // Render twice to immediately show the new AI move
            if !render(&mut imgui, &mut last_frame) {
                return ControlFlow::Break;
            }
            if !render(&mut imgui, &mut last_frame) {
                return ControlFlow::Break;
            }
        } else if let Event::WindowEvent { event, .. } = event {
            match event {
                Closed => return ControlFlow::Break,
                KeyboardInput { input, .. } => {
                    if let Some(VirtualKeyCode::Q) = input.virtual_keycode {
                        if cfg!(target_os = "macos") && input.modifiers.logo {
                            return ControlFlow::Break;
                        }
                    }
                }
                Refresh | Resized(_, _) | HiDPIFactorChanged(_) => {
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                CursorMoved {
                    position: (x, y), ..
                } => {
                    mouse_state.pos = (x as i32, y as i32);
                    update_mouse(&mut imgui, &mut mouse_state);

                    if Instant::now() - last_frame < FRAME_DURATION {
                        return ControlFlow::Continue;
                    } else if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                }
                MouseInput { state, button, .. } => if MouseButton::Left == button {
                    mouse_state.pressed.0 = state == Pressed;
                    update_mouse(&mut imgui, &mut mouse_state);

                    // Render twice to immediately show the results of the click
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                    if !render(&mut imgui, &mut last_frame) {
                        return ControlFlow::Break;
                    }
                },
                _ => (),
            }
        }
        ControlFlow::Continue
    });
}

fn update_mouse(imgui: &mut ImGui, mouse_state: &mut MouseState) {
    let scale = imgui.display_framebuffer_scale();
    imgui.set_mouse_pos(
        mouse_state.pos.0 as f32 / scale.0,
        mouse_state.pos.1 as f32 / scale.1,
    );
    imgui.set_mouse_down(&[
        mouse_state.pressed.0,
        mouse_state.pressed.1,
        mouse_state.pressed.2,
        false,
        false,
    ]);
    imgui.set_mouse_wheel(mouse_state.wheel / scale.1);
    mouse_state.wheel = 0.0;
}
<|endoftext|>


<commit_before>#![crate_name = "platform"]
#![crate_type = "rlib"]
#![no_std]
#![feature(lang_items)]

extern crate drivers;
extern crate hil;
extern crate nrf51822;
extern crate support;
extern crate process;
extern crate common;

use drivers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
use hil::gpio::GPIOPin;

pub mod systick;

pub struct Firestorm {
    chip: nrf51822::chip::Nrf51822,
    noop: &'static drivers::noop::Noop,
    gpio: &'static drivers::gpio::GPIO<'static, nrf51822::gpio::GPIOPin>,
    timer: &'static drivers::timer::TimerDriver<'static,
                    VirtualMuxAlarm<'static, nrf51822::timer::TimerAlarm>>,
}

impl Firestorm {
    pub unsafe fn service_pending_interrupts(&mut self) {
        self.chip.service_pending_interrupts()
    }

    pub unsafe fn has_pending_interrupts(&mut self) -> bool {
        self.chip.has_pending_interrupts()
    }

    #[inline(never)]
    pub fn with_driver<F, R>(&mut self, driver_num: usize, f: F) -> R where
            F: FnOnce(Option<&hil::Driver>) -> R {
        match driver_num {
           99 => f(Some(self.noop)),
            1 => f(Some(self.gpio)),
           // 3 => f(Some(self.timer)),
            _ => f(None)
        }
    }
}

macro_rules! static_init {
   ($V:ident : $T:ty = $e:expr) => {
        let $V : &mut $T = {
            // Waiting out for size_of to be available at compile-time to avoid
            // hardcoding an abitrary large size...
            static mut BUF : [u8; 1024] = [0; 1024];
            let mut tmp : &mut $T = mem::transmute(&mut BUF);
            *tmp = $e;
            tmp
        };
   }
}

pub unsafe fn init<'a>() -> &'a mut Firestorm {
    use core::mem;
    use nrf51822::gpio::PORT;

    static mut FIRESTORM_BUF : [u8; 1024] = [0; 1024];

    static_init!(gpio_pins : [&'static nrf51822::gpio::GPIOPin; 10] = [
            &nrf51822::gpio::PORT[18], // LED_0
            &nrf51822::gpio::PORT[19], // LED_1
            &nrf51822::gpio::PORT[0], // Top left header on EK board
            &nrf51822::gpio::PORT[1], //   |
            &nrf51822::gpio::PORT[2], //   V
            &nrf51822::gpio::PORT[3], //
            &nrf51822::gpio::PORT[4], //
            &nrf51822::gpio::PORT[5], //
            &nrf51822::gpio::PORT[6], //
            &nrf51822::gpio::PORT[7], //
            ]);
    static_init!(gpio : drivers::gpio::GPIO<'static, nrf51822::gpio::GPIOPin> =
                 drivers::gpio::GPIO::new(gpio_pins));
    for pin in gpio_pins.iter() {
        pin.set_client(gpio);
    }

//    let rtc = &nrf51822::rtc::RTC;
    let alarm = &nrf51822::timer::ALARM1;
    static_init!(mux_alarm : MuxAlarm<'static, nrf51822::timer::TimerAlarm> =
                    MuxAlarm::new(&nrf51822::timer::ALARM1));
    alarm.set_client(mux_alarm);

    static_init!(virtual_alarm1 : VirtualMuxAlarm<'static, nrf51822::timer::TimerAlarm> =
                    VirtualMuxAlarm::new(mux_alarm));
    static_init!(timer : drivers::timer::TimerDriver<'static,
                                VirtualMuxAlarm<'static, nrf51822::timer::TimerAlarm>> =
                            drivers::timer::TimerDriver::new(virtual_alarm1,
                                                 process::Container::create()));
    virtual_alarm1.set_client(timer);

    static_init!(noop : drivers::noop::Noop = drivers::noop::Noop::new());

    let firestorm : &'static mut Firestorm = mem::transmute(&mut FIRESTORM_BUF);
    *firestorm = Firestorm {
        chip: nrf51822::chip::Nrf51822::new(),
        noop: noop,
        gpio: gpio,
        timer: timer,
    };

    systick::reset();
    systick::enable(true);
    firestorm
}

use core::fmt::Arguments;
#[cfg(not(test))]
#[lang="panic_fmt"]
#[no_mangle]
pub unsafe extern fn rust_begin_unwind(_args: &Arguments,
    _file: &'static str, _line: usize) -> ! {
    use support::nop;
    use hil::gpio::GPIOPin;

    let led0 = &nrf51822::gpio::PORT[18];
    let led1 = &nrf51822::gpio::PORT[19];

    led0.enable_output();
    led1.enable_output();
    loop {
        for _ in 0..100000 {
            led0.set();
            led1.set();
            nop();
        }
        for _ in 0..100000 {
            led0.clear();
            led1.clear();
            nop();
        }
    }
}

<commit_msg>Clean up complex type definitions.<commit_after>#![crate_name = "platform"]
#![crate_type = "rlib"]
#![no_std]
#![feature(lang_items)]

extern crate drivers;
extern crate hil;
extern crate nrf51822;
extern crate support;
extern crate process;
extern crate common;

use drivers::virtual_alarm::{MuxAlarm, VirtualMuxAlarm};
use hil::gpio::GPIOPin;
use drivers::timer::TimerDriver;
use nrf51822::timer::TimerAlarm;
use nrf51822::timer::ALARM1;

pub mod systick;

pub struct Firestorm {
    chip: nrf51822::chip::Nrf51822,
    noop: &'static drivers::noop::Noop,
    gpio: &'static drivers::gpio::GPIO<'static, nrf51822::gpio::GPIOPin>,
    timer: &'static TimerDriver<'static, VirtualMuxAlarm<'static, TimerAlarm>>,
}

impl Firestorm {
    pub unsafe fn service_pending_interrupts(&mut self) {
        self.chip.service_pending_interrupts()
    }

    pub unsafe fn has_pending_interrupts(&mut self) -> bool {
        self.chip.has_pending_interrupts()
    }

    #[inline(never)]
    pub fn with_driver<F, R>(&mut self, driver_num: usize, f: F) -> R where
            F: FnOnce(Option<&hil::Driver>) -> R {
        match driver_num {
           99 => f(Some(self.noop)),
            1 => f(Some(self.gpio)),
           // 3 => f(Some(self.timer)),
            _ => f(None)
        }
    }
}

macro_rules! static_init {
   ($V:ident : $T:ty = $e:expr) => {
        let $V : &mut $T = {
            // Waiting out for size_of to be available at compile-time to avoid
            // hardcoding an abitrary large size...
            static mut BUF : [u8; 1024] = [0; 1024];
            let mut tmp : &mut $T = mem::transmute(&mut BUF);
            *tmp = $e;
            tmp
        };
   }
}

pub unsafe fn init<'a>() -> &'a mut Firestorm {
    use core::mem;
    use nrf51822::gpio::PORT;

    static mut FIRESTORM_BUF : [u8; 1024] = [0; 1024];

    static_init!(gpio_pins : [&'static nrf51822::gpio::GPIOPin; 10] = [
            &nrf51822::gpio::PORT[18], // LED_0
            &nrf51822::gpio::PORT[19], // LED_1
            &nrf51822::gpio::PORT[0], // Top left header on EK board
            &nrf51822::gpio::PORT[1], //   |
            &nrf51822::gpio::PORT[2], //   V
            &nrf51822::gpio::PORT[3], //
            &nrf51822::gpio::PORT[4], //
            &nrf51822::gpio::PORT[5], //
            &nrf51822::gpio::PORT[6], //
            &nrf51822::gpio::PORT[7], //
            ]);
    static_init!(gpio : drivers::gpio::GPIO<'static, nrf51822::gpio::GPIOPin> =
                 drivers::gpio::GPIO::new(gpio_pins));
    for pin in gpio_pins.iter() {
        pin.set_client(gpio);
    }

    let alarm = &nrf51822::timer::ALARM1;
    static_init!(mux_alarm : MuxAlarm<'static, TimerAlarm> = MuxAlarm::new(&ALARM1));
    alarm.set_client(mux_alarm);

    static_init!(virtual_alarm1 : VirtualMuxAlarm<'static, TimerAlarm> =
                                  VirtualMuxAlarm::new(mux_alarm));
    static_init!(timer : TimerDriver<'static, VirtualMuxAlarm<'static, TimerAlarm>> =
                         TimerDriver::new(virtual_alarm1, process::Container::create()));
    virtual_alarm1.set_client(timer);

    static_init!(noop : drivers::noop::Noop = drivers::noop::Noop::new());

    let firestorm : &'static mut Firestorm = mem::transmute(&mut FIRESTORM_BUF);
    *firestorm = Firestorm {
        chip: nrf51822::chip::Nrf51822::new(),
        noop: noop,
        gpio: gpio,
        timer: timer,
    };

    systick::reset();
    systick::enable(true);
    firestorm
}

use core::fmt::Arguments;
#[cfg(not(test))]
#[lang="panic_fmt"]
#[no_mangle]
pub unsafe extern fn rust_begin_unwind(_args: &Arguments,
    _file: &'static str, _line: usize) -> ! {
    use support::nop;
    use hil::gpio::GPIOPin;

    let led0 = &nrf51822::gpio::PORT[18];
    let led1 = &nrf51822::gpio::PORT[19];

    led0.enable_output();
    led1.enable_output();
    loop {
        for _ in 0..100000 {
            led0.set();
            led1.set();
            nop();
        }
        for _ in 0..100000 {
            led0.clear();
            led1.clear();
            nop();
        }
    }
}

<|endoftext|>


<commit_before>// we use the same values as the DIRAC15 code
pub fn get_bragg_angstrom(charge: i32) -> f64 {
    let radius = match charge {
        1 => 0.35,
        2 => 0.35,
        3 => 1.45,
        4 => 1.05,
        5 => 0.85,
        6 => 0.70,
        7 => 0.65,
        8 => 0.60,
        9 => 0.50,
        10 => 0.45,
        11 => 1.80,
        12 => 1.50,
        13 => 1.25,
        14 => 1.10,
        15 => 1.00,
        16 => 1.00,
        17 => 1.00,
        18 => 1.00,
        19 => 2.20,
        20 => 1.80,
        21 => 1.60,
        22 => 1.40,
        23 => 1.35,
        24 => 1.40,
        25 => 1.40,
        26 => 1.40,
        27 => 1.35,
        28 => 1.35,
        29 => 1.35,
        30 => 1.35,
        31 => 1.30,
        32 => 1.25,
        33 => 1.15,
        34 => 1.15,
        35 => 1.15,
        36 => 1.10,
        37 => 2.35,
        38 => 2.00,
        39 => 1.80,
        40 => 1.55,
        41 => 1.45,
        42 => 1.45,
        43 => 1.35,
        44 => 1.30,
        45 => 1.35,
        46 => 1.40,
        47 => 1.60,
        48 => 1.55,
        49 => 1.55,
        50 => 1.45,
        51 => 1.45,
        52 => 1.40,
        53 => 1.40,
        54 => 1.40,
        55 => 2.60,
        56 => 2.15,
        57 => 1.95,
        58 => 1.85,
        59 => 1.85,
        60 => 1.85,
        61 => 1.85,
        62 => 1.85,
        63 => 1.85,
        64 => 1.80,
        65 => 1.75,
        66 => 1.75,
        67 => 1.75,
        68 => 1.75,
        69 => 1.75,
        70 => 1.75,
        71 => 1.75,
        72 => 1.55,
        73 => 1.45,
        74 => 1.35,
        75 => 1.30,
        76 => 1.30,
        77 => 1.35,
        78 => 1.35,
        79 => 1.35,
        80 => 1.50,
        81 => 1.90,
        82 => 1.75,
        83 => 1.60,
        84 => 1.90,
        85 => 1.50,
        86 => 1.50,
        _ => panic!("Bragg radius not found for element"),
    };

    return radius;
}
<commit_msg>better error message<commit_after>// we use the same values as the DIRAC15 code
pub fn get_bragg_angstrom(charge: i32) -> f64 {
    let radius = match charge {
        1 => 0.35,
        2 => 0.35,
        3 => 1.45,
        4 => 1.05,
        5 => 0.85,
        6 => 0.70,
        7 => 0.65,
        8 => 0.60,
        9 => 0.50,
        10 => 0.45,
        11 => 1.80,
        12 => 1.50,
        13 => 1.25,
        14 => 1.10,
        15 => 1.00,
        16 => 1.00,
        17 => 1.00,
        18 => 1.00,
        19 => 2.20,
        20 => 1.80,
        21 => 1.60,
        22 => 1.40,
        23 => 1.35,
        24 => 1.40,
        25 => 1.40,
        26 => 1.40,
        27 => 1.35,
        28 => 1.35,
        29 => 1.35,
        30 => 1.35,
        31 => 1.30,
        32 => 1.25,
        33 => 1.15,
        34 => 1.15,
        35 => 1.15,
        36 => 1.10,
        37 => 2.35,
        38 => 2.00,
        39 => 1.80,
        40 => 1.55,
        41 => 1.45,
        42 => 1.45,
        43 => 1.35,
        44 => 1.30,
        45 => 1.35,
        46 => 1.40,
        47 => 1.60,
        48 => 1.55,
        49 => 1.55,
        50 => 1.45,
        51 => 1.45,
        52 => 1.40,
        53 => 1.40,
        54 => 1.40,
        55 => 2.60,
        56 => 2.15,
        57 => 1.95,
        58 => 1.85,
        59 => 1.85,
        60 => 1.85,
        61 => 1.85,
        62 => 1.85,
        63 => 1.85,
        64 => 1.80,
        65 => 1.75,
        66 => 1.75,
        67 => 1.75,
        68 => 1.75,
        69 => 1.75,
        70 => 1.75,
        71 => 1.75,
        72 => 1.55,
        73 => 1.45,
        74 => 1.35,
        75 => 1.30,
        76 => 1.30,
        77 => 1.35,
        78 => 1.35,
        79 => 1.35,
        80 => 1.50,
        81 => 1.90,
        82 => 1.75,
        83 => 1.60,
        84 => 1.90,
        85 => 1.50,
        86 => 1.50,
        _ => panic!("Bragg radius not found for input charge"),
    };

    return radius;
}
<|endoftext|>


<commit_before>use compression::{NUM_DOCS_PER_BLOCK, BlockDecoder, VIntDecoder};
use DocId;
use postings::{Postings, FreqHandler, DocSet, HasLen};
use std::num::Wrapping;
use fastfield::DeleteBitSet;

const EMPTY_DATA: [u8; 0] = [0u8; 0];

/// `SegmentPostings` represents the inverted list or postings associated to
/// a term in a `Segment`.
///
/// As we iterate through the `SegmentPostings`, the frequencies are optionally decoded.
/// Positions on the other hand, are optionally entirely decoded upfront.
pub struct SegmentPostings<'a> {
    len: usize,
    doc_offset: u32,
    block_decoder: BlockDecoder,
    freq_handler: FreqHandler,
    remaining_data: &'a [u8],
    cur: Wrapping<usize>,
    delete_bitset: DeleteBitSet,
}

impl<'a> SegmentPostings<'a> {
    fn load_next_block(&mut self) {
        let num_remaining_docs = self.len - self.cur.0;
        if num_remaining_docs >= NUM_DOCS_PER_BLOCK {
            self.remaining_data = self.block_decoder
                .uncompress_block_sorted(self.remaining_data, self.doc_offset);
            self.remaining_data = self.freq_handler.read_freq_block(self.remaining_data);
            self.doc_offset = self.block_decoder.output(NUM_DOCS_PER_BLOCK - 1);
        } else {
            self.remaining_data = self.block_decoder
                .uncompress_vint_sorted(self.remaining_data, self.doc_offset, num_remaining_docs);
            self.freq_handler.read_freq_vint(self.remaining_data, num_remaining_docs);
        }
    }

    /// Reads a Segment postings from an &[u8]
    ///
    /// * `len` - number of document in the posting lists.
    /// * `data` - data array. The complete data is not necessarily used.
    /// * `freq_handler` - the freq handler is in charge of decoding
    ///   frequencies and/or positions
    pub fn from_data(len: u32,
                     data: &'a [u8],
                     delete_bitset: &'a DeleteBitSet,
                     freq_handler: FreqHandler) -> SegmentPostings<'a> {
        SegmentPostings {
            len: len as usize,
            doc_offset: 0,
            block_decoder: BlockDecoder::new(),
            freq_handler: freq_handler,
            remaining_data: data,
            cur: Wrapping(usize::max_value()),
            delete_bitset: delete_bitset.clone(),
        }
    }

    /// Returns an empty segment postings object
    pub fn empty() -> SegmentPostings<'static> {
        SegmentPostings {
            len: 0,
            doc_offset: 0,
            block_decoder: BlockDecoder::new(),
            freq_handler: FreqHandler::new_without_freq(),
            remaining_data: &EMPTY_DATA,
            delete_bitset: DeleteBitSet::empty(),
            cur: Wrapping(usize::max_value()),
        }
    }

    /// Index within a block is used as an address when
    /// interacting with the `FreqHandler`
    fn index_within_block(&self) -> usize {
        self.cur.0 % NUM_DOCS_PER_BLOCK
    }
}

impl<'a> DocSet for SegmentPostings<'a> {
    // goes to the next element.
    // next needs to be called a first time to point to the correct element.
    #[inline]
    fn advance(&mut self) -> bool {
        loop {
            self.cur += Wrapping(1);
            if self.cur.0 >= self.len {
                return false;
            }
            if self.index_within_block() == 0 {
                self.load_next_block();
            }
            if !self.delete_bitset.is_deleted(self.doc()) {
                return true;
            }
        }
    }

    #[inline]
    fn doc(&self) -> DocId {
        self.block_decoder.output(self.index_within_block())
    }
}

impl<'a> HasLen for SegmentPostings<'a> {
    fn len(&self) -> usize {
        self.len
    }
}

impl<'a> Postings for SegmentPostings<'a> {
    fn term_freq(&self) -> u32 {
        self.freq_handler.freq(self.index_within_block())
    }

    fn positions(&self) -> &[u32] {
        self.freq_handler.positions(self.index_within_block())
    }
}
<commit_msg>Use binary search for SegmentPostings::skip_next<commit_after>use compression::{NUM_DOCS_PER_BLOCK, BlockDecoder, VIntDecoder};
use DocId;
use postings::{Postings, FreqHandler, DocSet, HasLen, SkipResult};
use std::num::Wrapping;
use fastfield::DeleteBitSet;

const EMPTY_DATA: [u8; 0] = [0u8; 0];

/// `SegmentPostings` represents the inverted list or postings associated to
/// a term in a `Segment`.
///
/// As we iterate through the `SegmentPostings`, the frequencies are optionally decoded.
/// Positions on the other hand, are optionally entirely decoded upfront.
pub struct SegmentPostings<'a> {
    len: usize,
    block_len: usize,
    doc_offset: u32,
    block_decoder: BlockDecoder,
    freq_handler: FreqHandler,
    remaining_data: &'a [u8],
    cur: Wrapping<usize>,
    delete_bitset: DeleteBitSet,
}

impl<'a> SegmentPostings<'a> {
    fn load_next_block(&mut self) {
        let num_remaining_docs = self.len - self.cur.0;
        if num_remaining_docs >= NUM_DOCS_PER_BLOCK {
            self.remaining_data = self.block_decoder
                .uncompress_block_sorted(self.remaining_data, self.doc_offset);
            self.remaining_data = self.freq_handler.read_freq_block(self.remaining_data);
            self.doc_offset = self.block_decoder.output(NUM_DOCS_PER_BLOCK - 1);
            self.block_len = NUM_DOCS_PER_BLOCK;
        } else {
            self.remaining_data = self.block_decoder
                .uncompress_vint_sorted(self.remaining_data, self.doc_offset, num_remaining_docs);
            self.freq_handler.read_freq_vint(self.remaining_data, num_remaining_docs);
            self.block_len = num_remaining_docs;
        }
    }

    /// Reads a Segment postings from an &[u8]
    ///
    /// * `len` - number of document in the posting lists.
    /// * `data` - data array. The complete data is not necessarily used.
    /// * `freq_handler` - the freq handler is in charge of decoding
    ///   frequencies and/or positions
    pub fn from_data(len: u32,
                     data: &'a [u8],
                     delete_bitset: &'a DeleteBitSet,
                     freq_handler: FreqHandler) -> SegmentPostings<'a> {
        SegmentPostings {
            len: len as usize,
            block_len: len as usize,
            doc_offset: 0,
            block_decoder: BlockDecoder::new(),
            freq_handler: freq_handler,
            remaining_data: data,
            cur: Wrapping(usize::max_value()),
            delete_bitset: delete_bitset.clone(),
        }
    }

    /// Returns an empty segment postings object
    pub fn empty() -> SegmentPostings<'static> {
        SegmentPostings {
            len: 0,
            block_len: 0,
            doc_offset: 0,
            block_decoder: BlockDecoder::new(),
            freq_handler: FreqHandler::new_without_freq(),
            remaining_data: &EMPTY_DATA,
            delete_bitset: DeleteBitSet::empty(),
            cur: Wrapping(usize::max_value()),
        }
    }

    /// Index within a block is used as an address when
    /// interacting with the `FreqHandler`
    fn index_within_block(&self) -> usize {
        self.cur.0 % NUM_DOCS_PER_BLOCK
    }

    /// Sets the current position to a location relative
    /// to the current block
    #[inline]
    fn set_within_block(&mut self, inner_pos: usize) {
        self.cur = Wrapping(self.cur.0 & !(NUM_DOCS_PER_BLOCK - 1)) + Wrapping(inner_pos)
    }
}

impl<'a> DocSet for SegmentPostings<'a> {
    // goes to the next element.
    // next needs to be called a first time to point to the correct element.
    #[inline]
    fn advance(&mut self) -> bool {
        loop {
            self.cur += Wrapping(1);
            if self.cur.0 >= self.len {
                return false;
            }
            if self.index_within_block() == 0 {
                self.load_next_block();
            }
            if !self.delete_bitset.is_deleted(self.doc()) {
                return true;
            }
        }
    }

    fn skip_next(&mut self, target: DocId) -> SkipResult {
        if !self.advance() {
            return SkipResult::End;
        }

        let mut pos = self.index_within_block();
        // skip blocks until one that might contain the target
        loop {
            // check if we need to go to the next block
            if target > self.block_decoder.output(self.block_len - 1) {
                self.cur += Wrapping(self.block_len - pos);
                self.load_next_block();
                pos = 0;

                // there was no more data
                if self.cur.0 == self.len {
                    return SkipResult::End;
                }
            } else if target < self.block_decoder.output(pos) {
                // We've overpassed the target after the first `advance` call
                // or we're at the beginning of a block.
                // Either way, we're on the first `DocId` greater than `target`
                return SkipResult::OverStep;
            } else {
                break;
            }
        }

        debug_assert!(target >= self.block_decoder.output(pos));
        debug_assert!(target <= self.block_decoder.output(self.block_len - 1));

        // we're in the right block, do a binary search
        let mut start = pos;
        let mut count = self.block_len - start;
        while count > 0 {
            let step = count / 2;
            let mid = start + step;
            let doc = self.block_decoder.output(mid);
            if doc < target {
                start = mid + 1;
                count -= step + 1;
            } else if doc > target {
                count = step;
            } else {
                self.set_within_block(mid);

                if !self.delete_bitset.is_deleted(doc) {
                    return SkipResult::Reached;
                }

                if self.advance() {
                    return SkipResult::OverStep;
                }
                return SkipResult::End;
            }
        }

        // `doc` is now >= `target`
        let doc = self.block_decoder.output(start);
        self.set_within_block(start);

        if !self.delete_bitset.is_deleted(doc) {
            if doc == target {
                return SkipResult::Reached;
            } else {
                return SkipResult::OverStep;
            }
        }

        if self.advance() {
            return SkipResult::OverStep;
        }
        return SkipResult::End;
    }

    #[inline]
    fn doc(&self) -> DocId {
        self.block_decoder.output(self.index_within_block())
    }
}

impl<'a> HasLen for SegmentPostings<'a> {
    fn len(&self) -> usize {
        self.len
    }
}

impl<'a> Postings for SegmentPostings<'a> {
    fn term_freq(&self) -> u32 {
        self.freq_handler.freq(self.index_within_block())
    }

    fn positions(&self) -> &[u32] {
        self.freq_handler.positions(self.index_within_block())
    }
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use csv;
use curl::http;
use regex::Regex;
use rustc_serialize::json;
use std::collections::HashMap;
use std::fs::File;
use std::process;
use std::result::Result;
use std::str;

use say;
use format::{OutputFormat};
use github::entities::{Issue, Issues, GithubError};

fn ratelimit(headers: &HashMap<String, Vec<String>>) -> String {
    headers.get("x-ratelimit-remaining").unwrap()
           .first().unwrap().to_string()
}

fn get_page(url: String, token: &str) -> http::Response {
    println!("{} {} {}", say::info(), "Fetching", url);

    let auth_header = format!("token {}", token);
    let res = http::handle()
                   .get(url)
                   .header("Authorization", &auth_header)
                   .header("User-Agent", "Github-Issues-CLI")
                   .header("Accept", "application/vnd.github.v3+json")
                   .exec()
                   .unwrap_or_else(|e| process::exit(1));

    if res.get_code() != 200 {
        match str::from_utf8(res.get_body()) {
            Ok(b) => {
                println!("{} {:?}", say::error(), json::decode::<GithubError>(b).ok());
                process::exit(1)
            }
            Err(..) => {
                println!("{} {}", say::error(), "Unable to parse the response from Github");
                process::exit(1)
            }
        }
    }

    res
}

fn next_url(link: String) -> Option<String> {
    let re = Regex::new(r"<([^;]+)>;\s*rel=.next.").unwrap();
    match re.captures(&link) {
        None => None,
        Some(cs) => cs.at(1).as_ref().map(|x| x.to_string())
    }
}

fn as_issues(raw: &[u8]) -> Result<Issues, json::DecoderError> {
    match str::from_utf8(raw) {
        Ok(b) => json::decode(b),
        Err(..) => {
            println!("{} {}", say::error(), "Unable to parse the response from Github");
            process::exit(1)
        }
    }
}

fn parse_repopath(path: String) -> (String, String) {
    let list: Vec<&str> = path.split("/").collect();

    (list[0].to_string(), list[1].to_string())
}

pub fn run(repopath: String,
           oauth_token: String,
           labels: Vec<String>,
           state: String,
           format: OutputFormat,
           output_file: String) {

    // let (owner, repo) = parse_repopath(repopath);
    let labels_pair = if labels.is_empty() { "".to_owned() }
                      else { format!("&labels={}", labels.join(",")) };
    let url = format!("https://api.github.com/repos/{}/issues?filter=all&state={}{}",
                      repopath, state, labels_pair);

    let res = get_page(url, &oauth_token);
    let mut issues = as_issues(res.get_body()).unwrap();

    // A Link header is not present if the requested collection has less than
    // _pagesize_.
    match res.get_headers().get("link") {
        Some(links) => {
            let mut nurl = next_url(links.first().unwrap().clone());

            while let Some(nu) = nurl {
                let r = get_page(nu.to_string(), &oauth_token);
                issues.extend(as_issues(r.get_body()).unwrap());

                let link = r.get_headers().get("link").unwrap()
                                          .first().unwrap()
                                          .clone();
                nurl = next_url(link);
                if nurl.is_none() {
                    println!("{} {} {}", say::warn(), ratelimit(r.get_headers()), "Remaining requests");
                }
            }
        }
        _ => {
            println!("{} {} {}", say::warn(), ratelimit(res.get_headers()), "Remaining requests");
        }
    }

    println!("{} {}", say::highlight("Total issues collected:"), issues.len());

    match format {
        OutputFormat::CSV => write_csv(issues, output_file),
        OutputFormat::JSON => write_json(issues, output_file),
    }
}

fn write_json(issues: Issues, output_file: String) {
    let mut f = File::create(output_file).unwrap();
    let string: String = json::encode(&issues).unwrap();
    f.write_all(string.as_bytes());
}

fn write_csv(issues: Issues, output_file: String) {
    let mut wtr = csv::Writer::from_file(output_file).unwrap();

    let headers = ("number",
                   "title",
                   "state",
                   "created_at",
                   "closed_at",
                   "assignee",
                   "user",
                   "labels",
                   "body");
    wtr.encode(headers);

    for issue in issues {
        let labels = issue.labels.iter()
                                 .map(|x| x.name.clone())
                                 .collect::<Vec<String>>()
                                 .join(",");
        let user = issue.user;
        let assignee = issue.assignee;

        let row = (issue.number,
                   issue.title,
                   issue.state,
                   issue.created_at,
                   issue.closed_at,
                   assignee,
                   user,
                   labels,
                   issue.body);

        wtr.encode(row);
    }
}

// #[cfg(test)]
// mod tests {
//     use super::*;
// }
<commit_msg>Add test to validate ratelimit extraction<commit_after>use std::io::prelude::*;
use csv;
use curl::http;
use regex::Regex;
use rustc_serialize::json;
use std::collections::HashMap;
use std::fs::File;
use std::process;
use std::result::Result;
use std::str;

use say;
use format::{OutputFormat};
use github::entities::{Issue, Issues, GithubError};

fn ratelimit(headers: &HashMap<String, Vec<String>>) -> String {
    headers.get("x-ratelimit-remaining").unwrap()
           .first().unwrap().to_string()
}
#[test]
fn valid_ratelimit() {
    let mut headers: HashMap<String, Vec<String>> = HashMap::new();
    headers.insert("x-ratelimit-remaining".to_owned(), vec!["1".to_owned()]);

    assert_eq!(ratelimit(&headers), "1");
}

fn get_page(url: String, token: &str) -> http::Response {
    println!("{} {} {}", say::info(), "Fetching", url);

    let auth_header = format!("token {}", token);
    let res = http::handle()
                   .get(url)
                   .header("Authorization", &auth_header)
                   .header("User-Agent", "Github-Issues-CLI")
                   .header("Accept", "application/vnd.github.v3+json")
                   .exec()
                   .unwrap_or_else(|e| process::exit(1));

    if res.get_code() != 200 {
        match str::from_utf8(res.get_body()) {
            Ok(b) => {
                println!("{} {:?}", say::error(), json::decode::<GithubError>(b).ok());
                process::exit(1)
            }
            Err(..) => {
                println!("{} {}", say::error(), "Unable to parse the response from Github");
                process::exit(1)
            }
        }
    }

    res
}

fn next_url(link: String) -> Option<String> {
    let re = Regex::new(r"<([^;]+)>;\s*rel=.next.").unwrap();
    match re.captures(&link) {
        None => None,
        Some(cs) => cs.at(1).as_ref().map(|x| x.to_string())
    }
}

fn as_issues(raw: &[u8]) -> Result<Issues, json::DecoderError> {
    match str::from_utf8(raw) {
        Ok(b) => json::decode(b),
        Err(..) => {
            println!("{} {}", say::error(), "Unable to parse the response from Github");
            process::exit(1)
        }
    }
}

fn parse_repopath(path: String) -> (String, String) {
    let list: Vec<&str> = path.split("/").collect();

    (list[0].to_string(), list[1].to_string())
}

pub fn run(repopath: String,
           oauth_token: String,
           labels: Vec<String>,
           state: String,
           format: OutputFormat,
           output_file: String) {

    // let (owner, repo) = parse_repopath(repopath);
    let labels_pair = if labels.is_empty() { "".to_owned() }
                      else { format!("&labels={}", labels.join(",")) };
    let url = format!("https://api.github.com/repos/{}/issues?filter=all&state={}{}",
                      repopath, state, labels_pair);

    let res = get_page(url, &oauth_token);
    let mut issues = as_issues(res.get_body()).unwrap();

    // A Link header is not present if the requested collection has less than
    // _pagesize_.
    match res.get_headers().get("link") {
        Some(links) => {
            let mut nurl = next_url(links.first().unwrap().clone());

            while let Some(nu) = nurl {
                let r = get_page(nu.to_string(), &oauth_token);
                issues.extend(as_issues(r.get_body()).unwrap());

                let link = r.get_headers().get("link").unwrap()
                                          .first().unwrap()
                                          .clone();
                nurl = next_url(link);
                if nurl.is_none() {
                    println!("{} {} {}", say::warn(), ratelimit(r.get_headers()), "Remaining requests");
                }
            }
        }
        _ => {
            println!("{} {} {}", say::warn(), ratelimit(res.get_headers()), "Remaining requests");
        }
    }

    println!("{} {}", say::highlight("Total issues collected:"), issues.len());

    match format {
        OutputFormat::CSV => write_csv(issues, output_file),
        OutputFormat::JSON => write_json(issues, output_file),
    }
}

fn write_json(issues: Issues, output_file: String) {
    let mut f = File::create(output_file).unwrap();
    let string: String = json::encode(&issues).unwrap();
    f.write_all(string.as_bytes());
}

fn write_csv(issues: Issues, output_file: String) {
    let mut wtr = csv::Writer::from_file(output_file).unwrap();

    let headers = ("number",
                   "title",
                   "state",
                   "created_at",
                   "closed_at",
                   "assignee",
                   "user",
                   "labels",
                   "body");
    wtr.encode(headers);

    for issue in issues {
        let labels = issue.labels.iter()
                                 .map(|x| x.name.clone())
                                 .collect::<Vec<String>>()
                                 .join(",");
        let user = issue.user;
        let assignee = issue.assignee;

        let row = (issue.number,
                   issue.title,
                   issue.state,
                   issue.created_at,
                   issue.closed_at,
                   assignee,
                   user,
                   labels,
                   issue.body);

        wtr.encode(row);
    }
}
<|endoftext|>


<commit_before>extern crate robots;

use robots::actors::{Actor,ActorCell};
use std::sync::Mutex;
use std::any::Any;
use std::thread;
use std::time;
use super::super::gpioaccess::{DirectionProxied,PinProxyContract,PwmProxyContract};
use super::super::gpioaccess::proxyimpl::{PinProxy,PwmProxy};

#[derive(Copy, Clone, PartialEq,Debug)]
pub enum PinCommands {
	Blink(u64),
	Switch,
}

pub struct PinActor {
	pinproxy :  Mutex<PinProxy>,
}

impl Actor for PinActor {
	fn receive(&self, _message: Box<Any>, _context: ActorCell) {
		if let Ok(_message) = Box::<Any>::downcast::<PinCommands>(_message){
			match *_message {
				PinCommands::Blink(times) => {
					let pin = self.pinproxy.lock().unwrap();
					match pin.export() {
						Ok(()) => {
							println!("Pin exported");
							for _ in 1..times {
								pin.set_direction(DirectionProxied::Out);
								pin.set_direction(DirectionProxied::High);
								thread::sleep(time::Duration::from_millis(400));
								pin.set_direction(DirectionProxied::Low);
								thread::sleep(time::Duration::from_millis(400));
								println!("Blink");
							}
							match pin.unexport() {
								Ok(()) => {
									println!("Pin unexported");
								}
								_ => {}
							}
						}
						_ => {}
					}
				},
				PinCommands::Switch => { }
			}
		}
	}
}

impl PinActor{
	pub fn new(pin_number: u64) -> PinActor {
		PinActor{ pinproxy : Mutex::new(PinProxy::new(pin_number)) }
	}
}

#[derive(Copy, Clone, PartialEq,Debug)]
pub enum PwmCommands {
	MoveToDegree(u16)
}

pub struct PwmActor {
	pwmproxy :  Result<Mutex<PwmProxy>,()>,
}

impl Actor for PwmActor {
	fn receive(&self, _message: Box<Any>, _context: ActorCell) {
		println!("pwm received message");
		if let Ok(_message) = Box::<Any>::downcast::<PwmCommands>(_message){
			println!("pwm recognized command");
			match *_message {
				PwmCommands::MoveToDegree(degree) => {
					println!("pwm movetodegree");
					match self.pwmproxy {
						Ok(ref pwmproxyresult) =>
						{
							let pwm = pwmproxyresult.lock().unwrap();
							match pwm.export() {
								Ok(()) => {
									println!("Pwm exported");
									match pwm.set_period_ns(20_000)
									{
										Ok(()) => {
											println!("Pwm setting period");
											match pwm.enable(true)
											{
												Ok(()) => {
													println!("Pwm enable");
													for x in 1..10 {
														println!("Pwm {}",x);
														pwm.increase_to_max(0.05);
														pwm.decrease_to_minimum(0.05);
													}
													match pwm.enable(false)
													{
														Ok(()) => {
															println!("Pwm disable");
														}
														_ => println!("Pwm disabled failed")
													}
												}
												_ => println!("Pwm enabled failed")
											}
										}
										_ => println!("Pwm set period failed")
									}
									match pwm.unexport() {
										Ok(()) => {
											println!("Pwm unexported");
										}
										_ => println!("Pwm unexport failed")
									}
								}
								_ => println!("Pwm export failed")
							}
						}
						_ => println!("No Pwm")
					}

					println!("pwm done");
				}
			}
		}
	}
}

impl PwmActor{
	pub fn new(number: u32) -> PwmActor {
		match PwmProxy::new(0,number)
		{
			Ok(pwmproxy) =>  PwmActor{ pwmproxy : Ok(Mutex::new(pwmproxy)) },
			_ => PwmActor{ pwmproxy : Err(()) },
		}
	}
}<commit_msg>period<commit_after>extern crate robots;

use robots::actors::{Actor,ActorCell};
use std::sync::Mutex;
use std::any::Any;
use std::thread;
use std::time;
use super::super::gpioaccess::{DirectionProxied,PinProxyContract,PwmProxyContract};
use super::super::gpioaccess::proxyimpl::{PinProxy,PwmProxy};

#[derive(Copy, Clone, PartialEq,Debug)]
pub enum PinCommands {
	Blink(u64),
	Switch,
}

pub struct PinActor {
	pinproxy :  Mutex<PinProxy>,
}

impl Actor for PinActor {
	fn receive(&self, _message: Box<Any>, _context: ActorCell) {
		if let Ok(_message) = Box::<Any>::downcast::<PinCommands>(_message){
			match *_message {
				PinCommands::Blink(times) => {
					let pin = self.pinproxy.lock().unwrap();
					match pin.export() {
						Ok(()) => {
							println!("Pin exported");
							for _ in 1..times {
								pin.set_direction(DirectionProxied::Out);
								pin.set_direction(DirectionProxied::High);
								thread::sleep(time::Duration::from_millis(400));
								pin.set_direction(DirectionProxied::Low);
								thread::sleep(time::Duration::from_millis(400));
								println!("Blink");
							}
							match pin.unexport() {
								Ok(()) => {
									println!("Pin unexported");
								}
								_ => {}
							}
						}
						_ => {}
					}
				},
				PinCommands::Switch => { }
			}
		}
	}
}

impl PinActor{
	pub fn new(pin_number: u64) -> PinActor {
		PinActor{ pinproxy : Mutex::new(PinProxy::new(pin_number)) }
	}
}

#[derive(Copy, Clone, PartialEq,Debug)]
pub enum PwmCommands {
	MoveToDegree(u16)
}

pub struct PwmActor {
	pwmproxy :  Result<Mutex<PwmProxy>,()>,
}

impl Actor for PwmActor {
	fn receive(&self, _message: Box<Any>, _context: ActorCell) {
		println!("pwm received message");
		if let Ok(_message) = Box::<Any>::downcast::<PwmCommands>(_message){
			println!("pwm recognized command");
			match *_message {
				PwmCommands::MoveToDegree(degree) => {
					println!("pwm movetodegree");
					match self.pwmproxy {
						Ok(ref pwmproxyresult) =>
						{
							let pwm = pwmproxyresult.lock().unwrap();
							match pwm.export() {
								Ok(()) => {
									println!("Pwm exported");
									match pwm.set_period_ns(100_000)
									{
										Ok(()) => {
											println!("Pwm setting period");
											match pwm.enable(true)
											{
												Ok(()) => {
													println!("Pwm enable");
													for x in 1..10 {
														println!("Pwm {}",x);
														pwm.increase_to_max(0.05);
														pwm.decrease_to_minimum(0.05);
													}
													match pwm.enable(false)
													{
														Ok(()) => {
															println!("Pwm disable");
														}
														_ => println!("Pwm disabled failed")
													}
												}
												_ => println!("Pwm enabled failed")
											}
										}
										_ => println!("Pwm set period failed")
									}
									match pwm.unexport() {
										Ok(()) => {
											println!("Pwm unexported");
										}
										_ => println!("Pwm unexport failed")
									}
								}
								_ => println!("Pwm export failed")
							}
						}
						_ => println!("No Pwm")
					}

					println!("pwm done");
				}
			}
		}
	}
}

impl PwmActor{
	pub fn new(number: u32) -> PwmActor {
		match PwmProxy::new(0,number)
		{
			Ok(pwmproxy) =>  PwmActor{ pwmproxy : Ok(Mutex::new(pwmproxy)) },
			_ => PwmActor{ pwmproxy : Err(()) },
		}
	}
}<|endoftext|>


<commit_before>use std::{
    env,
    ffi::OsStr,
    fs::{self, File},
    io::{BufReader, Cursor, Read, Seek},
    path::{Path, PathBuf},
};

use app_dirs::{get_app_root, AppDataType};
use log::debug;
use reqwest::{blocking::Client, Proxy};
use std::time::{Duration, SystemTime};
use walkdir::{DirEntry, WalkDir};
use zip::ZipArchive;

use crate::{
    error::TealdeerError::{self, CacheError, UpdateError},
    types::{PathSource, PlatformType},
};

static CACHE_DIR_ENV_VAR: &str = "TEALDEER_CACHE_DIR";

pub static TLDR_PAGES_DIR: &str = "tldr-pages";
static TLDR_OLD_PAGES_DIR: &str = "tldr-master";

#[derive(Debug)]
pub struct Cache {
    url: String,
    platform: PlatformType,
}

#[derive(Debug)]
pub struct PageLookupResult {
    pub page_path: PathBuf,
    pub patch_path: Option<PathBuf>,
}

impl PageLookupResult {
    pub fn with_page(page_path: PathBuf) -> Self {
        Self {
            page_path,
            patch_path: None,
        }
    }

    pub fn with_optional_patch(mut self, patch_path: Option<PathBuf>) -> Self {
        self.patch_path = patch_path;
        self
    }

    /// Create a buffered reader that sequentially reads from the page and the
    /// patch, as if they were concatenated.
    ///
    /// This will return an error if either the page file or the patch file
    /// cannot be opened.
    pub fn reader(&self) -> Result<BufReader<Box<dyn Read>>, String> {
        // Open page file
        let page_file = File::open(&self.page_path)
            .map_err(|msg| format!("Could not open page file at {:?}: {}", self.page_path, msg))?;

        // Open patch file
        let patch_file_opt = match &self.patch_path {
            Some(path) => Some(
                File::open(path)
                    .map_err(|msg| format!("Could not open patch file at {:?}: {}", path, msg))?,
            ),
            None => None,
        };

        // Create chained reader from file(s)
        //
        // Note: It might be worthwhile to create our own struct that accepts
        // the page and patch files and that will read them sequentially,
        // because it avoids the boxing below. However, the performance impact
        // would first need to be shown to be significant using a benchmark.
        Ok(BufReader::new(if let Some(patch_file) = patch_file_opt {
            Box::new(page_file.chain(patch_file)) as Box<dyn Read>
        } else {
            Box::new(page_file) as Box<dyn Read>
        }))
    }
}

pub enum CacheFreshness {
    /// The cache is still fresh (less than MAX_CACHE_AGE old)
    Fresh,
    /// The cache is stale and should be updated
    Stale(Duration),
    /// The cache is missing
    Missing,
}

impl Cache {
    pub fn new<S>(url: S, platform: PlatformType) -> Self
    where
        S: Into<String>,
    {
        Self {
            url: url.into(),
            platform,
        }
    }

    /// Return the path to the cache directory.
    pub fn get_cache_dir() -> Result<(PathBuf, PathSource), TealdeerError> {
        // Allow overriding the cache directory by setting the env variable.
        if let Ok(value) = env::var(CACHE_DIR_ENV_VAR) {
            let path = PathBuf::from(value);
            let (path_exists, path_is_dir) = path
                .metadata()
                .map_or((false, false), |md| (true, md.is_dir()));
            if path_exists && !path_is_dir {
                return Err(CacheError(format!(
                    "Path specified by ${} is not a directory.",
                    CACHE_DIR_ENV_VAR
                )));
            }
            if !path_exists {
                // Try to create the complete directory path.
                fs::create_dir_all(&path).map_err(|_| {
                    CacheError(format!(
                        "Directory path specified by ${} cannot be created.",
                        CACHE_DIR_ENV_VAR
                    ))
                })?;
                eprintln!(
                    "Successfully created cache directory path `{}`.",
                    path.to_str().unwrap()
                );
            }
            return Ok((path, PathSource::EnvVar));
        };

        // Otherwise, fall back to user cache directory.
        match get_app_root(AppDataType::UserCache, &crate::APP_INFO) {
            Ok(dirs) => Ok((dirs, PathSource::OsConvention)),
            Err(_) => Err(CacheError(
                "Could not determine user cache directory.".into(),
            )),
        }
    }

    /// Download the archive
    fn download(&self) -> Result<Vec<u8>, TealdeerError> {
        let mut builder = Client::builder();
        if let Ok(ref host) = env::var("HTTP_PROXY") {
            if let Ok(proxy) = Proxy::http(host) {
                builder = builder.proxy(proxy);
            }
        }
        if let Ok(ref host) = env::var("HTTPS_PROXY") {
            if let Ok(proxy) = Proxy::https(host) {
                builder = builder.proxy(proxy);
            }
        }
        let client = builder.build().unwrap_or_else(|_| Client::new());
        let mut resp = client.get(&self.url).send()?;
        let mut buf: Vec<u8> = vec![];
        let bytes_downloaded = resp.copy_to(&mut buf)?;
        debug!("{} bytes downloaded", bytes_downloaded);
        Ok(buf)
    }

    /// Decompress and open the archive
    fn decompress<R: Read + Seek>(reader: R) -> ZipArchive<R> {
        ZipArchive::new(reader).unwrap()
    }

    /// Update the pages cache.
    pub fn update(&self) -> Result<(), TealdeerError> {
        // First, download the compressed data
        let bytes: Vec<u8> = self.download()?;

        // Decompress the response body into an `Archive`
        let mut archive = Self::decompress(Cursor::new(bytes));

        // Determine paths
        let (cache_dir, _) = Self::get_cache_dir()?;
        let pages_dir = cache_dir.join(TLDR_PAGES_DIR);

        // Make sure that cache directory exists
        debug!("Ensure cache directory {:?} exists", &cache_dir);
        fs::create_dir_all(&cache_dir)
            .map_err(|e| UpdateError(format!("Could not create cache directory: {}", e)))?;

        // Clear cache directory
        // Note: This is not the best solution. Ideally we would download the
        // archive to a temporary directory and then swap the two directories.
        // But renaming a directory doesn't work across filesystems and Rust
        // does not yet offer a recursive directory copying function. So for
        // now, we'll use this approach.
        Self::clear()?;

        // Extract archive
        archive
            .extract(&pages_dir)
            .map_err(|e| UpdateError(format!("Could not unpack compressed data: {}", e)))?;

        Ok(())
    }

    /// Return the duration since the cache directory was last modified.
    pub fn last_update() -> Option<Duration> {
        if let Ok((cache_dir, _)) = Self::get_cache_dir() {
            if let Ok(metadata) = fs::metadata(cache_dir.join(TLDR_PAGES_DIR)) {
                if let Ok(mtime) = metadata.modified() {
                    let now = SystemTime::now();
                    return now.duration_since(mtime).ok();
                };
            };
        };
        None
    }

    /// Return the freshness of the cache (fresh, stale or missing).
    pub fn freshness() -> CacheFreshness {
        match Cache::last_update() {
            Some(ago) if ago > crate::config::MAX_CACHE_AGE => CacheFreshness::Stale(ago),
            Some(_) => CacheFreshness::Fresh,
            None => CacheFreshness::Missing,
        }
    }

    /// Return the platform directory.
    fn get_platform_dir(&self) -> &'static str {
        match self.platform {
            PlatformType::Linux => "linux",
            PlatformType::OsX => "osx",
            PlatformType::SunOs => "sunos",
            PlatformType::Windows => "windows",
        }
    }

    /// Check for pages for a given platform in one of the given languages.
    fn find_page_for_platform(
        page_name: &str,
        cache_dir: &Path,
        platform: &str,
        language_dirs: &[String],
    ) -> Option<PathBuf> {
        language_dirs
            .iter()
            .map(|lang_dir| cache_dir.join(lang_dir).join(platform).join(page_name))
            .find(|path| path.exists() && path.is_file())
    }

    /// Look up custom patch (<name>.patch). If it exists, store it in a variable.
    fn find_patch(patch_name: &str, custom_pages_dir: Option<&Path>) -> Option<PathBuf> {
        custom_pages_dir
            .map(|custom_dir| custom_dir.join(patch_name))
            .filter(|path| path.exists() && path.is_file())
    }

    /// Search for a page and return the path to it.
    pub fn find_page(
        &self,
        name: &str,
        languages: &[String],
        custom_pages_dir: Option<&Path>,
    ) -> Option<PageLookupResult> {
        let page_filename = format!("{}.md", name);
        let patch_filename = format!("{}.patch", name);
        let custom_filename = format!("{}.page", name);

        // Get cache dir
        let cache_dir = match Self::get_cache_dir() {
            Ok((cache_dir, _)) => cache_dir.join(TLDR_PAGES_DIR),
            Err(e) => {
                log::error!("Could not get cache directory: {}", e);
                return None;
            }
        };

        let lang_dirs: Vec<String> = languages
            .iter()
            .map(|lang| {
                if lang == "en" {
                    String::from("pages")
                } else {
                    format!("pages.{}", lang)
                }
            })
            .collect();

        // Look up custom page (<name>.page). If it exists, return it directly
        if let Some(config_dir) = custom_pages_dir {
            let custom_page = config_dir.join(custom_filename);
            if custom_page.exists() && custom_page.is_file() {
                return Some(PageLookupResult::with_page(custom_page));
            }
        }

        let patch_path = Self::find_patch(&patch_filename, custom_pages_dir);

        // Try to find a platform specific path next, append custom patch to it.
        let platform_dir = self.get_platform_dir();
        if let Some(page) =
            Self::find_page_for_platform(&page_filename, &cache_dir, platform_dir, &lang_dirs)
        {
            return Some(PageLookupResult::with_page(page).with_optional_patch(patch_path));
        }

        // Did not find platform specific results, fall back to "common"
        Self::find_page_for_platform(&page_filename, &cache_dir, "common", &lang_dirs)
            .map(|page| PageLookupResult::with_page(page).with_optional_patch(patch_path))
    }

    /// Return the available pages.
    pub fn list_pages(&self) -> Result<Vec<String>, TealdeerError> {
        // Determine platforms directory and platform
        let (cache_dir, _) = Self::get_cache_dir()?;
        let platforms_dir = cache_dir.join(TLDR_PAGES_DIR).join("pages");
        let platform_dir = self.get_platform_dir();

        // Closure that allows the WalkDir instance to traverse platform
        // specific and common page directories, but not others.
        let should_walk = |entry: &DirEntry| -> bool {
            let file_type = entry.file_type();
            let file_name = match entry.file_name().to_str() {
                Some(name) => name,
                None => return false,
            };
            if file_type.is_dir() {
                return file_name == "common" || file_name == platform_dir;
            } else if file_type.is_file() {
                return true;
            }
            false
        };

        // Recursively walk through common and (if applicable) platform specific directory
        let mut pages = WalkDir::new(platforms_dir)
            .min_depth(1) // Skip root directory
            .into_iter()
            .filter_entry(|e| should_walk(e)) // Filter out pages for other architectures
            .filter_map(Result::ok) // Convert results to options, filter out errors
            .filter_map(|e| {
                let path = e.path();
                let extension = &path.extension().and_then(OsStr::to_str).unwrap_or("");
                if e.file_type().is_file() && extension == &"md" {
                    path.file_stem()
                        .and_then(|stem| stem.to_str().map(Into::into))
                } else {
                    None
                }
            })
            .collect::<Vec<String>>();
        pages.sort();
        pages.dedup();
        Ok(pages)
    }

    /// Delete the cache directory.
    pub fn clear() -> Result<(), TealdeerError> {
        let (path, _) = Self::get_cache_dir()?;
        if path.exists() && path.is_dir() {
            // Delete old tldr-pages cache location as well if present
            // TODO: To be removed in the future
            for pages_dir_name in [TLDR_PAGES_DIR, TLDR_OLD_PAGES_DIR] {
                let pages_dir = path.join(pages_dir_name);

                if pages_dir.exists() {
                    fs::remove_dir_all(&pages_dir).map_err(|e| {
                        CacheError(format!(
                            "Could not remove cache directory ({}): {}",
                            pages_dir.display(),
                            e
                        ))
                    })?;
                }
            }
        } else if path.exists() {
            return Err(CacheError(format!(
                "Cache path ({}) is not a directory.",
                path.display()
            )));
        } else {
            return Err(CacheError(format!(
                "Cache path ({}) does not exist.",
                path.display()
            )));
        };
        Ok(())
    }
}

/// Unit Tests for cache module
#[cfg(test)]
mod tests {
}
<commit_msg>Cache: Add tests for PageLookupResult::reader<commit_after>use std::{
    env,
    ffi::OsStr,
    fs::{self, File},
    io::{BufReader, Cursor, Read, Seek},
    path::{Path, PathBuf},
};

use app_dirs::{get_app_root, AppDataType};
use log::debug;
use reqwest::{blocking::Client, Proxy};
use std::time::{Duration, SystemTime};
use walkdir::{DirEntry, WalkDir};
use zip::ZipArchive;

use crate::{
    error::TealdeerError::{self, CacheError, UpdateError},
    types::{PathSource, PlatformType},
};

static CACHE_DIR_ENV_VAR: &str = "TEALDEER_CACHE_DIR";

pub static TLDR_PAGES_DIR: &str = "tldr-pages";
static TLDR_OLD_PAGES_DIR: &str = "tldr-master";

#[derive(Debug)]
pub struct Cache {
    url: String,
    platform: PlatformType,
}

#[derive(Debug)]
pub struct PageLookupResult {
    pub page_path: PathBuf,
    pub patch_path: Option<PathBuf>,
}

impl PageLookupResult {
    pub fn with_page(page_path: PathBuf) -> Self {
        Self {
            page_path,
            patch_path: None,
        }
    }

    pub fn with_optional_patch(mut self, patch_path: Option<PathBuf>) -> Self {
        self.patch_path = patch_path;
        self
    }

    /// Create a buffered reader that sequentially reads from the page and the
    /// patch, as if they were concatenated.
    ///
    /// This will return an error if either the page file or the patch file
    /// cannot be opened.
    pub fn reader(&self) -> Result<BufReader<Box<dyn Read>>, String> {
        // Open page file
        let page_file = File::open(&self.page_path)
            .map_err(|msg| format!("Could not open page file at {:?}: {}", self.page_path, msg))?;

        // Open patch file
        let patch_file_opt = match &self.patch_path {
            Some(path) => Some(
                File::open(path)
                    .map_err(|msg| format!("Could not open patch file at {:?}: {}", path, msg))?,
            ),
            None => None,
        };

        // Create chained reader from file(s)
        //
        // Note: It might be worthwhile to create our own struct that accepts
        // the page and patch files and that will read them sequentially,
        // because it avoids the boxing below. However, the performance impact
        // would first need to be shown to be significant using a benchmark.
        Ok(BufReader::new(if let Some(patch_file) = patch_file_opt {
            Box::new(page_file.chain(patch_file)) as Box<dyn Read>
        } else {
            Box::new(page_file) as Box<dyn Read>
        }))
    }
}

pub enum CacheFreshness {
    /// The cache is still fresh (less than MAX_CACHE_AGE old)
    Fresh,
    /// The cache is stale and should be updated
    Stale(Duration),
    /// The cache is missing
    Missing,
}

impl Cache {
    pub fn new<S>(url: S, platform: PlatformType) -> Self
    where
        S: Into<String>,
    {
        Self {
            url: url.into(),
            platform,
        }
    }

    /// Return the path to the cache directory.
    pub fn get_cache_dir() -> Result<(PathBuf, PathSource), TealdeerError> {
        // Allow overriding the cache directory by setting the env variable.
        if let Ok(value) = env::var(CACHE_DIR_ENV_VAR) {
            let path = PathBuf::from(value);
            let (path_exists, path_is_dir) = path
                .metadata()
                .map_or((false, false), |md| (true, md.is_dir()));
            if path_exists && !path_is_dir {
                return Err(CacheError(format!(
                    "Path specified by ${} is not a directory.",
                    CACHE_DIR_ENV_VAR
                )));
            }
            if !path_exists {
                // Try to create the complete directory path.
                fs::create_dir_all(&path).map_err(|_| {
                    CacheError(format!(
                        "Directory path specified by ${} cannot be created.",
                        CACHE_DIR_ENV_VAR
                    ))
                })?;
                eprintln!(
                    "Successfully created cache directory path `{}`.",
                    path.to_str().unwrap()
                );
            }
            return Ok((path, PathSource::EnvVar));
        };

        // Otherwise, fall back to user cache directory.
        match get_app_root(AppDataType::UserCache, &crate::APP_INFO) {
            Ok(dirs) => Ok((dirs, PathSource::OsConvention)),
            Err(_) => Err(CacheError(
                "Could not determine user cache directory.".into(),
            )),
        }
    }

    /// Download the archive
    fn download(&self) -> Result<Vec<u8>, TealdeerError> {
        let mut builder = Client::builder();
        if let Ok(ref host) = env::var("HTTP_PROXY") {
            if let Ok(proxy) = Proxy::http(host) {
                builder = builder.proxy(proxy);
            }
        }
        if let Ok(ref host) = env::var("HTTPS_PROXY") {
            if let Ok(proxy) = Proxy::https(host) {
                builder = builder.proxy(proxy);
            }
        }
        let client = builder.build().unwrap_or_else(|_| Client::new());
        let mut resp = client.get(&self.url).send()?;
        let mut buf: Vec<u8> = vec![];
        let bytes_downloaded = resp.copy_to(&mut buf)?;
        debug!("{} bytes downloaded", bytes_downloaded);
        Ok(buf)
    }

    /// Decompress and open the archive
    fn decompress<R: Read + Seek>(reader: R) -> ZipArchive<R> {
        ZipArchive::new(reader).unwrap()
    }

    /// Update the pages cache.
    pub fn update(&self) -> Result<(), TealdeerError> {
        // First, download the compressed data
        let bytes: Vec<u8> = self.download()?;

        // Decompress the response body into an `Archive`
        let mut archive = Self::decompress(Cursor::new(bytes));

        // Determine paths
        let (cache_dir, _) = Self::get_cache_dir()?;
        let pages_dir = cache_dir.join(TLDR_PAGES_DIR);

        // Make sure that cache directory exists
        debug!("Ensure cache directory {:?} exists", &cache_dir);
        fs::create_dir_all(&cache_dir)
            .map_err(|e| UpdateError(format!("Could not create cache directory: {}", e)))?;

        // Clear cache directory
        // Note: This is not the best solution. Ideally we would download the
        // archive to a temporary directory and then swap the two directories.
        // But renaming a directory doesn't work across filesystems and Rust
        // does not yet offer a recursive directory copying function. So for
        // now, we'll use this approach.
        Self::clear()?;

        // Extract archive
        archive
            .extract(&pages_dir)
            .map_err(|e| UpdateError(format!("Could not unpack compressed data: {}", e)))?;

        Ok(())
    }

    /// Return the duration since the cache directory was last modified.
    pub fn last_update() -> Option<Duration> {
        if let Ok((cache_dir, _)) = Self::get_cache_dir() {
            if let Ok(metadata) = fs::metadata(cache_dir.join(TLDR_PAGES_DIR)) {
                if let Ok(mtime) = metadata.modified() {
                    let now = SystemTime::now();
                    return now.duration_since(mtime).ok();
                };
            };
        };
        None
    }

    /// Return the freshness of the cache (fresh, stale or missing).
    pub fn freshness() -> CacheFreshness {
        match Cache::last_update() {
            Some(ago) if ago > crate::config::MAX_CACHE_AGE => CacheFreshness::Stale(ago),
            Some(_) => CacheFreshness::Fresh,
            None => CacheFreshness::Missing,
        }
    }

    /// Return the platform directory.
    fn get_platform_dir(&self) -> &'static str {
        match self.platform {
            PlatformType::Linux => "linux",
            PlatformType::OsX => "osx",
            PlatformType::SunOs => "sunos",
            PlatformType::Windows => "windows",
        }
    }

    /// Check for pages for a given platform in one of the given languages.
    fn find_page_for_platform(
        page_name: &str,
        cache_dir: &Path,
        platform: &str,
        language_dirs: &[String],
    ) -> Option<PathBuf> {
        language_dirs
            .iter()
            .map(|lang_dir| cache_dir.join(lang_dir).join(platform).join(page_name))
            .find(|path| path.exists() && path.is_file())
    }

    /// Look up custom patch (<name>.patch). If it exists, store it in a variable.
    fn find_patch(patch_name: &str, custom_pages_dir: Option<&Path>) -> Option<PathBuf> {
        custom_pages_dir
            .map(|custom_dir| custom_dir.join(patch_name))
            .filter(|path| path.exists() && path.is_file())
    }

    /// Search for a page and return the path to it.
    pub fn find_page(
        &self,
        name: &str,
        languages: &[String],
        custom_pages_dir: Option<&Path>,
    ) -> Option<PageLookupResult> {
        let page_filename = format!("{}.md", name);
        let patch_filename = format!("{}.patch", name);
        let custom_filename = format!("{}.page", name);

        // Get cache dir
        let cache_dir = match Self::get_cache_dir() {
            Ok((cache_dir, _)) => cache_dir.join(TLDR_PAGES_DIR),
            Err(e) => {
                log::error!("Could not get cache directory: {}", e);
                return None;
            }
        };

        let lang_dirs: Vec<String> = languages
            .iter()
            .map(|lang| {
                if lang == "en" {
                    String::from("pages")
                } else {
                    format!("pages.{}", lang)
                }
            })
            .collect();

        // Look up custom page (<name>.page). If it exists, return it directly
        if let Some(config_dir) = custom_pages_dir {
            let custom_page = config_dir.join(custom_filename);
            if custom_page.exists() && custom_page.is_file() {
                return Some(PageLookupResult::with_page(custom_page));
            }
        }

        let patch_path = Self::find_patch(&patch_filename, custom_pages_dir);

        // Try to find a platform specific path next, append custom patch to it.
        let platform_dir = self.get_platform_dir();
        if let Some(page) =
            Self::find_page_for_platform(&page_filename, &cache_dir, platform_dir, &lang_dirs)
        {
            return Some(PageLookupResult::with_page(page).with_optional_patch(patch_path));
        }

        // Did not find platform specific results, fall back to "common"
        Self::find_page_for_platform(&page_filename, &cache_dir, "common", &lang_dirs)
            .map(|page| PageLookupResult::with_page(page).with_optional_patch(patch_path))
    }

    /// Return the available pages.
    pub fn list_pages(&self) -> Result<Vec<String>, TealdeerError> {
        // Determine platforms directory and platform
        let (cache_dir, _) = Self::get_cache_dir()?;
        let platforms_dir = cache_dir.join(TLDR_PAGES_DIR).join("pages");
        let platform_dir = self.get_platform_dir();

        // Closure that allows the WalkDir instance to traverse platform
        // specific and common page directories, but not others.
        let should_walk = |entry: &DirEntry| -> bool {
            let file_type = entry.file_type();
            let file_name = match entry.file_name().to_str() {
                Some(name) => name,
                None => return false,
            };
            if file_type.is_dir() {
                return file_name == "common" || file_name == platform_dir;
            } else if file_type.is_file() {
                return true;
            }
            false
        };

        // Recursively walk through common and (if applicable) platform specific directory
        let mut pages = WalkDir::new(platforms_dir)
            .min_depth(1) // Skip root directory
            .into_iter()
            .filter_entry(|e| should_walk(e)) // Filter out pages for other architectures
            .filter_map(Result::ok) // Convert results to options, filter out errors
            .filter_map(|e| {
                let path = e.path();
                let extension = &path.extension().and_then(OsStr::to_str).unwrap_or("");
                if e.file_type().is_file() && extension == &"md" {
                    path.file_stem()
                        .and_then(|stem| stem.to_str().map(Into::into))
                } else {
                    None
                }
            })
            .collect::<Vec<String>>();
        pages.sort();
        pages.dedup();
        Ok(pages)
    }

    /// Delete the cache directory.
    pub fn clear() -> Result<(), TealdeerError> {
        let (path, _) = Self::get_cache_dir()?;
        if path.exists() && path.is_dir() {
            // Delete old tldr-pages cache location as well if present
            // TODO: To be removed in the future
            for pages_dir_name in [TLDR_PAGES_DIR, TLDR_OLD_PAGES_DIR] {
                let pages_dir = path.join(pages_dir_name);

                if pages_dir.exists() {
                    fs::remove_dir_all(&pages_dir).map_err(|e| {
                        CacheError(format!(
                            "Could not remove cache directory ({}): {}",
                            pages_dir.display(),
                            e
                        ))
                    })?;
                }
            }
        } else if path.exists() {
            return Err(CacheError(format!(
                "Cache path ({}) is not a directory.",
                path.display()
            )));
        } else {
            return Err(CacheError(format!(
                "Cache path ({}) does not exist.",
                path.display()
            )));
        };
        Ok(())
    }
}

/// Unit Tests for cache module
#[cfg(test)]
mod tests {
    use super::*;

    use std::{
        fs::File,
        io::{Read, Write},
    };

    #[test]
    fn test_reader_with_patch() {
        // Write test files
        let dir = tempfile::tempdir().unwrap();
        let page_path = dir.path().join("test.page");
        let patch_path = dir.path().join("test.patch");
        {
            let mut f1 = File::create(&page_path).unwrap();
            f1.write_all(b"Hello\n").unwrap();
            let mut f2 = File::create(&patch_path).unwrap();
            f2.write_all(b"World").unwrap();
        }

        // Create chained reader from lookup result
        let lr = PageLookupResult::with_page(page_path).with_optional_patch(Some(patch_path));
        let mut reader = lr.reader().unwrap();

        // Read into a Vec
        let mut buf = Vec::new();
        reader.read_to_end(&mut buf).unwrap();

        assert_eq!(&buf, b"Hello\nWorld");
    }

    #[test]
    fn test_reader_without_patch() {
        // Write test file
        let dir = tempfile::tempdir().unwrap();
        let page_path = dir.path().join("test.page");
        {
            let mut f = File::create(&page_path).unwrap();
            f.write_all(b"Hello").unwrap();
        }

        // Create chained reader from lookup result
        let lr = PageLookupResult::with_page(page_path);
        let mut reader = lr.reader().unwrap();

        // Read into a Vec
        let mut buf = Vec::new();
        reader.read_to_end(&mut buf).unwrap();

        assert_eq!(&buf, b"Hello");
    }
}
<|endoftext|>


<commit_before>use super::command;

// An item on the chain
pub enum Item<'a> {
    FatalCommand(String),
    NonFatalCommand(String),
    ResultProcessor(&'a Fn(&command::Result) -> command::Result),
    CommandModifier(&'a Fn(&'a command::Result, Option<&'a mut Item>) -> Option<&'a mut Item<'a>>)
}

pub struct CommandChain<'a> {
    pub commands: Vec<Item<'a>>,
    current_result: Option<command::Result>
}

impl<'a> CommandChain<'a> {

    pub fn new() -> CommandChain<'a> {
        CommandChain {
            commands: ::std::vec::Vec::new(),
            current_result: None
        }
    }

    pub fn result_proc(&'a mut self, f: &'a Fn(&command::Result) -> command::Result) -> &'a mut CommandChain {
        self.commands.push(Item::ResultProcessor(f));
        self
    }

    pub fn cmd(&'a mut self, command_string: &str) -> &'a mut CommandChain {
        self.commands.push(Item::FatalCommand(String::from(command_string)));
        self
    }

    pub fn cmd_nonfatal(&'a mut self, command_string: &str) -> &'a mut CommandChain {
        self.commands.push(Item::NonFatalCommand(String::from(command_string)));
        self
    }

    fn run_command(cmd_str : &str) -> command::Result {
        let result = command::run_host_cmd(cmd_str);
        if result.success {
            info!("stdout:{}", result.stdout);
        } else {
            warn!("stderr:{}", result.stderr);
        }
        result
    }

    // TODO: Return an actual Result here
    pub fn execute(&'a mut self) -> Option<command::Result> {
        for item in self.commands.iter() {
            match item {
                &Item::FatalCommand(ref s) => {
                    let result = CommandChain::run_command(s);
                    if !result.success {
                        return None;
                    }
                    self.current_result = Some(result);
                },
                &Item::NonFatalCommand(ref s) => {
                    let result = CommandChain::run_command(s);
                    self.current_result = Some(result);
                },
                &Item::ResultProcessor(f) => {
                    self.current_result = {
                        if let Some(ref mut curr_res) = self.current_result {
                            Some(f(&curr_res))
                        } else {
                            warn!("Executing ResultProcessor with no current result is a no-op!");
                            None
                        }
                    };
                },
                &Item::CommandModifier(f) => {
                }
            }
        }
        if let Some(ref curr_res) = self.current_result {
            return Some(curr_res.clone())
        } else {
            None
        }
    }
}
<commit_msg>added todo to implement me<commit_after>use super::command;

// An item on the chain
pub enum Item<'a> {
    FatalCommand(String),
    NonFatalCommand(String),
    ResultProcessor(&'a Fn(&command::Result) -> command::Result),
    CommandModifier(&'a Fn(&command::Result, Option<&'a mut Item>) -> Option<&'a mut Item<'a>>)
}

pub struct CommandChain<'a> {
    pub commands: Vec<Item<'a>>,
    current_result: Option<command::Result>
}

impl<'a> CommandChain<'a> {

    pub fn new() -> CommandChain<'a> {
        CommandChain {
            commands: ::std::vec::Vec::new(),
            current_result: None
        }
    }

    pub fn result_proc(&'a mut self, f: &'a Fn(&command::Result) -> command::Result) -> &'a mut CommandChain {
        self.commands.push(Item::ResultProcessor(f));
        self
    }

    pub fn cmd(&'a mut self, command_string: &str) -> &'a mut CommandChain {
        self.commands.push(Item::FatalCommand(String::from(command_string)));
        self
    }

    pub fn cmd_nonfatal(&'a mut self, command_string: &str) -> &'a mut CommandChain {
        self.commands.push(Item::NonFatalCommand(String::from(command_string)));
        self
    }

    fn run_command(cmd_str : &str) -> command::Result {
        let result = command::run_host_cmd(cmd_str);
        if result.success {
            info!("stdout:{}", result.stdout);
        } else {
            warn!("stderr:{}", result.stderr);
        }
        result
    }

    pub fn execute(&'a mut self) -> Option<command::Result> {
        for item in self.commands.iter() {
            match item {
                &Item::FatalCommand(ref s) => {
                    let result = CommandChain::run_command(s);
                    if !result.success {
                        return None;
                    }
                    self.current_result = Some(result);
                },
                &Item::NonFatalCommand(ref s) => {
                    let result = CommandChain::run_command(s);
                    self.current_result = Some(result);
                },
                &Item::ResultProcessor(f) => {
                    self.current_result = {
                        if let Some(ref mut curr_res) = self.current_result {
                            Some(f(&curr_res))
                        } else {
                            warn!("Executing ResultProcessor with no current result is a no-op!");
                            None
                        }
                    };
                },
                &Item::CommandModifier(f) => {
                    // TODO: implement me!
                }
            }
        }
        if let Some(ref curr_res) = self.current_result {
            return Some(curr_res.clone())
        } else {
            None
        }
    }
}
<|endoftext|>