<commit_before>use super::camera::*;
use super::event_manager::*;
use GameContext;
use glium::glutin::{CursorState, ElementState, Event, MouseButton, VirtualKeyCode};
use base::math::*;
use base::world::*;
use std::rc::Rc;
use super::world_manager::*;

const GRAVITY: f32 = 9.81;
/// Represents a `Player` in the world, the `Player` can move up, right, down
/// left, right with w, a, s, d, jump with space and speed with shift
pub struct Player {
    cam: Camera,
    context: Rc<GameContext>,
    world_manager: WorldManager,
    acceleration: Vector3f,
    velocity: Vector3f,
    timer_jump: f32,
    timer_vel: f32,
    mouselock: bool,
    shift_speed: f32,
}

impl Player {
    pub fn new(context: Rc<GameContext>, world_manager: WorldManager) -> Self {
        Player {
            cam: Camera {
                position: Point3::new(15.0, 10.0, 50.0),
                phi: -0.27,
                theta: 2.6,
            },
            world_manager: world_manager,
            context: context,
            timer_jump: 1.0,
            timer_vel: 1.0,
            acceleration: Vector3::new(0.0, 0.0, 0.0),
            velocity: Vector3::new(0.0, 0.0, 0.0),
            mouselock: false,
            shift_speed: 1.0,
        }
    }
    /// Gets the actual `Height` of the `HexPillar`
    pub fn get_ground_height(&self) -> Option<f32> {
        let world = self.world_manager.get_world();
        let real_pos = Point2f::new(self.cam.position.x, self.cam.position.y);
        let pillar_index = PillarIndex(AxialPoint::from_real(real_pos));

        world.pillar_at(pillar_index).map(|pillar| pillar.sections()[0].top.to_real())
    }
    /// Getter method for the `Camera`
    pub fn get_camera(&self) -> Camera {
        self.cam
    }
    /// Updates the `Player` after every iteration
    pub fn update(&mut self, delta: f32) {
        let height = self.get_ground_height().unwrap_or(0.0) + 1.70;

        // Moves the Player forward or backward with the acceleration and delta
        // (1.0 - (-((self.timer_vel * delta) / (1.0))).exp()) -> this is a formula
        // that calculates a
        // number from 0 to 1. So the player has a maximum velocity
        if self.acceleration.x != 0.0 {
            self.cam.move_forward(self.acceleration.x * delta * delta * self.shift_speed *
                                  (1.0 - (-((self.timer_vel * delta) / (1.0))).exp()));
            if self.timer_vel != 100.0 {
                self.timer_vel += 1.0;
            }
            if self.acceleration.x == 0.0 {
                self.timer_vel = 1.0;
            }
        }
        // Moves the Player left an right with the acceleration and delta
        if self.acceleration.y != 0.0 {
            self.cam
                .move_right(self.acceleration.y * delta * delta *
                            (1.0 - (-((self.timer_vel * delta) / (1.0))).exp()));
            if self.timer_vel != 100.0 {
                self.timer_vel += 1.0;
            }
            if self.acceleration.y == 0.0 {
                self.timer_vel = 1.0;
            }
        }
        // Let the player jump with the given start-velocity
        if self.velocity.z != 0.0 {
            self.cam.move_up((self.velocity.z * self.timer_jump * delta -
                              self.timer_jump * self.timer_jump * delta * delta * GRAVITY) /
                             16.0);
            self.timer_jump += 1.0;
            if self.cam.position.z < height {
                self.velocity.z = 0.0;
                self.timer_jump = 1.0;
            }
        }
        // Places the `Player` on the actual `HexPillar` if the position of the
        // `Player` is less than
        // the `HexPillar`
        if self.velocity.z == 0.0 && self.cam.position.z < height {
            self.cam.position.z = height;
        }
        // Checks if the `Player` is higher than the actual `Player` on wich he is
        // standing and let
        // him fall on that
        if self.cam.position.z > height && self.velocity.z == 0.0 {
            self.cam
                .move_down((self.timer_jump * self.timer_jump * delta * delta * GRAVITY) / 16.0);
            self.timer_jump += 1.0;
            if self.cam.position.z < height {
                self.timer_jump = 1.0;
            }
        }
    }
}
/// `EventHandler` for the `Player`
impl EventHandler for Player {
    fn handle_event(&mut self, e: &Event) -> EventResponse {
        match *e {
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::W)) => {
                self.acceleration.x = 500.5;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::W)) => {
                self.acceleration.x = 0.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::S)) => {
                self.acceleration.x = -400.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::S)) => {
                self.acceleration.x = 0.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::A)) => {
                self.acceleration.y = -400.5;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::A)) => {
                self.acceleration.y = 0.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::D)) => {
                self.acceleration.y = 400.5;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::D)) => {
                self.acceleration.y = 0.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Space)) => {
                self.velocity.z = 6.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::Space)) => {
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::LControl)) => {
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::LControl)) => {
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::LShift)) => {
                self.shift_speed = 2.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::LShift)) => {
                self.shift_speed = 1.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::F)) => {
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::F)) => {
                EventResponse::Continue
            }
            Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                if !self.mouselock {
                    self.mouselock = true;
                    if let Some(window) = self.context.get_facade().get_window() {
                        window.set_cursor_state(CursorState::Hide)
                            .expect("failed to set cursor state");
                    } else {
                        warn!("Failed to obtain window from facade");
                    }
                } else if self.mouselock {
                    self.mouselock = false;

                    if let Some(window) = self.context.get_facade().get_window() {
                        window.set_cursor_state(CursorState::Normal)
                            .expect("failed to set cursor state");
                    } else {
                        warn!("Failed to obtain window from facade");
                    }
                }

                EventResponse::Continue
            }

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

            _ => EventResponse::NotHandled,
        }
    }
}
<commit_msg>Added crouch-function<commit_after>use super::camera::*;
use super::event_manager::*;
use GameContext;
use glium::glutin::{CursorState, ElementState, Event, MouseButton, VirtualKeyCode};
use base::math::*;
use base::world::*;
use std::rc::Rc;
use super::world_manager::*;

const GRAVITY: f32 = 9.81;
/// Represents a `Player` in the world, the `Player` can move up, right, down
/// left, right with w, a, s, d, jump with space and speed with shift
pub struct Player {
    cam: Camera,
    context: Rc<GameContext>,
    world_manager: WorldManager,
    acceleration: Vector3f,
    velocity: Vector3f,
    timer_jump: f32,
    timer_vel: f32,
    mouselock: bool,
    shift_speed: f32,
}

