<commit_before>use bullet::{BulletDrawable, bullets::Bullets};
use cgmath;
use cgmath::{Deg, Point2};
use character::{CharacterDrawable, controls::CharacterInputState};
use critter::CritterData;
use data;
use game::constants::{ASPECT_RATIO, NORMAL_DEATH_SPRITE_OFFSET, SPRITE_OFFSET, ZOMBIE_SHEET_TOTAL_WIDTH, ZOMBIE_STILL_SPRITE_OFFSET};
use game::get_random_bool;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use graphics::{camera::CameraInputState, can_move_to_tile, Dimensions, direction, direction_movement, get_orientation, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::{Fetch, ReadStorage, WriteStorage};
use zombie::zombies::Zombies;

pub mod zombies;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Debug, Clone)]
pub struct ZombieDrawable {
  projection: Projection,
  pub position: Position,
  previous_position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
  pub movement_direction: Point2<f32>,
  zombie_idx: usize,
  zombie_death_idx: usize,
  is_colliding: bool,
}

impl ZombieDrawable {
  pub fn new(position: Position) -> ZombieDrawable {
    let view = Dimensions::get_view_matrix();
    ZombieDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(Deg(60.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position,
      previous_position: Position::new([0.0, 0.0]),
      orientation: Orientation::Left,
      stance: Stance::Still,
      direction: Orientation::Left,
      movement_direction: Point2::new(0.0, 0.0),
      zombie_idx: 0,
      zombie_death_idx: 0,
      is_colliding: false
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, character: &CharacterDrawable) {
    self.projection = *world_to_clip;

    let offset_delta =
      Position::new([
        ci.x_movement - self.previous_position.position[0],
        ci.y_movement - self.previous_position.position[1]
      ]);

    self.previous_position = Position::new([
      ci.x_movement,
      ci.y_movement
    ]);
    let movement_speed = 1.0;

    if self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
      let dir = direction(
        Point2::new(self.position.position[0], self.position.position[1]),
        Point2::new(character.position.position[0], character.position.position[1])
      );

      self.direction = get_orientation(dir);

      self.movement_direction = direction_movement(dir);
      self.stance = Stance::Walking;
    } else {
      self.movement_direction = Point2::new(0.0, 0.0);
    }

    let new_pos = Position::new([
      self.position.position[0] + offset_delta.position[0] + (self.movement_direction.x * movement_speed),
      self.position.position[1] + offset_delta.position[1] + (self.movement_direction.y * movement_speed)
    ]);

    let new_pos_without_movement = Position::new([
      self.position.position[0] + offset_delta.position[0],
      self.position.position[1] + offset_delta.position[1]
    ]);

    let tile_pos = Position::new([ci.x_movement - new_pos.position[0], ci.y_movement - new_pos.position[1]]);

    if can_move_to_tile(tile_pos) {
      self.position = new_pos;
    } else {
      self.position = new_pos_without_movement;
    }
  }

  pub fn check_bullet_hits(&mut self, bullets: &[BulletDrawable]) {
    bullets.iter().for_each(|bullet| {
      if overlaps(self.position, bullet.position, 80.0, 80.0) && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
        self.stance =
          if get_random_bool() {
            Stance::NormalDeath
          } else {
            Stance::CriticalDeath
          };
      }
    });
  }