impl Player {
    pub fn new(context: Rc<GameContext>, world_manager: WorldManager) -> Self {
        Player {
            cam: Camera {
                position: Point3::new(15.0, 10.0, 50.0),
                phi: -0.27,
                theta: 2.6,
            },
            world_manager: world_manager,
            context: context,
            timer_jump: 1.0,
            timer_vel: 1.0,
            acceleration: Vector3::new(0.0, 0.0, 0.0),
            velocity: Vector3::new(0.0, 0.0, 0.0),
            mouselock: false,
            shift_speed: 1.0,
        }
    }
    /// Gets the actual `Height` of the `HexPillar`
    pub fn get_ground_height(&self) -> Option<f32> {
        let world = self.world_manager.get_world();
        let real_pos = Point2f::new(self.cam.position.x, self.cam.position.y);
        let pillar_index = PillarIndex(AxialPoint::from_real(real_pos));

        world.pillar_at(pillar_index).map(|pillar| pillar.sections()[0].top.to_real())
    }
    /// Getter method for the `Camera`
    pub fn get_camera(&self) -> Camera {
        self.cam
    }
    /// Updates the `Player` after every iteration
    pub fn update(&mut self, delta: f32) {
        let height = self.get_ground_height().unwrap_or(0.0) + 1.70;

        // Moves the Player forward or backward with the acceleration and delta
        // (1.0 - (-((self.timer_vel * delta) / (1.0))).exp()) -> this is a formula
        // that calculates a
        // number from 0 to 1. So the player has a maximum velocity
        if self.acceleration.x != 0.0 {
            self.cam.move_forward(self.acceleration.x * delta * delta * self.shift_speed *
                                  (1.0 - (-((self.timer_vel * delta) / (1.0))).exp()));
            if self.timer_vel != 100.0 {
                self.timer_vel += 1.0;
            }
            if self.acceleration.x == 0.0 {
                self.timer_vel = 1.0;
            }
        }

        // Moves the Player left an right with the acceleration and delta
        if self.acceleration.y != 0.0 {
            self.cam
                .move_right(self.acceleration.y * delta * delta *
                            (1.0 - (-((self.timer_vel * delta) / (1.0))).exp()));
            if self.timer_vel != 100.0 {
                self.timer_vel += 1.0;
            }
            if self.acceleration.y == 0.0 {
                self.timer_vel = 1.0;
            }
        }

        if self.shift_speed == 0.5 && self.cam.position.z == height {
            self.cam.position.z = height - 0.5;
        }
        // Let the player jump with the given start-velocity
        if self.velocity.z != 0.0 {
            self.cam.move_up((self.velocity.z * self.timer_jump * delta -
                              self.timer_jump * self.timer_jump * delta * delta * GRAVITY) /
                             16.0);
            self.timer_jump += 1.0;
            if self.cam.position.z < height {
                self.velocity.z = 0.0;
                self.timer_jump = 1.0;
            }
        }
        // Places the `Player` on the actual `HexPillar` if the position of the
        // `Player` is less than
        // the `HexPillar`
        if self.velocity.z == 0.0 && self.cam.position.z < height {
            if self.shift_speed != 0.5 {
                self.cam.position.z = height;
            } else {
                self.cam.position.z = height - 0.5;
            }
        }
        // Checks if the `Player` is higher than the actual `Player` on wich he is
        // standing and let
        // him fall on that
        if self.cam.position.z > height && self.velocity.z == 0.0 && self.shift_speed != 0.5 {
            self.cam
                .move_down((self.timer_jump * self.timer_jump * delta * delta * GRAVITY) / 16.0);
            self.timer_jump += 1.0;
            if self.cam.position.z < height {
                self.timer_jump = 1.0;
            }
        } else if self.shift_speed == 0.5 {
            self.cam
                .move_down((self.timer_jump * self.timer_jump * delta * delta * GRAVITY) / 16.0);
            self.timer_jump += 1.0;
            if self.cam.position.z + 0.5 < height {
                self.timer_jump = 1.0;
            }
        }
    }
}
/// `EventHandler` for the `Player`
impl EventHandler for Player {
    fn handle_event(&mut self, e: &Event) -> EventResponse {
        match *e {
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::W)) => {
                self.acceleration.x = 500.5;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::W)) => {
                self.acceleration.x = 0.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::S)) => {
                self.acceleration.x = -400.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::S)) => {
                self.acceleration.x = 0.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::A)) => {
                self.acceleration.y = -400.5;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::A)) => {
                self.acceleration.y = 0.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::D)) => {
                self.acceleration.y = 400.5;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::D)) => {
                self.acceleration.y = 0.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Space)) => {
                self.velocity.z = 6.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::Space)) => {
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::LControl)) => {
                self.shift_speed = 0.5;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::LControl)) => {
                self.shift_speed = 1.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::LShift)) => {
                self.shift_speed = 2.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::LShift)) => {
                self.shift_speed = 1.0;
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::F)) => {
                EventResponse::Continue
            }
            Event::KeyboardInput(ElementState::Released, _, Some(VirtualKeyCode::F)) => {
                EventResponse::Continue
            }
            Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                if !self.mouselock {
                    self.mouselock = true;
                    if let Some(window) = self.context.get_facade().get_window() {
                        window.set_cursor_state(CursorState::Hide)
                            .expect("failed to set cursor state");
                    } else {
                        warn!("Failed to obtain window from facade");
                    }
                } else if self.mouselock {
                    self.mouselock = false;

                    if let Some(window) = self.context.get_facade().get_window() {
                        window.set_cursor_state(CursorState::Normal)
                            .expect("failed to set cursor state");
                    } else {
                        warn!("Failed to obtain window from facade");
                    }
                }

                EventResponse::Continue
            }

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

            _ => EventResponse::NotHandled,
        }
    }
}
<|endoftext|>


<commit_before>// TODO: This is a placeholder before I start generalizing traits
// from the old code.
use super::apu::Apu;
use super::voice::Voice;
use super::filter::Filter;

const NUM_VOICES: usize = 8;

const COUNTER_RANGE: i32 = 30720;
static COUNTER_RATES: [i32; 32] = [
    COUNTER_RANGE + 1, // Never fires
    2048, 1536, 1280, 1024, 768, 640, 512, 384, 320, 256, 192, 160, 128, 96,
    80, 64, 48, 40, 32, 24, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 1];

static COUNTER_OFFSETS: [i32; 32] = [
    1, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040,
    536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 0, 0];

pub struct Dsp {
    emulator: *mut Apu,

    voices: Vec<Box<Voice>>,

    left_filter: Box<Filter>,
    right_filter: Box<Filter>,

    /*left_output_buffer: i16,
    right_output_buffer: i16,
    output_index: i32,*/

    vol_left: u8,
    vol_right: u8,
    echo_vol_left: u8,
    echo_vol_right: u8,
    noise_clock: u8,
    echo_write_enabled: bool,
    echo_feedback: u8,
    source_dir: u8,
    echo_start_address: u16,
    echo_delay: u8,

    counter: i32,

    cycles_since_last_flush: i32,
    is_flushing: bool,
    noise: i32,
    echo_pos: i32,
    echo_length: i32
}

impl Dsp {
    pub fn new(emulator: *mut Apu) -> Box<Dsp> {
        let mut ret = Box::new(Dsp {
            emulator: emulator,

            voices: Vec::with_capacity(NUM_VOICES),

            left_filter: Box::new(Filter::new()),
            right_filter: Box::new(Filter::new()),

            // TODO

            vol_left: 0,
            vol_right: 0,
            echo_vol_left: 0,
            echo_vol_right: 0,
            noise_clock: 0,
            echo_write_enabled: false,
            echo_feedback: 0,
            source_dir: 0,
            echo_start_address: 0,
            echo_delay: 0,

            counter: 0,

            cycles_since_last_flush: 0,
            is_flushing: false,
            noise: 0,
            echo_pos: 0,
            echo_length: 0,
        });
        let ret_ptr = &mut *ret as *mut _;
        for i in 0..NUM_VOICES {
            ret.voices.push(Box::new(Voice::new(ret_ptr, emulator)));
        }
        ret.reset();
        ret
    }

    #[inline]
    fn emulator(&self) -> &mut Apu {
        unsafe {
            &mut (*self.emulator)
        }
    }

    fn set_filter_coefficient(&mut self, index: i32, value: u8) {
        self.left_filter.coefficients[index as usize] = value;
        self.right_filter.coefficients[index as usize] = value;
    }

    pub fn reset(&mut self) {
        // TODO: NO idea if some of these are correct
        self.vol_left = 0x89;
        self.vol_right = 0x9c;
        self.echo_vol_left = 0x9f;
        self.echo_vol_right = 0x9c;
        self.noise_clock = 0;
        self.echo_write_enabled = false;
        self.echo_feedback = 0;
        self.source_dir = 0;
        self.echo_start_address = 0x60 << 8; // TODO: This shift gets repeated; abstract?
        self.echo_delay = 0x0e;

        self.set_filter_coefficient(0x00, 0x80);
        self.set_filter_coefficient(0x01, 0xff);
        self.set_filter_coefficient(0x02, 0x9a);
        self.set_filter_coefficient(0x03, 0xff);
        self.set_filter_coefficient(0x04, 0x67);
        self.set_filter_coefficient(0x05, 0xff);
        self.set_filter_coefficient(0x06, 0x0f);
        self.set_filter_coefficient(0x07, 0xff);

        self.counter = 0;

        self.cycles_since_last_flush = 0;
        self.is_flushing = false;
        self.noise = 0x4000;
        self.echo_pos = 0;
        self.echo_length = 0;
    }

    pub fn cycles_callback(&mut self, num_cycles: i32) {
        self.cycles_since_last_flush = self.cycles_since_last_flush + num_cycles;
    }

    pub fn flush(&mut self) {
        while self.cycles_since_last_flush > 64 {
            if !self.read_counter(self.noise_clock as i32) {
                let feedback = (self.noise << 13) ^ (self.noise << 14);
                self.noise = (feedback & 0x4000) ^ (self.noise >> 1);
            }

            let mut left_out = 0;
            let mut right_out = 0;
            let mut left_echo_out = 0;
            let mut right_echo_out = 0;
            let mut last_voice_out = 0;
            for j in 0..NUM_VOICES {
                let voice = &mut self.voices[j];

            }
        }
    }

    pub fn read_counter(&self, rate: i32) -> bool {
        ((self.counter + COUNTER_OFFSETS[rate as usize]) % COUNTER_RATES[rate as usize]) != 0
    }

    // TODO: Refactor these methods to reduce code duplication
    pub fn read_source_dir_start_address(&self, index: i32) -> u32 {
        let dir_address = (self.source_dir as i32) * 0x100;
        let entry_address = dir_address + index * 4;
        let mut ret = self.emulator().read_u8(entry_address as u32) as u32;
        ret |= (self.emulator().read_u8((entry_address as u32) + 1) as u32) << 8;
        ret
    }

    pub fn read_source_dir_loop_address(&self, index: i32) -> u32 {
        let dir_address = (self.source_dir as i32) * 0x100;
        let entry_address = dir_address + index * 4;
        let mut ret = self.emulator().read_u8(entry_address as u32 + 2) as u32;
        ret |= (self.emulator().read_u8((entry_address as u32) + 3) as u32) << 8;
        ret
    }
}
<commit_msg>Fixed the stalling problem<commit_after>// TODO: This is a placeholder before I start generalizing traits
// from the old code.
use super::apu::Apu;
use super::voice::Voice;
use super::filter::Filter;

const NUM_VOICES: usize = 8;

const COUNTER_RANGE: i32 = 30720;
static COUNTER_RATES: [i32; 32] = [
    COUNTER_RANGE + 1, // Never fires
    2048, 1536, 1280, 1024, 768, 640, 512, 384, 320, 256, 192, 160, 128, 96,
    80, 64, 48, 40, 32, 24, 20, 16, 12, 10, 8, 6, 5, 4, 3, 2, 1];

static COUNTER_OFFSETS: [i32; 32] = [
    1, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040,
    536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 536, 0, 1040, 0, 0];

pub struct Dsp {
    emulator: *mut Apu,

    voices: Vec<Box<Voice>>,

    left_filter: Box<Filter>,
    right_filter: Box<Filter>,

    /*left_output_buffer: i16,
    right_output_buffer: i16,
    output_index: i32,*/

    vol_left: u8,
    vol_right: u8,
    echo_vol_left: u8,
    echo_vol_right: u8,
    noise_clock: u8,
    echo_write_enabled: bool,
    echo_feedback: u8,
    source_dir: u8,
    echo_start_address: u16,
    echo_delay: u8,

    counter: i32,

    cycles_since_last_flush: i32,
    is_flushing: bool,
    noise: i32,
    echo_pos: i32,
    echo_length: i32
}

impl Dsp {
    pub fn new(emulator: *mut Apu) -> Box<Dsp> {
        let mut ret = Box::new(Dsp {
            emulator: emulator,

            voices: Vec::with_capacity(NUM_VOICES),

            left_filter: Box::new(Filter::new()),
            right_filter: Box::new(Filter::new()),

            // TODO

            vol_left: 0,
            vol_right: 0,
            echo_vol_left: 0,
            echo_vol_right: 0,
            noise_clock: 0,
            echo_write_enabled: false,
            echo_feedback: 0,
            source_dir: 0,
            echo_start_address: 0,
            echo_delay: 0,

            counter: 0,

            cycles_since_last_flush: 0,
            is_flushing: false,
            noise: 0,
            echo_pos: 0,
            echo_length: 0,
        });
        let ret_ptr = &mut *ret as *mut _;
        for i in 0..NUM_VOICES {
            ret.voices.push(Box::new(Voice::new(ret_ptr, emulator)));
        }
        ret.reset();
        ret
    }

    #[inline]
    fn emulator(&self) -> &mut Apu {
        unsafe {
            &mut (*self.emulator)
        }
    }

    fn set_filter_coefficient(&mut self, index: i32, value: u8) {
        self.left_filter.coefficients[index as usize] = value;
        self.right_filter.coefficients[index as usize] = value;
    }

    pub fn reset(&mut self) {
        // TODO: NO idea if some of these are correct
        self.vol_left = 0x89;
        self.vol_right = 0x9c;
        self.echo_vol_left = 0x9f;
        self.echo_vol_right = 0x9c;
        self.noise_clock = 0;
        self.echo_write_enabled = false;
        self.echo_feedback = 0;
        self.source_dir = 0;
        self.echo_start_address = 0x60 << 8; // TODO: This shift gets repeated; abstract?
        self.echo_delay = 0x0e;

        self.set_filter_coefficient(0x00, 0x80);
        self.set_filter_coefficient(0x01, 0xff);
        self.set_filter_coefficient(0x02, 0x9a);
        self.set_filter_coefficient(0x03, 0xff);
        self.set_filter_coefficient(0x04, 0x67);
        self.set_filter_coefficient(0x05, 0xff);
        self.set_filter_coefficient(0x06, 0x0f);
        self.set_filter_coefficient(0x07, 0xff);

        self.counter = 0;

        self.cycles_since_last_flush = 0;
        self.is_flushing = false;
        self.noise = 0x4000;
        self.echo_pos = 0;
        self.echo_length = 0;
    }

    pub fn cycles_callback(&mut self, num_cycles: i32) {
        self.cycles_since_last_flush = self.cycles_since_last_flush + num_cycles;
    }

    pub fn flush(&mut self) {
        while self.cycles_since_last_flush > 64 {
            if !self.read_counter(self.noise_clock as i32) {
                let feedback = (self.noise << 13) ^ (self.noise << 14);
                self.noise = (feedback & 0x4000) ^ (self.noise >> 1);
            }

            let mut left_out = 0;
            let mut right_out = 0;
            let mut left_echo_out = 0;
            let mut right_echo_out = 0;
            let mut last_voice_out = 0;
            for j in 0..NUM_VOICES {
                let voice = &mut self.voices[j];

            }

            // TODO

            self.counter = (self.counter + 1) % COUNTER_RANGE;
            self.cycles_since_last_flush -= 64;
        }
    }

    pub fn read_counter(&self, rate: i32) -> bool {
        ((self.counter + COUNTER_OFFSETS[rate as usize]) % COUNTER_RATES[rate as usize]) != 0
    }

    // TODO: Refactor these methods to reduce code duplication
    pub fn read_source_dir_start_address(&self, index: i32) -> u32 {
        let dir_address = (self.source_dir as i32) * 0x100;
        let entry_address = dir_address + index * 4;
        let mut ret = self.emulator().read_u8(entry_address as u32) as u32;
        ret |= (self.emulator().read_u8((entry_address as u32) + 1) as u32) << 8;
        ret
    }