  pub fn update_alive_idx(&mut self, max_idx: usize) {
    if self.zombie_idx < max_idx {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_death_idx(&mut self, max_idx: usize) {
    if self.zombie_death_idx < max_idx {
      self.zombie_death_idx += 1;
    }
  }
}

pub struct ZombieDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> ZombieDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> ZombieDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let zombie_bytes = include_bytes!("../../assets/zombie.png");

    let vertex_data: [VertexData; 4] = [
      VertexData::new([-25.0, -35.0], [0.0, 1.0]),
      VertexData::new([25.0, -35.0], [1.0, 1.0]),
      VertexData::new([25.0, 35.0], [1.0, 0.0]),
      VertexData::new([-25.0, 35.0], [0.0, 0.0]),
    ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data[..], &index_data[..]);

    let char_texture = load_texture(factory, zombie_bytes).unwrap();
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_zombie();

    ZombieDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, drawable: &mut ZombieDrawable) -> CharacterSheet {
    let zombie_sprite = match drawable.stance {
      Stance::Still => {
        let sprite_idx = (drawable.direction as usize * 4 + drawable.zombie_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::Walking if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::NormalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 6 + drawable.zombie_death_idx + NORMAL_DEATH_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::CriticalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_death_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      _ => {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
    };

    let (y_div, row_idx) =
      if drawable.stance == Stance::NormalDeath || drawable.stance == Stance::CriticalDeath {
        (0.0, 2)
      } else {
        (1.0, 2)
      };

    let elements_x = ZOMBIE_SHEET_TOTAL_WIDTH / (zombie_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div,
      row_idx,
      index: zombie_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut ZombieDrawable,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(&mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, Zombies>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, Bullets>,
                     Fetch<'a, Dimensions>);

  fn run(&mut self, (mut zombies, camera_input, character_input, character, bullets, dim): Self::SystemData) {
    use specs::Join;

    for (zs, camera, ci, c, bs) in (&mut zombies, &camera_input, &character_input, &character, &bullets).join() {
      let world_to_clip = dim.world_to_projection(camera);

      for z in &mut zs.zombies {
        z.update(&world_to_clip, ci, c);
        z.check_bullet_hits(&bs.bullets);
      }
    }
  }
}
<commit_msg>Resize zombie hit area<commit_after>use bullet::{BulletDrawable, bullets::Bullets};
use cgmath;
use cgmath::{Deg, Point2};
use character::{CharacterDrawable, controls::CharacterInputState};
use critter::CritterData;
use data;
use game::constants::{ASPECT_RATIO, NORMAL_DEATH_SPRITE_OFFSET, SPRITE_OFFSET, ZOMBIE_SHEET_TOTAL_WIDTH, ZOMBIE_STILL_SPRITE_OFFSET};
use game::get_random_bool;
use gfx;
use gfx_app::{ColorFormat, DepthFormat};
use graphics::{camera::CameraInputState, can_move_to_tile, Dimensions, direction, direction_movement, get_orientation, load_texture, orientation::{Orientation, Stance}, overlaps};
use shaders::{CharacterSheet, critter_pipeline, Position, Projection, VertexData};
use specs;
use specs::{Fetch, ReadStorage, WriteStorage};
use zombie::zombies::Zombies;

pub mod zombies;

const SHADER_VERT: &[u8] = include_bytes!("../shaders/character.v.glsl");
const SHADER_FRAG: &[u8] = include_bytes!("../shaders/character.f.glsl");

#[derive(Debug, Clone)]
pub struct ZombieDrawable {
  projection: Projection,
  pub position: Position,
  previous_position: Position,
  orientation: Orientation,
  pub stance: Stance,
  direction: Orientation,
  pub movement_direction: Point2<f32>,
  zombie_idx: usize,
  zombie_death_idx: usize,
  is_colliding: bool,
}

impl ZombieDrawable {
  pub fn new(position: Position) -> ZombieDrawable {
    let view = Dimensions::get_view_matrix();
    ZombieDrawable {
      projection: Projection {
        model: view.into(),
        view: view.into(),
        proj: cgmath::perspective(Deg(60.0f32), ASPECT_RATIO, 0.1, 4000.0).into(),
      },
      position,
      previous_position: Position::new([0.0, 0.0]),
      orientation: Orientation::Left,
      stance: Stance::Still,
      direction: Orientation::Left,
      movement_direction: Point2::new(0.0, 0.0),
      zombie_idx: 0,
      zombie_death_idx: 0,
      is_colliding: false
    }
  }

  pub fn update(&mut self, world_to_clip: &Projection, ci: &CharacterInputState, character: &CharacterDrawable) {
    self.projection = *world_to_clip;

    let offset_delta =
      Position::new([
        ci.x_movement - self.previous_position.position[0],
        ci.y_movement - self.previous_position.position[1]
      ]);

    self.previous_position = Position::new([
      ci.x_movement,
      ci.y_movement
    ]);
    let movement_speed = 1.0;

    if self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
      let dir = direction(
        Point2::new(self.position.position[0], self.position.position[1]),
        Point2::new(character.position.position[0], character.position.position[1])
      );

      self.direction = get_orientation(dir);

      self.movement_direction = direction_movement(dir);
      self.stance = Stance::Walking;
    } else {
      self.movement_direction = Point2::new(0.0, 0.0);
    }

    let new_pos = Position::new([
      self.position.position[0] + offset_delta.position[0] + (self.movement_direction.x * movement_speed),
      self.position.position[1] + offset_delta.position[1] + (self.movement_direction.y * movement_speed)
    ]);

    let new_pos_without_movement = Position::new([
      self.position.position[0] + offset_delta.position[0],
      self.position.position[1] + offset_delta.position[1]
    ]);

    let tile_pos = Position::new([ci.x_movement - new_pos.position[0], ci.y_movement - new_pos.position[1]]);

    if can_move_to_tile(tile_pos) {
      self.position = new_pos;
    } else {
      self.position = new_pos_without_movement;
    }
  }

  pub fn check_bullet_hits(&mut self, bullets: &[BulletDrawable]) {
    bullets.iter().for_each(|bullet| {
      if overlaps(self.position, bullet.position, 40.0, 80.0) && self.stance != Stance::NormalDeath && self.stance != Stance::CriticalDeath {
        self.stance =
          if get_random_bool() {
            Stance::NormalDeath
          } else {
            Stance::CriticalDeath
          };
      }
    });
  }

  pub fn update_alive_idx(&mut self, max_idx: usize) {
    if self.zombie_idx < max_idx {
      self.zombie_idx += 1;
    } else {
      self.zombie_idx = 0;
    }
  }

  pub fn update_death_idx(&mut self, max_idx: usize) {
    if self.zombie_death_idx < max_idx {
      self.zombie_death_idx += 1;
    }
  }
}

pub struct ZombieDrawSystem<R: gfx::Resources> {
  bundle: gfx::pso::bundle::Bundle<R, critter_pipeline::Data<R>>,
  data: Vec<CritterData>,
}

impl<R: gfx::Resources> ZombieDrawSystem<R> {
  pub fn new<F>(factory: &mut F,
                rtv: gfx::handle::RenderTargetView<R, ColorFormat>,
                dsv: gfx::handle::DepthStencilView<R, DepthFormat>) -> ZombieDrawSystem<R>
                where F: gfx::Factory<R> {
    use gfx::traits::FactoryExt;

    let zombie_bytes = include_bytes!("../../assets/zombie.png");

    let vertex_data: [VertexData; 4] = [
      VertexData::new([-25.0, -35.0], [0.0, 1.0]),
      VertexData::new([25.0, -35.0], [1.0, 1.0]),
      VertexData::new([25.0, 35.0], [1.0, 0.0]),
      VertexData::new([-25.0, 35.0], [0.0, 0.0]),
    ];

    let index_data: [u16; 6] = [0, 1, 2, 2, 3, 0];

    let (vertex_buf, slice) = factory.create_vertex_buffer_with_slice(&vertex_data[..], &index_data[..]);

    let char_texture = load_texture(factory, zombie_bytes).unwrap();
    let pso = factory
      .create_pipeline_simple(SHADER_VERT,
                              SHADER_FRAG,
                              critter_pipeline::new())
      .unwrap();

    let pipeline_data = critter_pipeline::Data {
      vbuf: vertex_buf,
      projection_cb: factory.create_constant_buffer(1),
      position_cb: factory.create_constant_buffer(1),
      character_sprite_cb: factory.create_constant_buffer(1),
      charactersheet: (char_texture, factory.create_sampler_linear()),
      out_color: rtv,
      out_depth: dsv,
    };

    let data = data::load_zombie();

    ZombieDrawSystem {
      bundle: gfx::Bundle::new(slice, pso, pipeline_data),
      data,
    }
  }

  fn get_next_sprite(&self, drawable: &mut ZombieDrawable) -> CharacterSheet {
    let zombie_sprite = match drawable.stance {
      Stance::Still => {
        let sprite_idx = (drawable.direction as usize * 4 + drawable.zombie_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::Walking if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::NormalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 6 + drawable.zombie_death_idx + NORMAL_DEATH_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      Stance::CriticalDeath if drawable.orientation != Orientation::Still => {
        let sprite_idx = (drawable.direction as usize * 8 + drawable.zombie_death_idx) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
      _ => {
        drawable.direction = drawable.orientation;
        let sprite_idx = (drawable.orientation as usize * 8 + drawable.zombie_idx + ZOMBIE_STILL_SPRITE_OFFSET) as usize;
        (&self.data[sprite_idx], sprite_idx)
      },
    };

    let (y_div, row_idx) =
      if drawable.stance == Stance::NormalDeath || drawable.stance == Stance::CriticalDeath {
        (0.0, 2)
      } else {
        (1.0, 2)
      };

    let elements_x = ZOMBIE_SHEET_TOTAL_WIDTH / (zombie_sprite.0.data[2] + SPRITE_OFFSET);
    CharacterSheet {
      x_div: elements_x,
      y_div,
      row_idx,
      index: zombie_sprite.1 as f32,
    }
  }

  pub fn draw<C>(&mut self,
                 mut drawable: &mut ZombieDrawable,
                 encoder: &mut gfx::Encoder<R, C>)
                 where C: gfx::CommandBuffer<R> {
    encoder.update_constant_buffer(&self.bundle.data.projection_cb, &drawable.projection);
    encoder.update_constant_buffer(&self.bundle.data.position_cb, &drawable.position);
    encoder.update_constant_buffer(&self.bundle.data.character_sprite_cb,
                                   &self.get_next_sprite(&mut drawable));
    self.bundle.encode(encoder);
  }
}

#[derive(Debug)]
pub struct PreDrawSystem;

impl PreDrawSystem {
  pub fn new() -> PreDrawSystem {
    PreDrawSystem {}
  }
}

impl<'a> specs::System<'a> for PreDrawSystem {
  #[cfg_attr(feature = "cargo-clippy", allow(type_complexity))]
  type SystemData = (WriteStorage<'a, Zombies>,
                     ReadStorage<'a, CameraInputState>,
                     ReadStorage<'a, CharacterInputState>,
                     ReadStorage<'a, CharacterDrawable>,
                     ReadStorage<'a, Bullets>,
                     Fetch<'a, Dimensions>);

  fn run(&mut self, (mut zombies, camera_input, character_input, character, bullets, dim): Self::SystemData) {
    use specs::Join;

    for (zs, camera, ci, c, bs) in (&mut zombies, &camera_input, &character_input, &character, &bullets).join() {
      let world_to_clip = dim.world_to_projection(camera);

      for z in &mut zs.zombies {
        z.update(&world_to_clip, ci, c);
        z.check_bullet_hits(&bs.bullets);
      }
    }
  }
}
<|endoftext|>


<commit_before>#![doc(html_root_url="https://drakulix.github.io/zwreec/",
    html_logo_url="https://dl.dropboxusercontent.com/u/70410095/zwreec/logo.png")]
//! Twee-to-Zcode compile library.
//!
//! Zwreec is a compiler for [interactive fiction](http://en.wikipedia.org/wiki/Interactive_fiction)
//! in the Twee format (created by the [Twine software](http://en.wikipedia.org/wiki/Twine_(software)))
//! to [Z-Machine](http://en.wikipedia.org/wiki/Z-machine) instructions (Zcode)
//! which can be run with interpreters like [frotz](http://frotz.sourceforge.net).
//!
//! This library was developed as part of the course "Softwareprojekt
//! Übersetzerbau" in 2015.
//!
//! # Requirements and Usage
//!
//! The library uses a [fork of rustlex](https://github.com/Drakulix/rustlex)
//! to do lexical analysis.
//! You can install Rust stable or nightly as you like via the
//! [provided binaries](http://www.rust-lang.org/install.html)
//! or the rustup script:
//!
//! ```sh
//! $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly --date=2015-06-02
//! ```
//!
//! Or install the Ubuntu/Debian Packages for
//! [Rust](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/r/rust-nightly/) and
//! [Cargo](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/c/cargo-nightly/).
//!
//! To use zwreec in your project you can add it as a dependency to your `Cargo.toml`.
//!
//! ```toml
//! [dependencies.zwreec]
//! git = "https:://github.com/Drakulix/zwreec"
//! ```
//!
//! Then you can use it in your crate root.
//!
//! ```rust
//! extern crate zwreec;
//! ```
//!
//! # Features
//!
//! Only rudimentary Twee features are supported right now. This is about to change in the upcoming weeks.
//! Check the github issues for more information on the currently supported features.
//!
//! # Example
//!
//! The following example is itself a full working command line compiler. It is a simpler
//! version of the [Reference Binary Implementation](#reference-binary-implementation).
//!
//! ```no_run
//! extern crate zwreec;
//!
//! use std::env;
//! use std::error::Error;
//! use std::fs::File;
//! use std::path::Path;
//!
//! fn main() {
//!     let mut args: Vec<String> = env::args().collect();
//!
//!     if args.len() != 2 { panic!("Need exactly one input file!"); }
//!
//!     let cfg = zwreec::config::Config::default_config();
//!     let mut input = match File::open(Path::new(&args[1])) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open input: {}", Error::description(&why)); }
//!     };
//!     let mut output = match File::create(Path::new("a.z8")) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open output: {}", Error::description(&why)); }
//!     };
//!
//!     zwreec::compile(cfg, &mut input, &mut output);
//! }
//! ```
//!
//! # Reference Binary Implementation
//!
//! Zwreecs [Github-Repository](https://github.com/Drakulix/zwreec) contains a
//! reference binary implementation that uses this library and provides a simple
//! command line interface to compile Twee files.
//!
//! To build the binary, you need the rust version as outlined in [Requirements and
//! Usage](#requirements-and-usage). Then you can build the binary using
//!
//! ```sh
//! $ cargo build
//! ```
//!
//! The resulting binary can be found at `target/debug/zwreec`.
//!
//! # Logging
//!
//! Zwreec uses the logging facade provided by
//! [log](../log/index.html). The reference binary implementation of zwreec also includes an
//! implementation of the `Log` trait.

extern crate rustlex_codegen as rustlex;
#[macro_use] extern crate log;
extern crate time;
extern crate term;
extern crate getopts;

#[macro_use] pub mod utils;
pub mod config;
pub mod frontend;
pub mod backend;

use config::{Config,TestCase};
use std::io::{Read,Write};

/// Compiles a Twee Input to Zcode
///
/// This is the main call into the Zwreec compiler. It will process `input: Read`
/// by calling the different parts of the compiler-chain, notably `frontend::lexer`
/// to generate a tokenstream, `frontend::parser` and `frontend::ast` to generate
/// the Abstract Syntax Tree and lastly `frontend::codegen` to generate the Zcode.
///
/// # Example
///
/// ```no_run
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// # let mut args: Vec<String> = env::args().collect();
/// #
/// # if args.len() != 2 { panic!("Need exactly one input file!"); }
/// #
/// let cfg = zwreec::config::Config::default_config();
/// let mut input = File::open(Path::new(&args[1])).unwrap();
/// let mut output = File::create(Path::new("a.z8")).unwrap();
///
/// zwreec::compile(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn compile<R: Read, W: Write>(cfg: Config, input: &mut R, output: &mut W) {
    // tokenize
    let tokens = frontend::lexer::lex(&cfg, input);

    // create parser
    let parser = frontend::parser::Parser::new(&cfg);

    // create ast builder
    let ast_builder = frontend::ast::ASTBuilder::new(&cfg);

    //build up ast from tokens
    let ast = ast_builder.build(parser.parse(tokens.inspect(|ref token| {
        debug!("{:?}", token);
    })));
    debug!("{:?}", ast);

    // create code
    frontend::codegen::generate_zcode(&cfg, ast, output);
}

/// Run internal library tests.
///
/// This function is used to circumvent certain parts of the compiler toolchain.
/// It currently only processes `TestCase::ZcodeBackend` which creates a Zcode
/// file using all available OP-Codes.
///
/// **Warning:** This function should be considered unstable and might be removed
/// in later versions.
///
/// # Example
///
/// ```
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// let mut cfg = zwreec::config::Config::default_config();
/// cfg.test_cases.push(zwreec::config::TestCase::ZcodeBackend);
///
/// let mut input: Option<File> = None;
/// let mut output = Some(File::create(Path::new("a.z8")).unwrap());
///
/// zwreec::test_library(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn test_library<R: Read, W: Write>(cfg: Config, input: &mut Option<R>, output: &mut Option<W>) {
    for case in cfg.test_cases {
        match case {
            TestCase::ZcodeBackend => {
                match output.as_mut() {
                     Some(o) => backend::zcode::temp_create_zcode_example(o),
                     None => error!("TestCase::ZcodeBackend requires output!"),
                }
            }
        }
    }
}
<commit_msg>checks if there is a bom and remove it<commit_after>#![doc(html_root_url="https://drakulix.github.io/zwreec/",
    html_logo_url="https://dl.dropboxusercontent.com/u/70410095/zwreec/logo.png")]
//! Twee-to-Zcode compile library.
//!
//! Zwreec is a compiler for [interactive fiction](http://en.wikipedia.org/wiki/Interactive_fiction)
//! in the Twee format (created by the [Twine software](http://en.wikipedia.org/wiki/Twine_(software)))
//! to [Z-Machine](http://en.wikipedia.org/wiki/Z-machine) instructions (Zcode)
//! which can be run with interpreters like [frotz](http://frotz.sourceforge.net).
//!
//! This library was developed as part of the course "Softwareprojekt
//! Übersetzerbau" in 2015.
//!
//! # Requirements and Usage
//!
//! The library uses a [fork of rustlex](https://github.com/Drakulix/rustlex)
//! to do lexical analysis.
//! You can install Rust stable or nightly as you like via the
//! [provided binaries](http://www.rust-lang.org/install.html)
//! or the rustup script:
//!
//! ```sh
//! $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly --date=2015-06-02
//! ```
//!
//! Or install the Ubuntu/Debian Packages for
//! [Rust](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/r/rust-nightly/) and
//! [Cargo](http://ppa.launchpad.net/hansjorg/rust/ubuntu/pool/main/c/cargo-nightly/).
//!
//! To use zwreec in your project you can add it as a dependency to your `Cargo.toml`.
//!
//! ```toml
//! [dependencies.zwreec]
//! git = "https:://github.com/Drakulix/zwreec"
//! ```
//!
//! Then you can use it in your crate root.
//!
//! ```rust
//! extern crate zwreec;
//! ```
//!
//! # Features
//!
//! Only rudimentary Twee features are supported right now. This is about to change in the upcoming weeks.
//! Check the github issues for more information on the currently supported features.
//!
//! # Example
//!
//! The following example is itself a full working command line compiler. It is a simpler
//! version of the [Reference Binary Implementation](#reference-binary-implementation).
//!
//! ```no_run
//! extern crate zwreec;
//!
//! use std::env;
//! use std::error::Error;
//! use std::fs::File;
//! use std::path::Path;
//!
//! fn main() {
//!     let mut args: Vec<String> = env::args().collect();
//!
//!     if args.len() != 2 { panic!("Need exactly one input file!"); }
//!
//!     let cfg = zwreec::config::Config::default_config();
//!     let mut input = match File::open(Path::new(&args[1])) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open input: {}", Error::description(&why)); }
//!     };
//!     let mut output = match File::create(Path::new("a.z8")) {
//!         Ok(file) => file,
//!         Err(why) => { panic!("Couldn't open output: {}", Error::description(&why)); }
//!     };
//!
//!     zwreec::compile(cfg, &mut input, &mut output);
//! }
//! ```
//!
//! # Reference Binary Implementation
//!
//! Zwreecs [Github-Repository](https://github.com/Drakulix/zwreec) contains a
//! reference binary implementation that uses this library and provides a simple
//! command line interface to compile Twee files.
//!
//! To build the binary, you need the rust version as outlined in [Requirements and
//! Usage](#requirements-and-usage). Then you can build the binary using
//!
//! ```sh
//! $ cargo build
//! ```
//!
//! The resulting binary can be found at `target/debug/zwreec`.
//!
//! # Logging
//!
//! Zwreec uses the logging facade provided by
//! [log](../log/index.html). The reference binary implementation of zwreec also includes an
//! implementation of the `Log` trait.

extern crate rustlex_codegen as rustlex;
#[macro_use] extern crate log;
extern crate time;
extern crate term;
extern crate getopts;

#[macro_use] pub mod utils;
pub mod config;
pub mod frontend;
pub mod backend;

use config::{Config,TestCase};
use std::error::Error;
use std::io::{BufReader,Cursor,Read,Write};

/// Compiles a Twee Input to Zcode
///
/// This is the main call into the Zwreec compiler. It will process `input: Read`
/// by calling the different parts of the compiler-chain, notably `frontend::lexer`
/// to generate a tokenstream, `frontend::parser` and `frontend::ast` to generate
/// the Abstract Syntax Tree and lastly `frontend::codegen` to generate the Zcode.
///
/// # Example
///
/// ```no_run
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// # let mut args: Vec<String> = env::args().collect();
/// #
/// # if args.len() != 2 { panic!("Need exactly one input file!"); }
/// #
/// let cfg = zwreec::config::Config::default_config();
/// let mut input = File::open(Path::new(&args[1])).unwrap();
/// let mut output = File::create(Path::new("a.z8")).unwrap();
///
/// zwreec::compile(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn compile<R: Read, W: Write>(cfg: Config, input: &mut R, output: &mut W) {

    // check the data if it has a bom
    let mut cursor = handle_bom_encoding(input);

    // tokenize
    let tokens = frontend::lexer::lex(&cfg, &mut cursor);

    // create parser
    let parser = frontend::parser::Parser::new(&cfg);

    // create ast builder
    let ast_builder = frontend::ast::ASTBuilder::new(&cfg);

    // build up ast from tokens
    let ast = ast_builder.build(parser.parse(tokens.inspect(|ref token| {
        debug!("{:?}", token);
    })));
    debug!("{:?}", ast);

    // create code
    frontend::codegen::generate_zcode(&cfg, ast, output);
}

/// checks the input if there is an bom, if true it will delete it
fn handle_bom_encoding<'a, R: Read>(input: &'a mut R) -> Cursor<Vec<u8>> {
    let mut reader = BufReader::new(input);
    let mut content = String::new();
    match reader.read_to_string(&mut content) {
        Err(why) => error!("Couldn't read {}", Error::description(&why)),
        Ok(_) => (),
    };

    let mut v: Vec<u8> = content.bytes().collect();
    if v.len() < 5 {
        error!("The file is to short for a valid twee File");
    }
    let has_bom = if &v[0..3] == [0xef, 0xbb, 0xbf] {
        true
    } else {
        false
    };
    if has_bom {
        debug!("File has Byte order mark");
        v.remove(0);
        v.remove(0);
        v.remove(0);
    }

    let cursor: Cursor<Vec<u8>> = Cursor::new(v);

    cursor
}

/// Run internal library tests.
///
/// This function is used to circumvent certain parts of the compiler toolchain.
/// It currently only processes `TestCase::ZcodeBackend` which creates a Zcode
/// file using all available OP-Codes.
///
/// **Warning:** This function should be considered unstable and might be removed
/// in later versions.
///
/// # Example
///
/// ```
/// # use std::env;
/// # use std::fs::File;
/// # use std::path::Path;
/// let mut cfg = zwreec::config::Config::default_config();
/// cfg.test_cases.push(zwreec::config::TestCase::ZcodeBackend);
///
/// let mut input: Option<File> = None;
/// let mut output = Some(File::create(Path::new("a.z8")).unwrap());
///
/// zwreec::test_library(cfg, &mut input, &mut output);
/// ```
#[allow(unused_variables)]
pub fn test_library<R: Read, W: Write>(cfg: Config, input: &mut Option<R>, output: &mut Option<W>) {
    for case in cfg.test_cases {
        match case {
            TestCase::ZcodeBackend => {
                match output.as_mut() {
                     Some(o) => backend::zcode::temp_create_zcode_example(o),
                     None => error!("TestCase::ZcodeBackend requires output!"),
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;

#[cfg(test)] extern crate toml;

extern crate libimagstore;
extern crate libimagrt;
extern crate libimagentrytag;
extern crate libimagerror;

#[cfg(test)]
#[macro_use]
extern crate libimagutil;

#[cfg(not(test))]
extern crate libimagutil;

#[cfg(test)]
extern crate toml_query;

#[cfg(test)]
extern crate env_logger;

use std::path::PathBuf;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagentrytag::tagable::Tagable;
use libimagentrytag::tag::Tag;
use libimagerror::trace::{trace_error, trace_error_exit};
use libimagstore::storeid::StoreId;
use libimagutil::warn_exit::warn_exit;

use clap::ArgMatches;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-store",
                                    &version!()[..],
                                    "Direct interface to the store. Use with great care!",
                                    build_ui);

    let id = rt.cli().value_of("id").unwrap(); // enforced by clap
    rt.cli()
        .subcommand_name()
        .map_or_else(
            || {
                let id = PathBuf::from(id);
                let add = get_add_tags(rt.cli());
                let rem = get_remove_tags(rt.cli());
                alter(&rt, id, add, rem);
            },
            |name| {
                let id = PathBuf::from(id);
                debug!("Call: {}", name);
                match name {
                    "list" => list(id, &rt),
                    _ => {
                        warn!("Unknown command");
                        // More error handling
                    },
                };
            });
}

fn alter(rt: &Runtime, id: PathBuf, add: Option<Vec<Tag>>, rem: Option<Vec<Tag>>) {
    let path = {
        match StoreId::new(Some(rt.store().path().clone()), id) {
            Err(e) => trace_error_exit(&e, 1),
            Ok(s) => s,
        }
    };
    debug!("path = {:?}", path);

    match rt.store().get(path) {
        Ok(Some(mut e)) => {
            add.map(|tags| {
                for tag in tags {
                    debug!("Adding tag '{:?}'", tag);
                    if let Err(e) = e.add_tag(tag) {
                        trace_error(&e);
                    }
                }
            }); // it is okay to ignore a None here

            rem.map(|tags| {
                for tag in tags {
                    debug!("Removing tag '{:?}'", tag);
                    if let Err(e) = e.remove_tag(tag) {
                        trace_error(&e);
                    }
                }
            }); // it is okay to ignore a None here
        },

        Ok(None) => {
            info!("No entry found.");
        },

        Err(e) => {
            info!("No entry.");
            trace_error(&e);
        },
    }
}

fn list(id: PathBuf, rt: &Runtime) {
    let path = match StoreId::new(Some(rt.store().path().clone()), id) {
        Err(e) => trace_error_exit(&e, 1),
        Ok(s)  => s,
    };
    debug!("path = {:?}", path);

    let entry = match rt.store().get(path.clone()) {
        Ok(Some(e)) => e,
        Ok(None) => warn_exit("No entry found.", 1),

        Err(e) => {
            warn!("Could not get entry '{:?}'", path);
            trace_error_exit(&e, 1);
        },
    };

    let scmd = rt.cli().subcommand_matches("list").unwrap(); // safe, we checked in main()

    let json_out = scmd.is_present("json");
    let line_out = scmd.is_present("linewise");
    let sepp_out = scmd.is_present("sep");
    let mut comm_out = scmd.is_present("commasep");

    if !vec![json_out, line_out, comm_out, sepp_out].iter().any(|v| *v) {
        // None of the flags passed, go to default
        comm_out = true;
    }

    let tags = entry.get_tags();
    if tags.is_err() {
        trace_error_exit(&tags.unwrap_err(), 1);
    }
    let tags = tags.unwrap();

    if json_out {
        unimplemented!()
    }

    if line_out {
        for tag in &tags {
            println!("{}", tag);
        }
    }

    if sepp_out {
        let sepp = scmd.value_of("sep").unwrap(); // we checked before
        println!("{}", tags.join(sepp));
    }

    if comm_out {
        println!("{}", tags.join(", "));
    }
}

/// Get the tags which should be added from the commandline
///
/// Returns none if the argument was not specified
fn get_add_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
    let a = "add-tags";
    extract_tags(matches, a, '+')
        .or_else(|| matches.values_of(a).map(|values| values.map(String::from).collect()))
}

/// Get the tags which should be removed from the commandline
///
/// Returns none if the argument was not specified
fn get_remove_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
    let r = "remove-tags";
    extract_tags(matches, r, '+')
        .or_else(|| matches.values_of(r).map(|values| values.map(String::from).collect()))
}

fn extract_tags(matches: &ArgMatches, specifier: &str, specchar: char) -> Option<Vec<Tag>> {
    if let Some(submatch) = matches.subcommand_matches("tags") {
        submatch.values_of(specifier)
            .map(|values| values.map(String::from).collect())
    } else {
        matches.values_of("specify-tags")
            .map(|argmatches| {
                argmatches
                    .map(String::from)
                    .filter(|s| s.starts_with(specchar))
                    .map(|s| {
                        String::from(s.split_at(1).1)
                    })
                    .collect()
            })
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;
    use std::ffi::OsStr;

    use toml::value::Value;
    use toml_query::read::TomlValueReadExt;
    use toml_query::error::Result as TomlQueryResult;

    use libimagrt::runtime::Runtime;
    use libimagstore::storeid::StoreId;
    use libimagstore::store::{Result as StoreResult, FileLockEntry};

    use super::*;

    make_mock_app! {
        app "imag-tag";
        modulename mock;
        version "0.4.0";
        with help "imag-tag mocking app";
    }
    use self::mock::generate_test_runtime;
    use libimagutil::testing::DEFAULT_ENTRY;

    fn create_test_default_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> StoreResult<StoreId> {
        let mut path = PathBuf::new();
        path.set_file_name(name);

        let id = StoreId::new_baseless(path)?;
        let mut entry = rt.store().create(id.clone())?;
        entry.get_content_mut().push_str(DEFAULT_ENTRY);

        Ok(id)
    }

    fn get_entry_tags<'a>(entry: &'a FileLockEntry<'a>) -> TomlQueryResult<Option<&'a Value>> {
        entry.get_header().read(&"imag.tags".to_owned())
    }

    fn tags_toml_value<'a, I: IntoIterator<Item = &'static str>>(tags: I) -> Value {
        Value::Array(tags.into_iter().map(|s| Value::String(s.to_owned())).collect())
    }

    fn setup_logging() {
        use env_logger;
        let _ = env_logger::init().unwrap_or(());
    }

    #[test]
    fn test_tag_add_adds_tag() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test", "--add", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        debug!("Getting 'add' tags");
        let add = get_add_tags(rt.cli());
        debug!("Add-tags: {:?}", add);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_ne!(*test_tags, tags_toml_value(vec![]));
        assert_eq!(*test_tags, tags_toml_value(vec!["foo"]));
    }

    #[test]
    fn test_tag_add_more_than_remove_adds_tags() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test",
                                       "--add", "foo",
                                       "--add", "bar",
                                       "--add", "baz",
                                       "--add", "bub",
                                       "--remove", "foo",
                                       "--remove", "bar",
                                       "--remove", "baz",
        ]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        // Manually add tags
        let add = get_add_tags(rt.cli());

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec!["bub"]));
    }

    #[test]
    fn test_tag_remove_removes_tag() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test", "--remove", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
    }

    #[test]
    fn test_tag_remove_removes_only_to_remove_tag() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test", "--remove", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned(), "bar".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec!["bar"]));
    }

    #[test]
    fn test_tag_remove_removes_but_doesnt_crash_on_nonexistent_tag() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test", "--remove", "foo", "--remove", "bar"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
    }

}