    pub fn read_source_dir_loop_address(&self, index: i32) -> u32 {
        let dir_address = (self.source_dir as i32) * 0x100;
        let entry_address = dir_address + index * 4;
        let mut ret = self.emulator().read_u8(entry_address as u32 + 2) as u32;
        ret |= (self.emulator().read_u8((entry_address as u32) + 3) as u32) << 8;
        ret
    }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Symbol range
//!
//! It's quite common to want to match ranges of symbols (most particularly, 'all' symbols but also things like all upper-case
//! characters). It would be inefficient to store all of the transitions represented by one of these transitions on a per-symbol
//! basis, and impossible for some symbol sets (consider a state machine working on u32s). Instead, transitions are stored as
//! symbol ranges.
//!

///
/// Trait implemented by symbol types that are countable - ie, for types where there's always a next symbol
///
pub trait Countable<Symbol> {
    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn next(&self) -> Option<Symbol>;

    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn prev(&self) -> Option<Symbol>;
}

///
/// Represents a range of symbols
///
/// Symbols must be ordered in order to use them with a range-based state machine.
///
#[derive(Clone, Eq, PartialEq)]
pub struct SymbolRange<Symbol: Ord+Clone> {
    ///
    /// Lowest symbol in the range
    ///
    pub lowest: Symbol,

    ///
    /// Highest symbol in the range
    ///
    /// This is inclusive, so the highest symbol is always included in the range (this makes it differ from Rust's
    /// built-in Range struct, and is important for supporting uncountable symbols)
    ///
    pub highest: Symbol
}

impl<Symbol: Ord+Clone> SymbolRange<Symbol> {
    ///
    /// Creates a new range covering
    ///
    #[inline]
    pub fn new(lowest: Symbol, highest: Symbol) -> SymbolRange<Symbol> {
        if lowest > highest {
            SymbolRange { lowest: highest, highest: lowest }
        } else {
            SymbolRange { lowest: lowest, highest: highest }
        }
    }
}

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

    #[test]
    fn can_create_range() {
        let range = SymbolRange::new(1, 2);

        assert!(range.lowest == 1);
        assert!(range.highest == 2);
    }

    #[test]
    fn can_create_range_reversed() {
        let range = SymbolRange::new(5, 1);

        assert!(range.lowest == 1);
        assert!(range.highest == 5);
    }
}
<commit_msg>Implement Countable for u8<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Symbol range
//!
//! It's quite common to want to match ranges of symbols (most particularly, 'all' symbols but also things like all upper-case
//! characters). It would be inefficient to store all of the transitions represented by one of these transitions on a per-symbol
//! basis, and impossible for some symbol sets (consider a state machine working on u32s). Instead, transitions are stored as
//! symbol ranges.
//!

///
/// Trait implemented by symbol types that are countable - ie, for types where there's always a next symbol
///
pub trait Countable where Self: Sized {
    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn next(&self) -> Option<Self>;

    ///
    /// Returns the next symbol in order (None if this is the last symbol)
    ///
    fn prev(&self) -> Option<Self>;
}

///
/// Represents a range of symbols
///
/// Symbols must be ordered in order to use them with a range-based state machine.
///
#[derive(Clone, Eq, PartialEq)]
pub struct SymbolRange<Symbol: Ord+Clone> {
    ///
    /// Lowest symbol in the range
    ///
    pub lowest: Symbol,

    ///
    /// Highest symbol in the range
    ///
    /// This is inclusive, so the highest symbol is always included in the range (this makes it differ from Rust's
    /// built-in Range struct, and is important for supporting uncountable symbols)
    ///
    pub highest: Symbol
}

impl<Symbol: Ord+Clone> SymbolRange<Symbol> {
    ///
    /// Creates a new range covering
    ///
    #[inline]
    pub fn new(lowest: Symbol, highest: Symbol) -> SymbolRange<Symbol> {
        if lowest > highest {
            SymbolRange { lowest: highest, highest: lowest }
        } else {
            SymbolRange { lowest: lowest, highest: highest }
        }
    }
}

impl Countable for u8 {
    fn next(&self) -> Option<u8> {
        if *self == u8::max_value() {
            None
        } else {
            Some(self+1)
        }
    }

    fn prev(&self) -> Option<u8> {
        if *self == u8::min_value() {
            None
        } else {
            Some(self-1)
        }
    }
}

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

    #[test]
    fn can_create_range() {
        let range = SymbolRange::new(1, 2);

        assert!(range.lowest == 1);
        assert!(range.highest == 2);
    }

    #[test]
    fn can_create_range_reversed() {
        let range = SymbolRange::new(5, 1);

        assert!(range.lowest == 1);
        assert!(range.highest == 5);
    }
}
<|endoftext|>


<commit_before>//! Resource system.
//!
//! Currently, a resource is a disk-cached object that can be hot-reloaded while you use it.
//! Resource can be serialized and deserialized as you see fit. The concept of *caching* and
//! *loading* are split in different code location so that you can easily compose both – provide
//! the loading code and ask the resource system to cache it for you.
//!
//! This great flexibility is exposed in the public interface so that the cache can be augmented
//! with user-provided objects. You might be interested in implementing `Load`, `CacheKey` – from
//! the [any-cache](https://crates.io/crates/any-cache) crate — as well as providing a type wrapper
//! over the key to access to your resource.
//!
//! # Note on keys
//!
//! If you use the resource system, your resources will be cached and accessible by their key. The
//! key type is not enforced. Resource’s keys are typed to enable namespacing: if you have two
//! resources which ID is `34`, because the key type is different, you can safely cache the resource
//! with the ID `34` without any clashing or undefined behaviors. More in the any-cache crate.

use any_cache::{Cache, HashCache};
pub use any_cache::CacheKey;
use notify::{Op, RawEvent, RecursiveMode, Watcher, raw_watcher};
use notify::op::WRITE;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Loadable object from disk.
///
/// An object can be loaded from disk if given a path it can be output a `LoadResult<_>`. It’s
/// important to note that you’re not supposed to load objects directly from this trait. Instead,
/// you should use a `Store`.
pub trait Load: 'static + Sized {
  // /// Convert from a key to its path representation.
  // fn key_to_path(key: &Self::Key) -> PathBuf;

  /// Load a resource. The `Store` can be used to load or declare additional resource dependencies.
  /// The result type is used to register for dependency events.
  fn load<P>(path: P, cache: &mut Store) -> Result<LoadResult<Self>, LoadError> where P: AsRef<Path>;
}

/// Result of a resource loading. This type enables you to register a resource for reloading events
/// of others (dependencies). If you don’t need to run specific code on a dependency reloading, use
/// the `.into()` function to lift your return value to `LoadResult<_>`.
pub struct LoadResult<T> {
  /// The loaded object.
  res: T,
  /// The list of dependencies to listen for events.
  dependencies: Vec<PathBuf>
}

impl<T> LoadResult<T> {
  pub fn with_dependencies(res: T, dependencies: Vec<PathBuf>) -> Self {
    LoadResult { res, dependencies }
  }
}

impl<T> From<T> for LoadResult<T> {
  fn from(res: T) -> Self {
    LoadResult {
      res,
      dependencies: Vec::new()
    }
  }
}

/// Error that might occur while loading a resource.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  /// The file was not found.
  FileNotFound(PathBuf),
  /// The file wasn’t correctly parsed.
  ParseFailed(String),
  /// The file wasn’t correctly converted, even though it might have been parsed.
  ConversionFailed(String)
}

/// Resources are wrapped in this type.
pub type Res<T> = Rc<RefCell<T>>;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource key. This type is used to adapt a key type’s target so that it can be mutably shared.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct RKey<K>(K);

impl<K, T> CacheKey for RKey<K> where K: CacheKey<Target = T> {
  type Target = Rc<RefCell<T>>;
}

/// Trait used to represente keys in a resource store.
pub trait StoreKey: CacheKey + Clone + Debug {
  /// Convert from a key to its path representation.
  fn key_to_path(&self) -> PathBuf;
}

/// Resource store. Responsible for holding and presenting resources.
pub struct Store {
  // canonicalized root path of resources
  root: PathBuf,
  // resource cache
  cache: HashCache,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // dependencies, mapping a dependency to its observers
  dependencies: HashMap<PathBuf, PathBuf>,
  // vector of pairs (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

impl Store {
  /// Create a new store.
  pub fn new<P>(root: P) -> Result<Self, StoreError> where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let root_ = root.clone();
    let canon_root = root.canonicalize().map_err(|_| StoreError::RootDoesDotExit(root_.into()))?;
    let canon_root_ = canon_root.clone();
    let (wsx, wrx) = channel();
    let mut watcher = raw_watcher(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(canon_root_.clone(), RecursiveMode::Recursive);

      for event in wrx.iter() {
        match event {
          RawEvent { path: Some(ref path), op: Ok(op), .. } if op | WRITE != Op::empty() => {
            dirty_.lock().unwrap().push((path.strip_prefix(&canon_root_).unwrap().to_owned(), Instant::now()));
          },
          _ => ()
        }
      }
    });

    deb!("resource cache started and listens to file changes in {}", root.display());

    Ok(Store {
      root: canon_root,
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dependencies: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    })
  }

  /// Inject a new resource in the cache.
  ///
  /// `key` is used to cache the resource and `path` is the path to where to reload the
  /// resource.
  fn inject<K>(&mut self, key: &K, resource: K::Target, dependencies: Vec<PathBuf>) -> Res<K::Target>
      where K: StoreKey,
            K::Target: Load {
    // wrap the resource to make it shared mutably
    let res = Rc::new(RefCell::new(resource));
    let res_ = res.clone();

    // create the path associated with the given key
    let key_ = key.clone();
    let path = self.root.join(K::key_to_path(&key));
    let path_ = path.clone();

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut Store) -> Result<(), LoadError>> = Box::new(move |cache| {
      deb!("reloading {:?}", key_);

      match K::Target::load(&path_, cache) {
        Ok(load_result) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = load_result.res;
          deb!("reloaded {:?}", key_);
          Ok(())
        },
        Err(e) => {
          warn!("{:?} failed to reload:\n{:#?}", key_, e);
          Err(e)
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now(),
    };

    // cache the resource and its meta data
    self.cache.save(RKey(key.clone()), res.clone());
    self.metadata.insert(path.clone(), metadata);

    deb!("cached resource {:?}", key);

    // register the resource as an observer of its dependencies in the dependencies graph
    for dep_key in dependencies {
      self.dependencies.insert(dep_key, path.clone());
    }

    res
  }

  /// Get a resource from the cache and return an error if loading failed.
  fn get_<K>(&mut self, key: &K) -> Result<Res<K::Target>, LoadError> where K: StoreKey, K::Target: Load {
    let rekey = RKey(key.clone());
    match self.cache.get(&rekey).cloned() {
      Some(resource) => {
        deb!("cache hit for {:?}", key);
        Ok(resource)
      },
      None => {
        deb!("cache miss for {:?}", key);

        // specific loading
        info!("loading {:?}", key);
        let path = self.root.join(K::key_to_path(key));
        let load_result = K::Target::load(&path, self)?;
        Ok(self.inject(key, load_result.res, load_result.dependencies))
      }
    }
  }

  /// Get a resource from the cache for the given key.
  pub fn get<K>(&mut self, key: &K) -> Option<Res<K::Target>> where K: StoreKey, K::Target: Load {
    deb!("getting {:?}", key);

    match self.get_(key) {
      Ok(resource) => Some(resource),
      Err(e) => {
        err!("cannot get {:?} because:\n{:#?}", key, e);
        None
      }
    }
  }

  /// Get a resource from the store for the given key. If it fails, a proxed version is used, which
  /// will get replaced by the resource once it’s available.
  pub fn get_proxied<K, P>(&mut self, key: &K, proxy: P) -> Result<Res<K::Target>, LoadError>
      where K: StoreKey,
            K::Target: Load,
            P: FnOnce() -> K::Target {
    match self.get_(key) {
      Ok(resource) => Ok(resource),
      Err(e) => {
        warn!("proxied {:?} because:\n{:#?}", key, e);

        // FIXME: we set the dependencies to none here, which is silly; find a better design
        Ok(self.inject(key, proxy(), Vec::new()))
      }
    }
  }

  /// Synchronize the cache by updating the resources that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let path = self.root.join(path);
      if let Some(mut metadata) = self.metadata.remove(&path) {
        if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
          if (metadata.on_reload)(self).is_ok() {
            // if we have successfully reloaded the resource, notify the observers that this
            // dependency has changed
            for dep in self.dependencies.get(path.as_path()).cloned() {
              if let Some(obs_metadata) = self.metadata.remove(dep.as_path()) {
                if let Err(e) = (obs_metadata.on_reload)(self) {
                  warn!("cannot reload {:?} {:?}", dep, e);
                }

                self.metadata.insert(dep, obs_metadata);
              }
            }
          }
        }

        metadata.last_update_instant = *instant;
        self.metadata.insert(path.clone(), metadata);
      }
    }

    dirty_.clear();
  }
}

/// Meta data about a resource.
struct ResMetaData {
  on_reload: Box<Fn(&mut Store) -> Result<(), LoadError>>,
  last_update_instant: Instant,
}

/// Error that might happen when creating a resource cache.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StoreError {
  /// The root path for the resources was not found.
  RootDoesDotExit(PathBuf)
}
<commit_msg>Remove some useless commented code.<commit_after>//! Resource system.
//!
//! Currently, a resource is a disk-cached object that can be hot-reloaded while you use it.
//! Resource can be serialized and deserialized as you see fit. The concept of *caching* and
//! *loading* are split in different code location so that you can easily compose both – provide
//! the loading code and ask the resource system to cache it for you.
//!
//! This great flexibility is exposed in the public interface so that the cache can be augmented
//! with user-provided objects. You might be interested in implementing `Load`, `CacheKey` – from
//! the [any-cache](https://crates.io/crates/any-cache) crate — as well as providing a type wrapper
//! over the key to access to your resource.
//!
//! # Note on keys
//!
//! If you use the resource system, your resources will be cached and accessible by their key. The
//! key type is not enforced. Resource’s keys are typed to enable namespacing: if you have two
//! resources which ID is `34`, because the key type is different, you can safely cache the resource
//! with the ID `34` without any clashing or undefined behaviors. More in the any-cache crate.

use any_cache::{Cache, HashCache};
pub use any_cache::CacheKey;
use notify::{Op, RawEvent, RecursiveMode, Watcher, raw_watcher};
use notify::op::WRITE;
use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt::Debug;
use std::path::{Path, PathBuf};
use std::rc::Rc;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::channel;
use std::thread;
use std::time::{Duration, Instant};

/// Loadable object from disk.
///
/// An object can be loaded from disk if given a path it can be output a `LoadResult<_>`. It’s
/// important to note that you’re not supposed to load objects directly from this trait. Instead,
/// you should use a `Store`.
pub trait Load: 'static + Sized {
  /// Load a resource. The `Store` can be used to load or declare additional resource dependencies.
  /// The result type is used to register for dependency events.
  fn load<P>(path: P, cache: &mut Store) -> Result<LoadResult<Self>, LoadError> where P: AsRef<Path>;
}