<commit_msg>Add default entry by simply creating an empty Entry<commit_after>//
// imag - the personal information management suite for the commandline
// Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; version
// 2.1 of the License.
//
// This library 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
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//

extern crate clap;
#[macro_use] extern crate log;
#[macro_use] extern crate version;

#[cfg(test)] extern crate toml;

extern crate libimagstore;
extern crate libimagrt;
extern crate libimagentrytag;
extern crate libimagerror;

#[cfg(test)]
#[macro_use]
extern crate libimagutil;

#[cfg(not(test))]
extern crate libimagutil;

#[cfg(test)]
extern crate toml_query;

#[cfg(test)]
extern crate env_logger;

use std::path::PathBuf;

use libimagrt::runtime::Runtime;
use libimagrt::setup::generate_runtime_setup;
use libimagentrytag::tagable::Tagable;
use libimagentrytag::tag::Tag;
use libimagerror::trace::{trace_error, trace_error_exit};
use libimagstore::storeid::StoreId;
use libimagutil::warn_exit::warn_exit;

use clap::ArgMatches;

mod ui;

use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-store",
                                    &version!()[..],
                                    "Direct interface to the store. Use with great care!",
                                    build_ui);

    let id = rt.cli().value_of("id").unwrap(); // enforced by clap
    rt.cli()
        .subcommand_name()
        .map_or_else(
            || {
                let id = PathBuf::from(id);
                let add = get_add_tags(rt.cli());
                let rem = get_remove_tags(rt.cli());
                alter(&rt, id, add, rem);
            },
            |name| {
                let id = PathBuf::from(id);
                debug!("Call: {}", name);
                match name {
                    "list" => list(id, &rt),
                    _ => {
                        warn!("Unknown command");
                        // More error handling
                    },
                };
            });
}

fn alter(rt: &Runtime, id: PathBuf, add: Option<Vec<Tag>>, rem: Option<Vec<Tag>>) {
    let path = {
        match StoreId::new(Some(rt.store().path().clone()), id) {
            Err(e) => trace_error_exit(&e, 1),
            Ok(s) => s,
        }
    };
    debug!("path = {:?}", path);

    match rt.store().get(path) {
        Ok(Some(mut e)) => {
            add.map(|tags| {
                for tag in tags {
                    debug!("Adding tag '{:?}'", tag);
                    if let Err(e) = e.add_tag(tag) {
                        trace_error(&e);
                    }
                }
            }); // it is okay to ignore a None here

            rem.map(|tags| {
                for tag in tags {
                    debug!("Removing tag '{:?}'", tag);
                    if let Err(e) = e.remove_tag(tag) {
                        trace_error(&e);
                    }
                }
            }); // it is okay to ignore a None here
        },

        Ok(None) => {
            info!("No entry found.");
        },

        Err(e) => {
            info!("No entry.");
            trace_error(&e);
        },
    }
}

fn list(id: PathBuf, rt: &Runtime) {
    let path = match StoreId::new(Some(rt.store().path().clone()), id) {
        Err(e) => trace_error_exit(&e, 1),
        Ok(s)  => s,
    };
    debug!("path = {:?}", path);

    let entry = match rt.store().get(path.clone()) {
        Ok(Some(e)) => e,
        Ok(None) => warn_exit("No entry found.", 1),

        Err(e) => {
            warn!("Could not get entry '{:?}'", path);
            trace_error_exit(&e, 1);
        },
    };

    let scmd = rt.cli().subcommand_matches("list").unwrap(); // safe, we checked in main()

    let json_out = scmd.is_present("json");
    let line_out = scmd.is_present("linewise");
    let sepp_out = scmd.is_present("sep");
    let mut comm_out = scmd.is_present("commasep");

    if !vec![json_out, line_out, comm_out, sepp_out].iter().any(|v| *v) {
        // None of the flags passed, go to default
        comm_out = true;
    }

    let tags = entry.get_tags();
    if tags.is_err() {
        trace_error_exit(&tags.unwrap_err(), 1);
    }
    let tags = tags.unwrap();

    if json_out {
        unimplemented!()
    }

    if line_out {
        for tag in &tags {
            println!("{}", tag);
        }
    }

    if sepp_out {
        let sepp = scmd.value_of("sep").unwrap(); // we checked before
        println!("{}", tags.join(sepp));
    }

    if comm_out {
        println!("{}", tags.join(", "));
    }
}