/// Result of a resource loading. This type enables you to register a resource for reloading events
/// of others (dependencies). If you don’t need to run specific code on a dependency reloading, use
/// the `.into()` function to lift your return value to `LoadResult<_>`.
pub struct LoadResult<T> {
  /// The loaded object.
  res: T,
  /// The list of dependencies to listen for events.
  dependencies: Vec<PathBuf>
}

impl<T> LoadResult<T> {
  pub fn with_dependencies(res: T, dependencies: Vec<PathBuf>) -> Self {
    LoadResult { res, dependencies }
  }
}

impl<T> From<T> for LoadResult<T> {
  fn from(res: T) -> Self {
    LoadResult {
      res,
      dependencies: Vec::new()
    }
  }
}

/// Error that might occur while loading a resource.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LoadError {
  /// The file was not found.
  FileNotFound(PathBuf),
  /// The file wasn’t correctly parsed.
  ParseFailed(String),
  /// The file wasn’t correctly converted, even though it might have been parsed.
  ConversionFailed(String)
}

/// Resources are wrapped in this type.
pub type Res<T> = Rc<RefCell<T>>;

/// Time to await after a resource update to establish that it should be reloaded.
const UPDATE_AWAIT_TIME_MS: u64 = 1000;

/// Resource key. This type is used to adapt a key type’s target so that it can be mutably shared.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
struct RKey<K>(K);

impl<K, T> CacheKey for RKey<K> where K: CacheKey<Target = T> {
  type Target = Rc<RefCell<T>>;
}

/// Trait used to represente keys in a resource store.
pub trait StoreKey: CacheKey + Clone + Debug {
  /// Convert from a key to its path representation.
  fn key_to_path(&self) -> PathBuf;
}

/// Resource store. Responsible for holding and presenting resources.
pub struct Store {
  // canonicalized root path of resources
  root: PathBuf,
  // resource cache
  cache: HashCache,
  // contains all metadata on resources
  metadata: HashMap<PathBuf, ResMetaData>,
  // dependencies, mapping a dependency to its observers
  dependencies: HashMap<PathBuf, PathBuf>,
  // vector of pairs (path, timestamp) giving indication on resources to reload
  dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>>,
  #[allow(dead_code)]
  watcher_thread: thread::JoinHandle<()>
}

impl Store {
  /// Create a new store.
  pub fn new<P>(root: P) -> Result<Self, StoreError> where P: AsRef<Path> {
    let dirty: Arc<Mutex<Vec<(PathBuf, Instant)>>> = Arc::new(Mutex::new(Vec::new()));
    let dirty_ = dirty.clone();

    let root = root.as_ref().to_owned();
    let root_ = root.clone();
    let canon_root = root.canonicalize().map_err(|_| StoreError::RootDoesDotExit(root_.into()))?;
    let canon_root_ = canon_root.clone();
    let (wsx, wrx) = channel();
    let mut watcher = raw_watcher(wsx).unwrap();

    let join_handle = thread::spawn(move || {
      let _ = watcher.watch(canon_root_.clone(), RecursiveMode::Recursive);

      for event in wrx.iter() {
        match event {
          RawEvent { path: Some(ref path), op: Ok(op), .. } if op | WRITE != Op::empty() => {
            dirty_.lock().unwrap().push((path.strip_prefix(&canon_root_).unwrap().to_owned(), Instant::now()));
          },
          _ => ()
        }
      }
    });

    deb!("resource cache started and listens to file changes in {}", root.display());

    Ok(Store {
      root: canon_root,
      cache: HashCache::new(),
      metadata: HashMap::new(),
      dependencies: HashMap::new(),
      dirty: dirty,
      watcher_thread: join_handle
    })
  }

  /// Inject a new resource in the cache.
  ///
  /// `key` is used to cache the resource and `path` is the path to where to reload the
  /// resource.
  fn inject<K>(&mut self, key: &K, resource: K::Target, dependencies: Vec<PathBuf>) -> Res<K::Target>
      where K: StoreKey,
            K::Target: Load {
    // wrap the resource to make it shared mutably
    let res = Rc::new(RefCell::new(resource));
    let res_ = res.clone();

    // create the path associated with the given key
    let key_ = key.clone();
    let path = self.root.join(K::key_to_path(&key));
    let path_ = path.clone();

    // closure used to reload the object when needed
    let on_reload: Box<for<'a> Fn(&'a mut Store) -> Result<(), LoadError>> = Box::new(move |cache| {
      deb!("reloading {:?}", key_);

      match K::Target::load(&path_, cache) {
        Ok(load_result) => {
          // replace the current resource with the freshly loaded one
          *res_.borrow_mut() = load_result.res;
          deb!("reloaded {:?}", key_);
          Ok(())
        },
        Err(e) => {
          warn!("{:?} failed to reload:\n{:#?}", key_, e);
          Err(e)
        }
      }
    });

    let metadata = ResMetaData {
      on_reload: on_reload,
      last_update_instant: Instant::now(),
    };

    // cache the resource and its meta data
    self.cache.save(RKey(key.clone()), res.clone());
    self.metadata.insert(path.clone(), metadata);

    deb!("cached resource {:?}", key);

    // register the resource as an observer of its dependencies in the dependencies graph
    for dep_key in dependencies {
      self.dependencies.insert(dep_key, path.clone());
    }

    res
  }

  /// Get a resource from the cache and return an error if loading failed.
  fn get_<K>(&mut self, key: &K) -> Result<Res<K::Target>, LoadError> where K: StoreKey, K::Target: Load {
    let rekey = RKey(key.clone());
    match self.cache.get(&rekey).cloned() {
      Some(resource) => {
        deb!("cache hit for {:?}", key);
        Ok(resource)
      },
      None => {
        deb!("cache miss for {:?}", key);

        // specific loading
        info!("loading {:?}", key);
        let path = self.root.join(K::key_to_path(key));
        let load_result = K::Target::load(&path, self)?;
        Ok(self.inject(key, load_result.res, load_result.dependencies))
      }
    }
  }

  /// Get a resource from the cache for the given key.
  pub fn get<K>(&mut self, key: &K) -> Option<Res<K::Target>> where K: StoreKey, K::Target: Load {
    deb!("getting {:?}", key);

    match self.get_(key) {
      Ok(resource) => Some(resource),
      Err(e) => {
        err!("cannot get {:?} because:\n{:#?}", key, e);
        None
      }
    }
  }

  /// Get a resource from the store for the given key. If it fails, a proxed version is used, which
  /// will get replaced by the resource once it’s available.
  pub fn get_proxied<K, P>(&mut self, key: &K, proxy: P) -> Result<Res<K::Target>, LoadError>
      where K: StoreKey,
            K::Target: Load,
            P: FnOnce() -> K::Target {
    match self.get_(key) {
      Ok(resource) => Ok(resource),
      Err(e) => {
        warn!("proxied {:?} because:\n{:#?}", key, e);

        // FIXME: we set the dependencies to none here, which is silly; find a better design
        Ok(self.inject(key, proxy(), Vec::new()))
      }
    }
  }

  /// Synchronize the cache by updating the resources that ought to.
  pub fn sync(&mut self) {
    let dirty = self.dirty.clone();
    let mut dirty_ = dirty.lock().unwrap();

    for &(ref path, ref instant) in dirty_.iter() {
      let path = self.root.join(path);
      if let Some(mut metadata) = self.metadata.remove(&path) {
        if instant.duration_since(metadata.last_update_instant) >= Duration::from_millis(UPDATE_AWAIT_TIME_MS) {
          if (metadata.on_reload)(self).is_ok() {
            // if we have successfully reloaded the resource, notify the observers that this
            // dependency has changed
            for dep in self.dependencies.get(path.as_path()).cloned() {
              if let Some(obs_metadata) = self.metadata.remove(dep.as_path()) {
                if let Err(e) = (obs_metadata.on_reload)(self) {
                  warn!("cannot reload {:?} {:?}", dep, e);
                }

                self.metadata.insert(dep, obs_metadata);
              }
            }
          }
        }

        metadata.last_update_instant = *instant;
        self.metadata.insert(path.clone(), metadata);
      }
    }

    dirty_.clear();
  }
}