/// Get the tags which should be added from the commandline
///
/// Returns none if the argument was not specified
fn get_add_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
    let a = "add-tags";
    extract_tags(matches, a, '+')
        .or_else(|| matches.values_of(a).map(|values| values.map(String::from).collect()))
}

/// Get the tags which should be removed from the commandline
///
/// Returns none if the argument was not specified
fn get_remove_tags(matches: &ArgMatches) -> Option<Vec<Tag>> {
    let r = "remove-tags";
    extract_tags(matches, r, '+')
        .or_else(|| matches.values_of(r).map(|values| values.map(String::from).collect()))
}

fn extract_tags(matches: &ArgMatches, specifier: &str, specchar: char) -> Option<Vec<Tag>> {
    if let Some(submatch) = matches.subcommand_matches("tags") {
        submatch.values_of(specifier)
            .map(|values| values.map(String::from).collect())
    } else {
        matches.values_of("specify-tags")
            .map(|argmatches| {
                argmatches
                    .map(String::from)
                    .filter(|s| s.starts_with(specchar))
                    .map(|s| {
                        String::from(s.split_at(1).1)
                    })
                    .collect()
            })
    }
}

#[cfg(test)]
mod tests {
    use std::path::PathBuf;
    use std::ffi::OsStr;

    use toml::value::Value;
    use toml_query::read::TomlValueReadExt;
    use toml_query::error::Result as TomlQueryResult;

    use libimagrt::runtime::Runtime;
    use libimagstore::storeid::StoreId;
    use libimagstore::store::{Result as StoreResult, FileLockEntry, Entry};

    use super::*;

    make_mock_app! {
        app "imag-tag";
        modulename mock;
        version "0.4.0";
        with help "imag-tag mocking app";
    }
    use self::mock::generate_test_runtime;

    fn create_test_default_entry<'a, S: AsRef<OsStr>>(rt: &'a Runtime, name: S) -> StoreResult<StoreId> {
        let mut path = PathBuf::new();
        path.set_file_name(name);

        let default_entry = Entry::new(StoreId::new_baseless(PathBuf::from("")).unwrap()).to_str();

        let id = StoreId::new_baseless(path)?;
        let mut entry = rt.store().create(id.clone())?;
        entry.get_content_mut().push_str(&default_entry);

        Ok(id)
    }

    fn get_entry_tags<'a>(entry: &'a FileLockEntry<'a>) -> TomlQueryResult<Option<&'a Value>> {
        entry.get_header().read(&"imag.tags".to_owned())
    }

    fn tags_toml_value<'a, I: IntoIterator<Item = &'static str>>(tags: I) -> Value {
        Value::Array(tags.into_iter().map(|s| Value::String(s.to_owned())).collect())
    }

    fn setup_logging() {
        use env_logger;
        let _ = env_logger::init().unwrap_or(());
    }

    #[test]
    fn test_tag_add_adds_tag() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test", "--add", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        debug!("Getting 'add' tags");
        let add = get_add_tags(rt.cli());
        debug!("Add-tags: {:?}", add);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_ne!(*test_tags, tags_toml_value(vec![]));
        assert_eq!(*test_tags, tags_toml_value(vec!["foo"]));
    }

    #[test]
    fn test_tag_add_more_than_remove_adds_tags() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test",
                                       "--add", "foo",
                                       "--add", "bar",
                                       "--add", "baz",
                                       "--add", "bub",
                                       "--remove", "foo",
                                       "--remove", "bar",
                                       "--remove", "baz",
        ]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        // Manually add tags
        let add = get_add_tags(rt.cli());

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec!["bub"]));
    }

    #[test]
    fn test_tag_remove_removes_tag() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test", "--remove", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
    }

    #[test]
    fn test_tag_remove_removes_only_to_remove_tag() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test", "--remove", "foo"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned(), "bar".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec!["bar"]));
    }

    #[test]
    fn test_tag_remove_removes_but_doesnt_crash_on_nonexistent_tag() {
        setup_logging();
        debug!("Generating runtime");
        let rt = generate_test_runtime(vec!["test", "--remove", "foo", "--remove", "bar"]).unwrap();

        debug!("Creating default entry");
        create_test_default_entry(&rt, "test").unwrap();
        let id = PathBuf::from(String::from("test"));

        // Manually add tags
        let add = Some(vec![ "foo".to_owned() ]);

        debug!("Getting 'remove' tags");
        let rem = get_remove_tags(rt.cli());
        debug!("Rem-tags: {:?}", rem);

        debug!("Altering things");
        alter(&rt, id.clone(), add, rem);
        debug!("Altered");

        let test_entry = rt.store().get(id).unwrap().unwrap();
        let test_tags  = get_entry_tags(&test_entry).unwrap().unwrap();

        assert_eq!(*test_tags, tags_toml_value(vec![]));
    }

}

<|endoftext|>


<commit_before><commit_msg>Consider references when applying postfix completions<commit_after><|endoftext|>


<commit_before>use std::rc::Rc;

use crate::{prelude::*, proc_macros::Event, shell::MouseButton, utils::*};

/// Checks if the given point is inside of a widget.
pub fn check_mouse_condition(mouse_position: Point, widget: &WidgetContainer<'_>) -> bool {
    let bounds = widget.get::<Rectangle>("bounds");
    let position = widget.get::<Point>("position");

    let mut rect = Rectangle::new(0.0, 0.0, bounds.width(), bounds.height());

    rect.set_x(position.x);
    rect.set_y(position.y);

    rect.contains((mouse_position.x, mouse_position.y))
}

#[derive(Event)]
pub struct MouseMoveEvent {
    pub x: f64,
    pub y: f64,
}

#[derive(Event)]
pub struct ScrollEvent {
    pub delta: Point,
}

#[derive(Event)]
pub struct MouseUpEvent {
    pub button: MouseButton,
    pub x: f64,

    pub y: f64,
}

#[derive(Event)]
pub struct ClickEvent {
    pub position: Point,
}

#[derive(Event)]
pub struct MouseDownEvent {
    pub button: MouseButton,
    pub x: f64,

    pub y: f64,
}

#[derive(Event)]
pub struct GlobalMouseUpEvent {
    pub button: MouseButton,
    pub x: f64,

    pub y: f64,
}

pub type MouseHandlerFunction = dyn Fn(&mut StatesContext, Point) -> bool + 'static;

pub type GlobalMouseHandlerFunction = dyn Fn(&mut StatesContext, Point) + 'static;

/// Used to handle click events. Could be attached to a widget.
pub struct ClickEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for ClickEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for ClickEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<ClickEvent>()
            .ok()
            .map_or(false, |event| (self.handler)(state_context, event.position))
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<ClickEvent>()
    }
}

/// Used to handle mouse down events. Could be attached to a widget.
pub struct MouseDownEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for MouseDownEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for MouseDownEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<MouseDownEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.x, event.y))
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<MouseDownEvent>()
    }
}

/// Defines an event handler for a global mouse up event. Global mouse up events could not be handled.
pub struct GlobalMouseUpEventHandler {
    handler: Rc<GlobalMouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for GlobalMouseUpEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for GlobalMouseUpEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<GlobalMouseUpEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.x, event.y));
                false
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<GlobalMouseUpEvent>()
    }
}

/// Used to handle mouse down events. Could be attached to a widget.
pub struct MouseUpEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for MouseUpEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for MouseUpEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<MouseUpEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.x, event.y))
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<MouseUpEvent>()
    }
}

/// Used to handle mouse down events. Could be attached to a widget.
pub struct MouseMoveEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for MouseMoveEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for MouseMoveEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<MouseMoveEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.x, event.y))
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<MouseMoveEvent>()
    }
}

pub struct ScrollEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for ScrollEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for ScrollEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<ScrollEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.delta.x, event.delta.y))
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<ScrollEvent>()
    }
}

pub trait MouseHandler: Sized + Widget {
    /// Inserts a click handler.
    fn on_click<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(ClickEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse down handler.
    fn on_mouse_down<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(MouseDownEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse up handler.
    fn on_mouse_up<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(MouseUpEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse handler for global up event.s
    fn on_global_mouse_up<H: Fn(&mut StatesContext, Point) + 'static>(self, handler: H) -> Self {
        self.insert_handler(GlobalMouseUpEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse move handler.
    fn on_mouse_move<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(MouseMoveEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse up handler.
    fn on_scroll<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(ScrollEventHandler {
            handler: Rc::new(handler),
        })
    }
}
<commit_msg>Add enabled check to mouse check.<commit_after>use std::rc::Rc;

use crate::{prelude::*, proc_macros::Event, shell::MouseButton, utils::*};

/// Checks if the given point is inside of a widget.
pub fn check_mouse_condition(mouse_position: Point, widget: &WidgetContainer<'_>) -> bool {
    let enabled = widget.get::<bool>("enabled");

    if !enabled {
        return false;
    }

    let bounds = widget.get::<Rectangle>("bounds");
    let position = widget.get::<Point>("position");

    let mut rect = Rectangle::new(0.0, 0.0, bounds.width(), bounds.height());

    rect.set_x(position.x);
    rect.set_y(position.y);

    rect.contains((mouse_position.x, mouse_position.y))
}

#[derive(Event)]
pub struct MouseMoveEvent {
    pub x: f64,
    pub y: f64,
}

#[derive(Event)]
pub struct ScrollEvent {
    pub delta: Point,
}

#[derive(Event)]
pub struct MouseUpEvent {
    pub button: MouseButton,
    pub x: f64,

    pub y: f64,
}

#[derive(Event)]
pub struct ClickEvent {
    pub position: Point,
}

#[derive(Event)]
pub struct MouseDownEvent {
    pub button: MouseButton,
    pub x: f64,

    pub y: f64,
}

#[derive(Event)]
pub struct GlobalMouseUpEvent {
    pub button: MouseButton,
    pub x: f64,

    pub y: f64,
}

pub type MouseHandlerFunction = dyn Fn(&mut StatesContext, Point) -> bool + 'static;

pub type GlobalMouseHandlerFunction = dyn Fn(&mut StatesContext, Point) + 'static;

/// Used to handle click events. Could be attached to a widget.
pub struct ClickEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for ClickEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for ClickEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<ClickEvent>()
            .ok()
            .map_or(false, |event| (self.handler)(state_context, event.position))
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<ClickEvent>()
    }
}

/// Used to handle mouse down events. Could be attached to a widget.
pub struct MouseDownEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for MouseDownEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for MouseDownEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<MouseDownEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.x, event.y))
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<MouseDownEvent>()
    }
}

/// Defines an event handler for a global mouse up event. Global mouse up events could not be handled.
pub struct GlobalMouseUpEventHandler {
    handler: Rc<GlobalMouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for GlobalMouseUpEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for GlobalMouseUpEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<GlobalMouseUpEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.x, event.y));
                false
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<GlobalMouseUpEvent>()
    }
}

/// Used to handle mouse down events. Could be attached to a widget.
pub struct MouseUpEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for MouseUpEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for MouseUpEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<MouseUpEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.x, event.y))
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<MouseUpEvent>()
    }
}

/// Used to handle mouse down events. Could be attached to a widget.
pub struct MouseMoveEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for MouseMoveEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for MouseMoveEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<MouseMoveEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.x, event.y))
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<MouseMoveEvent>()
    }
}

pub struct ScrollEventHandler {
    handler: Rc<MouseHandlerFunction>,
}

impl Into<Rc<dyn EventHandler>> for ScrollEventHandler {
    fn into(self) -> Rc<dyn EventHandler> {
        Rc::new(self)
    }
}

impl EventHandler for ScrollEventHandler {
    fn handle_event(&self, state_context: &mut StatesContext, event: &EventBox) -> bool {
        event
            .downcast_ref::<ScrollEvent>()
            .ok()
            .map_or(false, |event| {
                (self.handler)(state_context, Point::new(event.delta.x, event.delta.y))
            })
    }

    fn handles_event(&self, event: &EventBox) -> bool {
        event.is_type::<ScrollEvent>()
    }
}