/// Meta data about a resource.
struct ResMetaData {
  on_reload: Box<Fn(&mut Store) -> Result<(), LoadError>>,
  last_update_instant: Instant,
}

/// Error that might happen when creating a resource cache.
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum StoreError {
  /// The root path for the resources was not found.
  RootDoesDotExit(PathBuf)
}
<|endoftext|>


<commit_before>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives like
//! `// ignore-tidy-linelength`.

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

const COLS: usize = 100;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns `true` if `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns `true` if `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

fn contains_ignore_directive(contents: &String, check: &str) -> bool {
    contents.contains(&format!("// ignore-tidy-{}", check)) ||
        contents.contains(&format!("# ignore-tidy-{}", check))
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let skip_cr = contains_ignore_directive(&contents, "cr");
        let skip_tab = contains_ignore_directive(&contents, "tab");
        let skip_length = contains_ignore_directive(&contents, "linelength");
        let skip_end_whitespace = contains_ignore_directive(&contents, "end-whitespace");
        let skip_copyright = contains_ignore_directive(&contents, "copyright");
        let mut leading_new_lines = false;
        let mut trailing_new_lines = 0;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if !skip_length && line.chars().count() > COLS
                && !long_line_is_ok(line) {
                    err(&format!("line longer than {} chars", COLS));
            }
            if !skip_tab && line.contains('\t') {
                err("tab character");
            }
            if !skip_end_whitespace && (line.ends_with(' ') || line.ends_with('\t')) {
                err("trailing whitespace");
            }
            if !skip_cr && line.contains('\r') {
                err("CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if !skip_copyright && (line.starts_with("// Copyright") ||
                                   line.starts_with("# Copyright") ||
                                   line.starts_with("Copyright"))
                               && (line.contains("Rust Developers") ||
                                   line.contains("Rust Project Developers")) {
                err("copyright notices attributed to the Rust Project Developers are deprecated");
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                if i == 0 {
                    leading_new_lines = true;
                }
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
        }
        if leading_new_lines {
            tidy_error!(bad, "{}: leading newline", file.display());
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
    })
}
<commit_msg>Tidy warn on ignored line length when lines are not too long<commit_after>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives like
//! `// ignore-tidy-copyright`.

use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

const COLS: usize = 100;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns `true` if `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns `true` if `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

fn contains_ignore_directive(contents: &String, check: &str) -> bool {
    contents.contains(&format!("// ignore-tidy-{}", check)) ||
        contents.contains(&format!("# ignore-tidy-{}", check))
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let skip_cr = contains_ignore_directive(&contents, "cr");
        let skip_tab = contains_ignore_directive(&contents, "tab");
        let skip_length = contains_ignore_directive(&contents, "linelength");
        let skip_end_whitespace = contains_ignore_directive(&contents, "end-whitespace");
        let skip_copyright = contains_ignore_directive(&contents, "copyright");
        let mut leading_new_lines = false;
        let mut trailing_new_lines = 0;
        let mut contained_long_line = false;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if line.chars().count() > COLS && !long_line_is_ok(line) {
                contained_long_line = true;
                if !skip_length {
                    err(&format!("line longer than {} chars", COLS));
                }
            }
            if !skip_tab && line.contains('\t') {
                err("tab character");
            }
            if !skip_end_whitespace && (line.ends_with(' ') || line.ends_with('\t')) {
                err("trailing whitespace");
            }
            if !skip_cr && line.contains('\r') {
                err("CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if !skip_copyright && (line.starts_with("// Copyright") ||
                                   line.starts_with("# Copyright") ||
                                   line.starts_with("Copyright"))
                               && (line.contains("Rust Developers") ||
                                   line.contains("Rust Project Developers")) {
                err("copyright notices attributed to the Rust Project Developers are deprecated");
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                if i == 0 {
                    leading_new_lines = true;
                }
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
        }
        if leading_new_lines {
            tidy_error!(bad, "{}: leading newline", file.display());
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
        if !contained_long_line && skip_length {
            tidy_error!(bad, "{}: ignoring line length unnecessarily", file.display());
        }
    })
}
<|endoftext|>


<commit_before>
use super::*;

//