pub trait MouseHandler: Sized + Widget {
    /// Inserts a click handler.
    fn on_click<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(ClickEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse down handler.
    fn on_mouse_down<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(MouseDownEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse up handler.
    fn on_mouse_up<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(MouseUpEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse handler for global up event.s
    fn on_global_mouse_up<H: Fn(&mut StatesContext, Point) + 'static>(self, handler: H) -> Self {
        self.insert_handler(GlobalMouseUpEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse move handler.
    fn on_mouse_move<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(MouseMoveEventHandler {
            handler: Rc::new(handler),
        })
    }

    /// Insert a mouse up handler.
    fn on_scroll<H: Fn(&mut StatesContext, Point) -> bool + 'static>(self, handler: H) -> Self {
        self.insert_handler(ScrollEventHandler {
            handler: Rc::new(handler),
        })
    }
}
<|endoftext|>


<commit_before>//! fixed-point numerical types

use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};

// shared between Fixed and F2Dot14
macro_rules! fixed_impl {
    ($name:ident, $bits:literal, $fract_bits:literal, $ty:ty) => {
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
        #[doc = concat!(stringify!($bits), "-bit signed fixed point number with ", stringify!($fract_bits), " bits of fraction." )]
        pub struct $name($ty);
        impl $name {
            /// Minimum value.
            pub const MIN: Self = Self(<$ty>::MIN);

            /// Maximum value.
            pub const MAX: Self = Self(<$ty>::MAX);

            /// This type's smallest representable value
            pub const EPSILON: Self = Self(1);

            const INT_MASK: $ty = !0 << $fract_bits;
            const ROUND: $ty = 1 << ($fract_bits - 1);
            const ONE: $ty = 1 << $fract_bits;
            const FRACT_BITS: usize = $fract_bits;

            //TODO: is this actually useful?
            /// Returns the nearest integer value.
            pub fn round(self) -> Self {
                Self(self.0.wrapping_add(Self::ROUND) & Self::INT_MASK)
            }

            /// Returns the absolute value of the number.
            pub fn abs(self) -> Self {
                Self(self.0.abs())
            }

            /// Returns the largest integer less than or equal to the number.
            pub fn floor(self) -> Self {
                Self(self.0 & Self::INT_MASK)
            }

            /// Returns the fractional part of the number.
            pub fn fract(self) -> Self {
                Self(self.0 - self.floor().0)
            }

            /// Wrapping addition.
            pub fn wrapping_add(self, other: Self) -> Self {
                Self(self.0.wrapping_add(other.0))
            }

            /// Saturating addition.
            pub fn saturating_add(self, other: Self) -> Self {
                Self(self.0.saturating_add(other.0))
            }

            /// Wrapping substitution.
            pub fn wrapping_sub(self, other: Self) -> Self {
                Self(self.0.wrapping_sub(other.0))
            }

            /// Saturating substitution.
            pub fn saturating_sub(self, other: Self) -> Self {
                Self(self.0.saturating_sub(other.0))
            }

            /// The representation of this number as a big-endian byte array.
            pub fn to_be_bytes(self) -> [u8; $bits / 8] {
                self.0.to_be_bytes()
            }
        }

        impl Add for $name {
            type Output = Self;
            #[inline(always)]
            fn add(self, other: Self) -> Self {
                // same overflow semantics as std: panic in debug, wrap in release
                Self(self.0 + other.0)
            }
        }

        impl AddAssign for $name {
            fn add_assign(&mut self, other: Self) {
                *self = *self + other;
            }
        }

        impl Sub for $name {
            type Output = Self;
            #[inline(always)]
            fn sub(self, other: Self) -> Self {
                Self(self.0 - other.0)
            }
        }

        impl SubAssign for $name {
            fn sub_assign(&mut self, other: Self) {
                *self = *self - other;
            }
        }
    };
}

/// impl float conversion methods.
///
/// We convert to different float types in order to ensure we can roundtrip
/// without floating point error.
macro_rules! float_conv {
    ($name:ident, $to:ident, $from:ident, $ty:ty) => {
        impl $name {
            #[doc = concat!("Creates a fixed point value from a", stringify!($ty), ".")]
            ///
            /// This operation is lossy; the float will be rounded to the nearest
            /// representable value.
            pub fn $from(x: $ty) -> Self {
                #[cfg(any(feature = "std", test))]
                return Self((x * Self::ONE as $ty).round() as _);
                //NOTE: this behaviour is not exactly equivalent, but should be okay?
                //what matters is that we are rounding *away from zero*.
                #[cfg(all(not(feature = "std"), not(test)))]
                Self(
                    (x * Self::ONE as $ty + (0.5 * (-1.0 * x.is_sign_negative() as u8 as $ty)))
                        as _,
                )
            }

            #[doc = concat!("Returns the value as an ", stringify!($ty), ".")]
            ///
            /// This operation is lossless: all representable values can be
            /// round-tripped.
            pub fn $to(self) -> $ty {
                let int = ((self.0 & Self::INT_MASK) >> Self::FRACT_BITS) as $ty;
                let fract = (self.0 & !Self::INT_MASK) as $ty / Self::ONE as $ty;
                int + fract
            }
        }

        //hack: we can losslessly go to float, so use those fmt impls
        impl std::fmt::Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                self.$to().fmt(f)
            }
        }

        impl std::fmt::Debug for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                self.$to().fmt(f)
            }
        }
    };
}

fixed_impl!(F2Dot14, 16, 14, i16);
fixed_impl!(Fixed, 32, 16, i32);
float_conv!(F2Dot14, to_f32, from_f32, f32);
float_conv!(Fixed, to_f64, from_f64, f64);
crate::newtype_scalar!(F2Dot14, [u8; 2]);
crate::newtype_scalar!(Fixed, [u8; 4]);

impl F2Dot14 {
    /// Converts a 2.14 to 16.16 fixed point value.
    pub fn to_fixed(&self) -> Fixed {
        Fixed(self.0 as i32 * 4)
    }
}

impl Mul for Fixed {
    type Output = Self;
    #[inline(always)]
    fn mul(self, other: Self) -> Self::Output {
        let ab = self.0 as i64 * other.0 as i64;
        Self(((ab + 0x8000 - i64::from(ab < 0)) >> 16) as i32)
    }
}

impl MulAssign for Fixed {
    fn mul_assign(&mut self, rhs: Self) {
        *self = *self * rhs;
    }
}

impl Div for Fixed {
    type Output = Self;
    #[inline(always)]
    fn div(self, other: Self) -> Self::Output {
        let mut sign = 1;
        let mut a = self.0;
        let mut b = other.0;
        if a < 0 {
            a = -a;
            sign = -1;
        }
        if b < 0 {
            b = -b;
            sign = -sign;
        }
        let q = if b == 0 {
            0x7FFFFFFF
        } else {
            ((((a as u64) << 16) + ((b as u64) >> 1)) / (b as u64)) as u32
        };
        Self(if sign < 0 { -(q as i32) } else { q as i32 })
    }
}

impl DivAssign for Fixed {
    fn div_assign(&mut self, rhs: Self) {
        *self = *self / rhs;
    }
}

impl Neg for Fixed {
    type Output = Self;
    #[inline(always)]
    fn neg(self) -> Self {
        Self(-self.0)
    }
}

#[cfg(test)]
mod tests {
    #![allow(overflowing_literals)] // we want to specify byte values directly
    use super::*;

    #[test]
    fn f2dot14_floats() {
        // Examples from https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types
        assert_eq!(F2Dot14(0x7fff), F2Dot14::from_f32(1.999939));
        assert_eq!(F2Dot14(0x7000), F2Dot14::from_f32(1.75));
        assert_eq!(F2Dot14(0x0001), F2Dot14::from_f32(0.0000610356));
        assert_eq!(F2Dot14(0x0000), F2Dot14::from_f32(0.0));
        assert_eq!(F2Dot14(0xffff), F2Dot14::from_f32(-0.000061));
        assert_eq!(F2Dot14(0x8000), F2Dot14::from_f32(-2.0));
    }

    #[test]
    fn roundtrip_f2dot14() {
        for i in i16::MIN..=i16::MAX {
            let val = F2Dot14(i);
            assert_eq!(val, F2Dot14::from_f32(val.to_f32()));
        }
    }

    #[test]
    fn round_f2dot14() {
        assert_eq!(F2Dot14(0x7000).round(), F2Dot14::from_f32(-2.0));
        assert_eq!(F2Dot14(0x1F00).round(), F2Dot14::from_f32(0.0));
        assert_eq!(F2Dot14(0x2000).round(), F2Dot14::from_f32(1.0));
    }

    #[test]
    fn round_fixed() {
        //TODO: make good test cases
        assert_eq!(Fixed(0x0001_7FFE).round(), Fixed(0x0001_0000));
        assert_eq!(Fixed(0x0001_7FFF).round(), Fixed(0x0001_0000));
        assert_eq!(Fixed(0x0001_8000).round(), Fixed(0x0002_0000));
    }

    // disabled because it's slow; these were just for my edification anyway
    //#[test]
    //fn roundtrip_fixed() {
    //for i in i32::MIN..=i32::MAX {
    //let val = Fixed(i);
    //assert_eq!(val, Fixed::from_f64(val.to_f64()));
    //}
    //}

    #[test]
    fn fixed_floats() {
        assert_eq!(Fixed(0x7fff_0000), Fixed::from_f64(32767.));
        assert_eq!(Fixed(0x7000_0001), Fixed::from_f64(28672.00001525879));
        assert_eq!(Fixed(0x0001_0000), Fixed::from_f64(1.0));
        assert_eq!(Fixed(0x0000_0000), Fixed::from_f64(0.0));
        assert_eq!(
            Fixed(i32::from_be_bytes([0xff; 4])),
            Fixed::from_f64(-0.000015259)
        );
        assert_eq!(Fixed(0x7fff_ffff), Fixed::from_f64(32768.0));
    }
}
<commit_msg>add ZERO/ONE associated consts<commit_after>//! fixed-point numerical types

use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};