tests_impls!
{

  fn pair() -> Result< () >
  {
    use proc_macro_tools::syn::parse::Parser;

    // test.case( "basic" );
    let code = qt!( x core::option::Option< i32 > );
    let got = syn::parse2::< TheModule::Pair< syn::Ident, syn::Type > >( code )?;
    let exp = TheModule::Pair::< syn::Ident, syn::Type >::new
    (
      syn::Ident::new( "x", proc_macro2::Span::call_site() ),
      syn::parse2::< syn::Type >( qt!( core::option::Option< i32 > ) )?,
    );
    a_id!( got, exp );

    // test.case( "pair of many" );
    let code = qt!
    {
      #[ derive( Copy ) ]
      x1
      // #[ derive( Clone ) ]
      // x2
    };
    let got = syn::parse2::< TheModule::Pair< TheModule::Many< TheModule::AttributesOuter >, syn::Ident > >( code )?;
    let exp = TheModule::Pair::< TheModule::Many< TheModule::AttributesOuter >, syn::Ident >
    (
      TheModule::Many( vec![ TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Copy ) ] ) )? ) ] ),
      syn::Ident::new( "x1", proc_macro2::Span::call_site() ),
    );
    a_id!( got, exp );

    // test.case( "punctuated of pairs" );
    let code = qt!
    {
      #[ derive( Copy ) ]
      x1,
      #[ derive( Clone ) ]
      x2
    };
    type PunctuatedPairs = syn::punctuated::Punctuated
    <
      TheModule::Pair
      <
        TheModule::Many< TheModule::AttributesOuter >,
        syn::Ident,
      >,
      syn::token::Comma
    >;

    let got = PunctuatedPairs::parse_terminated.parse2( code )?;
    let mut exp = PunctuatedPairs::new();
    exp.push( TheModule::Pair::new
    (
      TheModule::Many( vec![ TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Copy ) ] ) )? ) ] ),
      syn::Ident::new( "x1", proc_macro2::Span::call_site() ),
    ));
    exp.push( TheModule::Pair::new
    (
      TheModule::Many( vec![ TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Clone ) ] ) )? ) ] ),
      syn::Ident::new( "x2", proc_macro2::Span::call_site() ),
    ));
    a_id!( got, exp );

    //

    Ok( () )
  }

  //

  fn many() -> Result< () >
  {
    use proc_macro_tools::syn::parse::Parser;

    // test.case( "AttributesOuter" );
    let code = qt!
    {
      #[ derive( Copy ) ]
      #[ derive( Clone ) ]
      #[ derive( Debug ) ]
    };
    let got = syn::parse2::< TheModule::Many< TheModule::AttributesOuter > >( code ).unwrap();
    let exp = TheModule::Many::< TheModule::AttributesOuter >::new_with( vec!
    [
      TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Copy ) ] ) )? ),
      TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Clone ) ] ) )? ),
      TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Debug ) ] ) )? ),
    ]);
    a_id!( got, exp );

    // test.case( "AttributesInner" );
    let code = qt!
    {
      #![ warn( missing_docs ) ]
      #![ warn( something ) ]
    };
    let got = syn::parse2::< TheModule::Many< TheModule::AttributesInner > >( code ).unwrap();
    let exp = TheModule::Many::< TheModule::AttributesInner >::new_with( vec!
    [
      TheModule::AttributesInner::from( syn::Attribute::parse_inner.parse2( qt!( #![ warn( missing_docs ) ] ) )? ),
      TheModule::AttributesInner::from( syn::Attribute::parse_inner.parse2( qt!( #![ warn( something ) ] ) )? ),
    ]);
    a_id!( got, exp );

    // test.case( "Item" );
    let code = qt!
    {
      fn f1(){}
      fn f2(){}
    };
    let got = syn::parse2::< TheModule::Many< TheModule::syn::Item > >( code ).unwrap();
    let exp = TheModule::Many::< TheModule::syn::Item >::new_with( vec!
    [
      syn::parse2::< syn::Item >( qt!( fn f1(){} ) )?,
      syn::parse2::< syn::Item >( qt!( fn f2(){} ) )?,
    ]);
    a_id!( got, exp );

    //

    Ok( () )
  }

}

//

tests_index!
{
  pair,
  many,
}

<commit_msg>!test<commit_after>
use super::*;

//

tests_impls!
{

  fn pair() -> Result< () >
  {
    use proc_macro_tools::syn::parse::Parser;

    // test.case( "basic" );
    let code = qt!( x core::option::Option< i32 > );
    let got = syn::parse2::< TheModule::Pair< syn::Ident, syn::Type > >( code )?;
    let exp = TheModule::Pair::< syn::Ident, syn::Type >::new
    (
      syn::Ident::new( "x", proc_macro2::Span::call_site() ),
      syn::parse2::< syn::Type >( qt!( core::option::Option< i32 > ) )?,
    );
    a_id!( got, exp );

    // test.case( "pair of many" );
    let code = qt!
    {
      #[ derive( Copy ) ]
      x1
      // #[ derive( Clone ) ]
      // x2
    };
    let got = syn::parse2::< TheModule::Pair< TheModule::Many< TheModule::AttributesOuter >, syn::Ident > >( code )?;
    let exp = TheModule::Pair::< TheModule::Many< TheModule::AttributesOuter >, syn::Ident >
    (
      TheModule::Many( vec![ TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Copy ) ] ) )? ) ] ),
      syn::Ident::new( "x1", proc_macro2::Span::call_site() ),
    );
    a_id!( got, exp );

    // test.case( "punctuated of pairs" );
    let code = qt!
    {
      #[ derive( Copy ) ]
      x1,
      #[ derive( Clone ) ]
      x2
    };
    type PunctuatedPairs = syn::punctuated::Punctuated
    <
      TheModule::Pair
      <
        TheModule::Many< TheModule::AttributesOuter >,
        syn::Ident,
      >,
      syn::token::Comma
    >;

    let got = PunctuatedPairs::parse_terminated.parse2( code )?;
    let mut exp = PunctuatedPairs::new();
    exp.push( TheModule::Pair::new
    (
      TheModule::Many( vec![ TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Copy ) ] ) )? ) ] ),
      syn::Ident::new( "x1", proc_macro2::Span::call_site() ),
    ));
    exp.push( TheModule::Pair::new
    (
      TheModule::Many( vec![ TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!( #[ derive( Clone ) ] ) )? ) ] ),
      syn::Ident::new( "x2", proc_macro2::Span::call_site() ),
    ));
    a_id!( got, exp );

    //

    Ok( () )
  }

  //

  fn many() -> Result< () >
  {
    use proc_macro_tools::syn::parse::Parser;

    // test.case( "AttributesOuter" );
    let code = qt!
    {
      #[ derive( Copy ) ]
      #[ derive( Clone ) ]
      #[ derive( Debug ) ]
    };
    let got = syn::parse2::< TheModule::Many< TheModule::AttributesOuter > >( code ).unwrap();
    let exp = TheModule::Many::< TheModule::AttributesOuter >::new_with( vec!
    [
      TheModule::AttributesOuter::from( syn::Attribute::parse_outer.parse2( qt!
      {
        #[ derive( Copy ) ]
        #[ derive( Clone ) ]
        #[ derive( Debug ) ]
      } )? ),
    ]);
    a_id!( got, exp );

    // test.case( "AttributesInner" );
    let code = qt!
    {
      #![ warn( missing_docs ) ]
      #![ warn( something ) ]
    };
    let got = syn::parse2::< TheModule::Many< TheModule::AttributesInner > >( code ).unwrap();
    let exp = TheModule::Many::< TheModule::AttributesInner >::new_with( vec!
    [
      TheModule::AttributesInner::from( syn::Attribute::parse_inner.parse2( qt!( #![ warn( missing_docs ) ] ) )? ),
      TheModule::AttributesInner::from( syn::Attribute::parse_inner.parse2( qt!( #![ warn( something ) ] ) )? ),
    ]);
    a_id!( got, exp );

    // test.case( "Item" );
    let code = qt!
    {
      fn f1(){}
      fn f2(){}
    };
    let got = syn::parse2::< TheModule::Many< TheModule::syn::Item > >( code ).unwrap();
    let exp = TheModule::Many::< TheModule::syn::Item >::new_with( vec!
    [
      syn::parse2::< syn::Item >( qt!( fn f1(){} ) )?,
      syn::parse2::< syn::Item >( qt!( fn f2(){} ) )?,
    ]);
    a_id!( got, exp );

    //

    Ok( () )
  }

}

//

tests_index!
{
  pair,
  many,
}

<|endoftext|>


<commit_before>extern crate serde;

use super::content::*;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone)]
pub struct Attachment {
    title: Text,
    file: File,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct Problem {
    title: Text,
    statement: File,
    attachments: Vec<Attachment>,
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        println!(
            "{}",
            serde_json::to_string_pretty(&Problem {
                title: vec![TextVariant {
                    attributes: vec!["language:en-US".to_owned()],
                    value: "Title".to_owned(),
                }],
                statement: vec![FileVariant {
                    attributes: vec!["language:en-US".to_owned()],
                    name: Some(FileName("english.pdf".to_owned())),
                    r#type: Some(MediaType("application/pdf".to_owned())),
                    content: vec![],
                }],
                attachments: vec![Attachment {
                    title: vec![TextVariant {
                        attributes: vec!["language:en-US".to_owned()],
                        value: "Skeleton".to_owned(),
                    }],
                    file: vec![FileVariant {
                        attributes: vec![],
                        name: Some(FileName("skeleton.cpp".to_owned())),
                        r#type: None,
                        content: vec![],
                    }],
                }],
            })
            .unwrap()
        );
    }
}
<commit_msg>problem: make struct fileds public<commit_after>extern crate serde;

use super::content::*;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone)]
pub struct Attachment {
    pub title: Text,
    pub file: File,
}

#[derive(Serialize, Deserialize, Clone)]
pub struct Problem {
    pub title: Text,
    pub statement: File,
    pub attachments: Vec<Attachment>,
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        println!(
            "{}",
            serde_json::to_string_pretty(&Problem {
                title: vec![TextVariant {
                    attributes: vec!["language:en-US".to_owned()],
                    value: "Title".to_owned(),
                }],
                statement: vec![FileVariant {
                    attributes: vec!["language:en-US".to_owned()],
                    name: Some(FileName("english.pdf".to_owned())),
                    r#type: Some(MediaType("application/pdf".to_owned())),
                    content: vec![],
                }],
                attachments: vec![Attachment {
                    title: vec![TextVariant {
                        attributes: vec!["language:en-US".to_owned()],
                        value: "Skeleton".to_owned(),
                    }],
                    file: vec![FileVariant {
                        attributes: vec![],
                        name: Some(FileName("skeleton.cpp".to_owned())),
                        r#type: None,
                        content: vec![],
                    }],
                }],
            })
            .unwrap()
        );
    }
}
<|endoftext|>