// shared between Fixed and F2Dot14
macro_rules! fixed_impl {
    ($name:ident, $bits:literal, $fract_bits:literal, $ty:ty) => {
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]
        #[doc = concat!(stringify!($bits), "-bit signed fixed point number with ", stringify!($fract_bits), " bits of fraction." )]
        pub struct $name($ty);
        impl $name {
            /// Minimum value.
            pub const MIN: Self = Self(<$ty>::MIN);

            /// Maximum value.
            pub const MAX: Self = Self(<$ty>::MAX);

            /// This type's smallest representable value
            pub const EPSILON: Self = Self(1);

            /// Representation of 0.0.
            pub const ZERO: Self = Self(0);

            /// Representation of 1.0.
            pub const ONE: Self = Self(1 << $fract_bits);

            const INT_MASK: $ty = !0 << $fract_bits;
            const ROUND: $ty = 1 << ($fract_bits - 1);
            const FRACT_BITS: usize = $fract_bits;

            //TODO: is this actually useful?
            /// Returns the nearest integer value.
            pub fn round(self) -> Self {
                Self(self.0.wrapping_add(Self::ROUND) & Self::INT_MASK)
            }

            /// Returns the absolute value of the number.
            pub fn abs(self) -> Self {
                Self(self.0.abs())
            }

            /// Returns the largest integer less than or equal to the number.
            pub fn floor(self) -> Self {
                Self(self.0 & Self::INT_MASK)
            }

            /// Returns the fractional part of the number.
            pub fn fract(self) -> Self {
                Self(self.0 - self.floor().0)
            }

            /// Wrapping addition.
            pub fn wrapping_add(self, other: Self) -> Self {
                Self(self.0.wrapping_add(other.0))
            }

            /// Saturating addition.
            pub fn saturating_add(self, other: Self) -> Self {
                Self(self.0.saturating_add(other.0))
            }

            /// Wrapping substitution.
            pub fn wrapping_sub(self, other: Self) -> Self {
                Self(self.0.wrapping_sub(other.0))
            }

            /// Saturating substitution.
            pub fn saturating_sub(self, other: Self) -> Self {
                Self(self.0.saturating_sub(other.0))
            }

            /// The representation of this number as a big-endian byte array.
            pub fn to_be_bytes(self) -> [u8; $bits / 8] {
                self.0.to_be_bytes()
            }
        }

        impl Add for $name {
            type Output = Self;
            #[inline(always)]
            fn add(self, other: Self) -> Self {
                // same overflow semantics as std: panic in debug, wrap in release
                Self(self.0 + other.0)
            }
        }

        impl AddAssign for $name {
            fn add_assign(&mut self, other: Self) {
                *self = *self + other;
            }
        }

        impl Sub for $name {
            type Output = Self;
            #[inline(always)]
            fn sub(self, other: Self) -> Self {
                Self(self.0 - other.0)
            }
        }

        impl SubAssign for $name {
            fn sub_assign(&mut self, other: Self) {
                *self = *self - other;
            }
        }
    };
}

/// impl float conversion methods.
///
/// We convert to different float types in order to ensure we can roundtrip
/// without floating point error.
macro_rules! float_conv {
    ($name:ident, $to:ident, $from:ident, $ty:ty) => {
        impl $name {
            #[doc = concat!("Creates a fixed point value from a", stringify!($ty), ".")]
            ///
            /// This operation is lossy; the float will be rounded to the nearest
            /// representable value.
            pub fn $from(x: $ty) -> Self {
                #[cfg(any(feature = "std", test))]
                return Self((x * Self::ONE.0 as $ty).round() as _);
                //NOTE: this behaviour is not exactly equivalent, but should be okay?
                //what matters is that we are rounding *away from zero*.
                #[cfg(all(not(feature = "std"), not(test)))]
                Self(
                    (x * Self::ONE as $ty + (0.5 * (-1.0 * x.is_sign_negative() as u8 as $ty)))
                        as _,
                )
            }

            #[doc = concat!("Returns the value as an ", stringify!($ty), ".")]
            ///
            /// This operation is lossless: all representable values can be
            /// round-tripped.
            pub fn $to(self) -> $ty {
                let int = ((self.0 & Self::INT_MASK) >> Self::FRACT_BITS) as $ty;
                let fract = (self.0 & !Self::INT_MASK) as $ty / Self::ONE.0 as $ty;
                int + fract
            }
        }

        //hack: we can losslessly go to float, so use those fmt impls
        impl std::fmt::Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                self.$to().fmt(f)
            }
        }

        impl std::fmt::Debug for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                self.$to().fmt(f)
            }
        }
    };
}

fixed_impl!(F2Dot14, 16, 14, i16);
fixed_impl!(Fixed, 32, 16, i32);
float_conv!(F2Dot14, to_f32, from_f32, f32);
float_conv!(Fixed, to_f64, from_f64, f64);
crate::newtype_scalar!(F2Dot14, [u8; 2]);
crate::newtype_scalar!(Fixed, [u8; 4]);

impl F2Dot14 {
    /// Converts a 2.14 to 16.16 fixed point value.
    pub fn to_fixed(&self) -> Fixed {
        Fixed(self.0 as i32 * 4)
    }
}

impl Mul for Fixed {
    type Output = Self;
    #[inline(always)]
    fn mul(self, other: Self) -> Self::Output {
        let ab = self.0 as i64 * other.0 as i64;
        Self(((ab + 0x8000 - i64::from(ab < 0)) >> 16) as i32)
    }
}

impl MulAssign for Fixed {
    fn mul_assign(&mut self, rhs: Self) {
        *self = *self * rhs;
    }
}

impl Div for Fixed {
    type Output = Self;
    #[inline(always)]
    fn div(self, other: Self) -> Self::Output {
        let mut sign = 1;
        let mut a = self.0;
        let mut b = other.0;
        if a < 0 {
            a = -a;
            sign = -1;
        }
        if b < 0 {
            b = -b;
            sign = -sign;
        }
        let q = if b == 0 {
            0x7FFFFFFF
        } else {
            ((((a as u64) << 16) + ((b as u64) >> 1)) / (b as u64)) as u32
        };
        Self(if sign < 0 { -(q as i32) } else { q as i32 })
    }
}

impl DivAssign for Fixed {
    fn div_assign(&mut self, rhs: Self) {
        *self = *self / rhs;
    }
}

impl Neg for Fixed {
    type Output = Self;
    #[inline(always)]
    fn neg(self) -> Self {
        Self(-self.0)
    }
}

#[cfg(test)]
mod tests {
    #![allow(overflowing_literals)] // we want to specify byte values directly
    use super::*;

    #[test]
    fn f2dot14_floats() {
        // Examples from https://docs.microsoft.com/en-us/typography/opentype/spec/otff#data-types
        assert_eq!(F2Dot14(0x7fff), F2Dot14::from_f32(1.999939));
        assert_eq!(F2Dot14(0x7000), F2Dot14::from_f32(1.75));
        assert_eq!(F2Dot14(0x0001), F2Dot14::from_f32(0.0000610356));
        assert_eq!(F2Dot14(0x0000), F2Dot14::from_f32(0.0));
        assert_eq!(F2Dot14(0xffff), F2Dot14::from_f32(-0.000061));
        assert_eq!(F2Dot14(0x8000), F2Dot14::from_f32(-2.0));
    }

    #[test]
    fn roundtrip_f2dot14() {
        for i in i16::MIN..=i16::MAX {
            let val = F2Dot14(i);
            assert_eq!(val, F2Dot14::from_f32(val.to_f32()));
        }
    }

    #[test]
    fn round_f2dot14() {
        assert_eq!(F2Dot14(0x7000).round(), F2Dot14::from_f32(-2.0));
        assert_eq!(F2Dot14(0x1F00).round(), F2Dot14::from_f32(0.0));
        assert_eq!(F2Dot14(0x2000).round(), F2Dot14::from_f32(1.0));
    }

    #[test]
    fn round_fixed() {
        //TODO: make good test cases
        assert_eq!(Fixed(0x0001_7FFE).round(), Fixed(0x0001_0000));
        assert_eq!(Fixed(0x0001_7FFF).round(), Fixed(0x0001_0000));
        assert_eq!(Fixed(0x0001_8000).round(), Fixed(0x0002_0000));
    }

    // disabled because it's slow; these were just for my edification anyway
    //#[test]
    //fn roundtrip_fixed() {
    //for i in i32::MIN..=i32::MAX {
    //let val = Fixed(i);
    //assert_eq!(val, Fixed::from_f64(val.to_f64()));
    //}
    //}

    #[test]
    fn fixed_floats() {
        assert_eq!(Fixed(0x7fff_0000), Fixed::from_f64(32767.));
        assert_eq!(Fixed(0x7000_0001), Fixed::from_f64(28672.00001525879));
        assert_eq!(Fixed(0x0001_0000), Fixed::from_f64(1.0));
        assert_eq!(Fixed(0x0000_0000), Fixed::from_f64(0.0));
        assert_eq!(
            Fixed(i32::from_be_bytes([0xff; 4])),
            Fixed::from_f64(-0.000015259)
        );
        assert_eq!(Fixed(0x7fff_ffff), Fixed::from_f64(32768.0));
    }
}
<|endoftext|>