<commit_before>use crate::stream::position::IndexPositioner;

use std::{
    io::{self, Read},
    mem::MaybeUninit,
};

#[cfg(any(feature = "futures-03", feature = "tokio-02"))]
use std::pin::Pin;

use bytes_05::{buf::BufMutExt, Buf, BufMut, BytesMut};

/// Used together with the `decode!` macro
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg_attr(feature = "pin-project", pin_project::pin_project)]
pub struct Decoder<R, S, P = IndexPositioner> {
    #[cfg_attr(feature = "pin-project", pin)]
    reader: R,
    pub positioner: P,
    state: S,
    buffer: BytesMut,
}

#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<R, S, P> Decoder<R, S, P>
where
    P: Default,
    S: Default,
{
    pub fn new(reader: R) -> Self {
        Decoder {
            reader,
            positioner: P::default(),
            state: S::default(),
            buffer: Default::default(),
        }
    }
}

impl<R, S, P> Decoder<R, S, P> {
    #[doc(hidden)]
    pub fn advance(&mut self, removed: usize) {
        // Remove the data we have parsed and adjust `removed` to be the amount of data we
        // committed from `self.reader`
        self.buffer.advance(removed);
    }
}

impl<R, S, P> Decoder<R, S, P>
where
    R: Read,
{
    #[doc(hidden)]
    pub fn before_parse(&mut self) -> io::Result<(&mut S, &mut P, &[u8], bool)> {
        let mut end_of_input = false;

        let take = (8 * 1024).max(self.buffer.remaining_mut() as u64);
        let copied = io::copy(
            &mut (&mut self.reader).take(take),
            &mut (&mut self.buffer).writer(),
        )?;
        if copied == 0 {
            end_of_input = true;
        }
        Ok((
            &mut self.state,
            &mut self.positioner,
            &self.buffer,
            end_of_input,
        ))
    }
}

#[cfg(feature = "tokio-02")]
impl<R, S, P> Decoder<R, S, P>
where
    R: tokio_02_dep::io::AsyncRead,
{
    #[doc(hidden)]
    pub async fn before_parse_tokio(
        self: Pin<&mut Self>,
    ) -> io::Result<(&mut S, &mut P, &[u8], bool)> {
        use tokio_02_dep::io::AsyncReadExt;

        let mut self_ = self.project();
        let mut end_of_input = false;
        if !self_.buffer.has_remaining_mut() {
            self_.buffer.reserve(8 * 1024);
        }
        let copied = self_.reader.read_buf(self_.buffer).await?;
        if copied == 0 {
            end_of_input = true;
        }
        Ok((
            self_.state,
            self_.positioner,
            &self_.buffer[..],
            end_of_input,
        ))
    }
}

#[cfg(feature = "futures-03")]
impl<R, S, P> Decoder<R, S, P>
where
    R: futures_io_03::AsyncRead,
{
    #[doc(hidden)]
    pub async fn before_parse_async(
        self: Pin<&mut Self>,
    ) -> io::Result<(&mut S, &mut P, &[u8], bool)> {
        use futures_util_03::io::AsyncReadExt;

        let mut self_ = self.project();
        if !self_.buffer.has_remaining_mut() {
            self_.buffer.reserve(8 * 1024);
        }
        // Copy of tokio's read_buf method (but it has to force initialize the buffer)
        let copied = unsafe {
            let n = {
                let bs = self_.buffer.bytes_mut();

                for b in &mut *bs {
                    *b = MaybeUninit::new(0);
                }

                // Convert to `&mut [u8]`
                let bs = &mut *(bs as *mut [MaybeUninit<u8>] as *mut [u8]);

                let n = self_.reader.read(bs).await?;
                assert!(n <= bs.len(), "AsyncRead reported that it initialized more than the number of bytes in the buffer");
                n
            };

            self_.buffer.advance_mut(n);
            n
        };

        let end_of_input = copied == 0;
        Ok((
            self_.state,
            self_.positioner,
            &self_.buffer[..],
            end_of_input,
        ))
    }
}
<commit_msg>Fix warning<commit_after>use crate::stream::position::IndexPositioner;

use std::io::{self, Read};

#[cfg(any(feature = "futures-03", feature = "tokio-02"))]
use std::pin::Pin;

use bytes_05::{buf::BufMutExt, Buf, BufMut, BytesMut};

/// Used together with the `decode!` macro
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
#[cfg_attr(feature = "pin-project", pin_project::pin_project)]
pub struct Decoder<R, S, P = IndexPositioner> {
    #[cfg_attr(feature = "pin-project", pin)]
    reader: R,
    pub positioner: P,
    state: S,
    buffer: BytesMut,
}

#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
impl<R, S, P> Decoder<R, S, P>
where
    P: Default,
    S: Default,
{
    pub fn new(reader: R) -> Self {
        Decoder {
            reader,
            positioner: P::default(),
            state: S::default(),
            buffer: Default::default(),
        }
    }
}

impl<R, S, P> Decoder<R, S, P> {
    #[doc(hidden)]
    pub fn advance(&mut self, removed: usize) {
        // Remove the data we have parsed and adjust `removed` to be the amount of data we
        // committed from `self.reader`
        self.buffer.advance(removed);
    }
}

impl<R, S, P> Decoder<R, S, P>
where
    R: Read,
{
    #[doc(hidden)]
    pub fn before_parse(&mut self) -> io::Result<(&mut S, &mut P, &[u8], bool)> {
        let mut end_of_input = false;

        let take = (8 * 1024).max(self.buffer.remaining_mut() as u64);
        let copied = io::copy(
            &mut (&mut self.reader).take(take),
            &mut (&mut self.buffer).writer(),
        )?;
        if copied == 0 {
            end_of_input = true;
        }
        Ok((
            &mut self.state,
            &mut self.positioner,
            &self.buffer,
            end_of_input,
        ))
    }
}

#[cfg(feature = "tokio-02")]
impl<R, S, P> Decoder<R, S, P>
where
    R: tokio_02_dep::io::AsyncRead,
{
    #[doc(hidden)]
    pub async fn before_parse_tokio(
        self: Pin<&mut Self>,
    ) -> io::Result<(&mut S, &mut P, &[u8], bool)> {
        use tokio_02_dep::io::AsyncReadExt;

        let mut self_ = self.project();
        let mut end_of_input = false;
        if !self_.buffer.has_remaining_mut() {
            self_.buffer.reserve(8 * 1024);
        }
        let copied = self_.reader.read_buf(self_.buffer).await?;
        if copied == 0 {
            end_of_input = true;
        }
        Ok((
            self_.state,
            self_.positioner,
            &self_.buffer[..],
            end_of_input,
        ))
    }
}

#[cfg(feature = "futures-03")]
impl<R, S, P> Decoder<R, S, P>
where
    R: futures_io_03::AsyncRead,
{
    #[doc(hidden)]
    pub async fn before_parse_async(
        self: Pin<&mut Self>,
    ) -> io::Result<(&mut S, &mut P, &[u8], bool)> {
        use std::mem::MaybeUninit;

        use futures_util_03::io::AsyncReadExt;

        let mut self_ = self.project();
        if !self_.buffer.has_remaining_mut() {
            self_.buffer.reserve(8 * 1024);
        }
        // Copy of tokio's read_buf method (but it has to force initialize the buffer)
        let copied = unsafe {
            let n = {
                let bs = self_.buffer.bytes_mut();

                for b in &mut *bs {
                    *b = MaybeUninit::new(0);
                }

                // Convert to `&mut [u8]`
                let bs = &mut *(bs as *mut [MaybeUninit<u8>] as *mut [u8]);

                let n = self_.reader.read(bs).await?;
                assert!(n <= bs.len(), "AsyncRead reported that it initialized more than the number of bytes in the buffer");
                n
            };

            self_.buffer.advance_mut(n);
            n
        };

        let end_of_input = copied == 0;
        Ok((
            self_.state,
            self_.positioner,
            &self_.buffer[..],
            end_of_input,
        ))
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::fs::{DirEntry, Metadata};
use std::path::{Components, Path, PathBuf};

use chrono::{DateTime, Local};
use walkdir;
use walkdir::WalkDir;

use error::*;
use times::system_time_to_date_time;

#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct FsNode {
  path: PathBuf,
  basename: String,
  entry: FsEntryType,
  mtime: DateTime<Local>,
  atime: DateTime<Local>,
  birthtime: DateTime<Local>,
}

#[derive(Debug, Serialize)]
pub struct FsRootNode(FsNode);

impl FsRootNode {
  pub fn new() -> Self {
    FsRootNode(FsNode {
      path: PathBuf::from(""),
      basename: String::new(),
      entry: FsEntryType::Directory { children: BTreeMap::new(), },
      mtime: Local::now(),
      atime: Local::now(),
      birthtime: Local::now(),
    })
  }

  pub fn add_root(&mut self, path: &Path) -> FomoResult<()> {

    for entry in WalkDir::new(path) {
      let entry = entry?;
      let mut parent = {
        let components = entry.path().components();
        let path_buffer = PathBuf::new();

        self.0.ensure_and_return_parent(path_buffer, components)?
      };

      {

        let new_node = FsNode::try_from_node_source(entry)?;
        let basename = new_node.basename.to_owned();

        match parent.entry {
          FsEntryType::Directory { ref mut children } => {
            children.insert(basename, new_node);
          }
          _ => panic!("Found a non-directory as the parent of a node."),
        }
      }
    }

    Ok(())
  }
}

impl FsNode {
  /// This should only be called if the node is a root node.
  fn ensure_and_return_parent<'a, 'b>(
    &'a mut self,
    mut path_so_far: PathBuf,
    mut components: Components<'b>
  ) -> FomoResult<&'a mut FsNode> {

    let component = components.next();

    if let Some(c) = component {

      let comp_str = match c.as_os_str().to_str() {
        Some(s) => s,
        None => return Err(FomoError::Internal("non unicode filename found")),
      };
      path_so_far.push(comp_str);

      match self.entry {
        FsEntryType::Directory { ref mut children } => {
          children.entry(comp_str.to_owned())
            .or_insert_with(|| FsNode::empty_dir(path_so_far.clone(), comp_str.to_owned()))
            .ensure_and_return_parent(path_so_far, components)
        }
        _ => panic!("Files should not exist in the middle of a path component."),
      }
    } else {
      return Ok(self);
    }
  }

  pub fn empty_dir(path: PathBuf, dirname: String) -> Self {
    FsNode {
      path: path,
      basename: dirname,
      entry: FsEntryType::Directory { children: BTreeMap::new(), },
      mtime: Local::now(),
      atime: Local::now(),
      birthtime: Local::now(),
    }
  }

  pub fn try_from_node_source<S: NodeSource>(entry: S) -> FomoResult<Self> {

    let basename = match entry.path().file_name() {
      Some(n) => {
        match n.to_str() {
          Some(b) => b.to_owned(),
          None => return Err(FomoError::Internal("non-unicode filename")),
        }
      }
      None => return Err(FomoError::Internal("walkdir gave us a relative path")),
    };

    let metadata = entry.metadata()?;

    let entry_ty = if metadata.is_dir() {
      FsEntryType::Directory { children: BTreeMap::new(), }
    } else if metadata.is_file() {
      FsEntryType::File { len: metadata.len(), }
    } else {
      // FIXME(dikaiosune) this doesn't handle links or other entry types
      unreachable!()
    };

    Ok(FsNode {
      path: entry.path(),
      basename: basename,
      entry: entry_ty,
      mtime: system_time_to_date_time(metadata.modified()?),
      atime: system_time_to_date_time(metadata.accessed()?),
      birthtime: system_time_to_date_time(metadata.created()?),
    })
  }
}

#[derive(Debug, Eq, PartialEq, Serialize)]
pub enum FsEntryType {
  Directory { children: BTreeMap<String, FsNode>, },
  File { len: u64, },
}

pub trait NodeSource {
  fn path(&self) -> PathBuf;
  fn metadata(&self) -> FomoResult<Metadata>;
}

impl NodeSource for walkdir::DirEntry {
  fn path(&self) -> PathBuf {
    self.path().to_path_buf()
  }

  fn metadata(&self) -> FomoResult<Metadata> {
    Ok(self.metadata()?)
  }
}

impl NodeSource for DirEntry {
  fn path(&self) -> PathBuf {
    self.path()
  }

  fn metadata(&self) -> FomoResult<Metadata> {
    Ok(self.metadata()?)
  }
}

#[cfg(test)]
mod test {
  use std::fs::File;
  use std::io::prelude::*;
  use tempdir::TempDir;
  use walkdir::WalkDir;

  use super::{FsEntryType, FsNode};

  #[test]
  fn single_file_tmp_dir() {
    let tmp = TempDir::new("single").expect("unable to create temp directory");
    let tmp_path = tmp.path().join("test-file");
    let mut tmp_file = File::create(&tmp_path).expect("unable to create test file");

    let mut traversed = 0;
    for entry in WalkDir::new(&tmp_path) {
      let entry = entry.expect("walking directory");
      let node = FsNode::try_from_node_source(entry).expect("creating node");
      assert_eq!(node.basename, "test-file");
      assert_eq!(node.path, tmp_path);
      assert_eq!(node.entry, FsEntryType::File { len: 0, });
      traversed += 1;
    }
    assert_eq!(traversed, 1);

    write!(tmp_file, "7 bytes").expect("writing to temp file");

    traversed = 0;
    for entry in WalkDir::new(&tmp_path) {
      let entry = entry.expect("walking directory");
      let node = FsNode::try_from_node_source(entry).expect("creating node");
      assert_eq!(node.basename, "test-file");
      assert_eq!(node.path, tmp_path);
      assert_eq!(node.entry, FsEntryType::File { len: 7, });
      traversed += 1;
    }
    assert_eq!(traversed, 1);
  }
}
<commit_msg>[fomo] Remove birthtime/created from FsNode.<commit_after>use std::collections::BTreeMap;
use std::fs::{DirEntry, Metadata};
use std::path::{Components, Path, PathBuf};

use chrono::{DateTime, Local};
use walkdir;
use walkdir::WalkDir;

use error::*;
use times::system_time_to_date_time;

#[derive(Debug, Eq, PartialEq, Serialize)]
pub struct FsNode {
  path: PathBuf,
  basename: String,
  entry: FsEntryType,
  mtime: DateTime<Local>,
}

#[derive(Debug, Serialize)]
pub struct FsRootNode(FsNode);

impl FsRootNode {
  pub fn new() -> Self {
    FsRootNode(FsNode {
      path: PathBuf::from(""),
      basename: String::new(),
      entry: FsEntryType::Directory { children: BTreeMap::new(), },
      mtime: Local::now(),
    })
  }

  pub fn add_root(&mut self, path: &Path) -> FomoResult<()> {

    for entry in WalkDir::new(path) {
      let entry = entry?;
      let mut parent = {
        let components = entry.path().components();
        let path_buffer = PathBuf::new();

        self.0.ensure_and_return_parent(path_buffer, components)?
      };

      {

        let new_node = FsNode::try_from_node_source(entry)?;
        let basename = new_node.basename.to_owned();

        match parent.entry {
          FsEntryType::Directory { ref mut children } => {
            children.insert(basename, new_node);
          }
          _ => panic!("Found a non-directory as the parent of a node."),
        }
      }
    }

    Ok(())
  }
}

impl FsNode {
  /// This should only be called if the node is a root node.
  fn ensure_and_return_parent<'a, 'b>(
    &'a mut self,
    mut path_so_far: PathBuf,
    mut components: Components<'b>
  ) -> FomoResult<&'a mut FsNode> {

    let component = components.next();

    if let Some(c) = component {

      let comp_str = match c.as_os_str().to_str() {
        Some(s) => s,
        None => return Err(FomoError::Internal("non unicode filename found")),
      };
      path_so_far.push(comp_str);

      match self.entry {
        FsEntryType::Directory { ref mut children } => {
          children.entry(comp_str.to_owned())
            .or_insert_with(|| FsNode::empty_dir(path_so_far.clone(), comp_str.to_owned()))
            .ensure_and_return_parent(path_so_far, components)
        }
        _ => panic!("Files should not exist in the middle of a path component."),
      }
    } else {
      return Ok(self);
    }
  }

  pub fn empty_dir(path: PathBuf, dirname: String) -> Self {
    FsNode {
      path: path,
      basename: dirname,
      entry: FsEntryType::Directory { children: BTreeMap::new(), },
      mtime: Local::now(),
    }
  }

  pub fn try_from_node_source<S: NodeSource>(entry: S) -> FomoResult<Self> {

    let basename = match entry.path().file_name() {
      Some(n) => {
        match n.to_str() {
          Some(b) => b.to_owned(),
          None => return Err(FomoError::Internal("non-unicode filename")),
        }
      }
      None => return Err(FomoError::Internal("walkdir gave us a relative path")),
    };

    let metadata = entry.metadata()?;

    let entry_ty = if metadata.is_dir() {
      FsEntryType::Directory { children: BTreeMap::new(), }
    } else if metadata.is_file() {
      FsEntryType::File { len: metadata.len(), }
    } else {
      // FIXME(dikaiosune) this doesn't handle links or other entry types
      unreachable!()
    };

    Ok(FsNode {
      path: entry.path(),
      basename: basename,
      entry: entry_ty,
      mtime: system_time_to_date_time(metadata.modified()?),
    })
  }
}

#[derive(Debug, Eq, PartialEq, Serialize)]
pub enum FsEntryType {
  Directory { children: BTreeMap<String, FsNode>, },
  File { len: u64, },
}

pub trait NodeSource {
  fn path(&self) -> PathBuf;
  fn metadata(&self) -> FomoResult<Metadata>;
}

impl NodeSource for walkdir::DirEntry {
  fn path(&self) -> PathBuf {
    self.path().to_path_buf()
  }

  fn metadata(&self) -> FomoResult<Metadata> {
    Ok(self.metadata()?)
  }
}

impl NodeSource for DirEntry {
  fn path(&self) -> PathBuf {
    self.path()
  }

  fn metadata(&self) -> FomoResult<Metadata> {
    Ok(self.metadata()?)
  }
}

#[cfg(test)]
mod test {
  use std::fs::File;
  use std::io::prelude::*;
  use tempdir::TempDir;
  use walkdir::WalkDir;

  use super::{FsEntryType, FsNode};

  #[test]
  fn single_file_tmp_dir() {
    let tmp = TempDir::new("single").expect("unable to create temp directory");
    let tmp_path = tmp.path().join("test-file");
    let mut tmp_file = File::create(&tmp_path).expect("unable to create test file");

    let mut traversed = 0;
    for entry in WalkDir::new(&tmp_path) {
      let entry = entry.expect("walking directory");
      let node = FsNode::try_from_node_source(entry).expect("creating node");
      assert_eq!(node.basename, "test-file");
      assert_eq!(node.path, tmp_path);
      assert_eq!(node.entry, FsEntryType::File { len: 0, });
      traversed += 1;
    }
    assert_eq!(traversed, 1);

    write!(tmp_file, "7 bytes").expect("writing to temp file");

    traversed = 0;
    for entry in WalkDir::new(&tmp_path) {
      let entry = entry.expect("walking directory");
      let node = FsNode::try_from_node_source(entry).expect("creating node");
      assert_eq!(node.basename, "test-file");
      assert_eq!(node.path, tmp_path);
      assert_eq!(node.entry, FsEntryType::File { len: 7, });
      traversed += 1;
    }
    assert_eq!(traversed, 1);
  }
}
<|endoftext|>


<commit_before>use character::controls::CharacterControl;
use game::constants::{RESOLUTION_X, RESOLUTION_Y};
use gfx;
use gfx_app::controls::{Control, TilemapControls};
use gfx_device_gl;
use gfx_window_glutin;
use glutin;
use glutin::{GlContext, KeyboardInput, MouseButton};
use glutin::ElementState::{Pressed, Released};
use glutin::VirtualKeyCode::{A, D, Escape, R, S, W, X, Z};
use glutin::dpi::{LogicalSize, PhysicalSize};

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct WindowContext {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl WindowContext {
  pub fn new() -> WindowContext {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Hinterland");

    let builder = if cfg!(feature = "windowed") {
      let logical_size = LogicalSize::new(RESOLUTION_X.into(), RESOLUTION_Y.into());
      window_title
        .with_dimensions(logical_size)
        .with_decorations(false)
        .with_min_dimensions(logical_size)
        .with_max_dimensions(logical_size)
    } else {
      let monitor = {
        events_loop.get_available_monitors().nth(0).expect("No monitor found")
      };
      let monitor_resolution = monitor.get_dimensions();

      let resolution = ((monitor_resolution.width as f32 * 16.0 / 9.0) as u32, monitor_resolution.height);

      let logical_size = LogicalSize::new(resolution.0.into(), resolution.1);
      window_title.with_fullscreen(Some(monitor))
                  .with_decorations(false)
                  .with_dimensions(logical_size)
    };

    let context = glutin::ContextBuilder::new()
      .with_vsync(true)
      .with_double_buffer(Some(true))
      .with_pixel_format(24, 8);

    let (window, device, factory, rtv, dsv) =
      gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop);

    WindowContext {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0),
    }
  }
}

#[derive(Debug, PartialEq, Eq)]
pub enum WindowStatus {
  Open,
  Close,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);
  fn get_viewport_size(&mut self) -> (f32, f32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_hidpi_factor(&mut self) -> f32;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
  fn poll_events(&mut self) -> WindowStatus;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for WindowContext {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
        .swap_buffers()
        .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (f32, f32) {
    if cfg!(feature = "windowed") {
      (RESOLUTION_X as f32, RESOLUTION_Y as f32)
    } else {
      let monitor = self.events_loop.get_available_monitors().nth(0).expect("No monitor found");
      let monitor_resolution = monitor.get_dimensions();
      (monitor_resolution.width as f32, monitor_resolution.height as f32)
    }
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_hidpi_factor(&mut self) -> f32 {
    self.window.get_hidpi_factor() as f32
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }

  fn poll_events(&mut self) -> WindowStatus {
    use glutin::WindowEvent::{CursorMoved, CloseRequested, MouseInput};

    // Hack for MacOS 10.14
    let resolution = self.get_viewport_size();
    self.window.resize(PhysicalSize::new(resolution.0.into(), resolution.1.into()));

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };

    let m_pos = &mut self.mouse_pos;
    let mut game_status = WindowStatus::Open;

    self.events_loop.poll_events(|event| {
      game_status = if let glutin::Event::WindowEvent { event, .. } = event {
        match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => { process_keyboard_input(input, controls) },
          MouseInput { state: Pressed, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(None);
            WindowStatus::Open
          }
          MouseInput { state: Pressed, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(None);
            WindowStatus::Open
          }
          CursorMoved { position, .. } => {
            *m_pos = ((position.x as f32).into(), (position.y as f32).into());
            WindowStatus::Open
          }
          CloseRequested => WindowStatus::Close,
          _ => WindowStatus::Open,
        }
      } else {
        WindowStatus::Open
      };
    });
    game_status
  }
}

fn process_keyboard_input(input: glutin::KeyboardInput, controls: &mut TilemapControls) -> WindowStatus {
  match input {
    KeyboardInput { state: Pressed, virtual_keycode: Some(Z), .. } => {
      controls.zoom(&Control::Negative);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(X), .. } => {
      controls.zoom(&Control::Plus);
    }
    KeyboardInput { state: Released, virtual_keycode: Some(Z), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(X), .. } => {
      controls.zoom(&Control::Released);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(W), .. } => {
      controls.move_character(CharacterControl::Up);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(S), .. } => {
      controls.move_character(CharacterControl::Down);
    }
    KeyboardInput { state: Released, virtual_keycode: Some(W), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(S), .. } => {
      controls.move_character(CharacterControl::YMoveStop);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(A), .. } => {
      controls.move_character(CharacterControl::Left);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(D), .. } => {
      controls.move_character(CharacterControl::Right);
    }
    KeyboardInput { state: Released, virtual_keycode: Some(A), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(D), .. } => {
      controls.move_character(CharacterControl::XMoveStop);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(R), .. } => {
      controls.reload_weapon(true);
    },
    KeyboardInput { state: Released, virtual_keycode: Some(R), .. } => {
      controls.reload_weapon(false);
    },
    KeyboardInput { state: Pressed, modifiers, .. } => {
      if modifiers.ctrl {
        controls.ctrl_pressed(true);
      }
    }
    KeyboardInput { state: Released, modifiers, .. } => {
      if !modifiers.ctrl {
        controls.ctrl_pressed(false);
      }
    }
  }
  if let Some(Escape) = input.virtual_keycode {
    WindowStatus::Close
  } else {
    WindowStatus::Open
  }
}

<commit_msg>Remove min/max dimensions from windowed mode<commit_after>use character::controls::CharacterControl;
use game::constants::{RESOLUTION_X, RESOLUTION_Y};
use gfx;
use gfx_app::controls::{Control, TilemapControls};
use gfx_device_gl;
use gfx_window_glutin;
use glutin;
use glutin::{GlContext, KeyboardInput, MouseButton};
use glutin::ElementState::{Pressed, Released};
use glutin::VirtualKeyCode::{A, D, Escape, R, S, W, X, Z};
use glutin::dpi::{LogicalSize, PhysicalSize};

pub mod init;
pub mod renderer;
pub mod system;
pub mod controls;
pub mod mouse_controls;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

pub struct WindowContext {
  window: glutin::GlWindow,
  controls: Option<controls::TilemapControls>,
  events_loop: glutin::EventsLoop,
  device: gfx_device_gl::Device,
  factory: gfx_device_gl::Factory,
  render_target_view: gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat>,
  depth_stencil_view: gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat>,
  mouse_pos: (f64, f64),
}

impl WindowContext {
  pub fn new() -> WindowContext {
    let events_loop = glutin::EventsLoop::new();

    let window_title = glutin::WindowBuilder::new()
      .with_title("Hinterland");

    let builder = if cfg!(feature = "windowed") {
      let logical_size = LogicalSize::new(RESOLUTION_X.into(), RESOLUTION_Y.into());
      window_title
        .with_dimensions(logical_size)
        .with_decorations(false)
    } else {
      let monitor = {
        events_loop.get_available_monitors().nth(0).expect("No monitor found")
      };
      let monitor_resolution = monitor.get_dimensions();

      let resolution = ((monitor_resolution.width as f32 * 16.0 / 9.0) as u32, monitor_resolution.height);

      let logical_size = LogicalSize::new(resolution.0.into(), resolution.1);
      window_title.with_fullscreen(Some(monitor))
                  .with_decorations(false)
                  .with_dimensions(logical_size)
    };

    let context = glutin::ContextBuilder::new()
      .with_vsync(true)
      .with_double_buffer(Some(true))
      .with_pixel_format(24, 8);

    let (window, device, factory, rtv, dsv) =
      gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder, context, &events_loop);

    WindowContext {
      window,
      controls: None,
      events_loop,
      device,
      factory,
      render_target_view: rtv,
      depth_stencil_view: dsv,
      mouse_pos: (0.0, 0.0),
    }
  }
}

#[derive(Debug, PartialEq, Eq)]
pub enum WindowStatus {
  Open,
  Close,
}

pub trait Window<D: gfx::Device, F: gfx::Factory<D::Resources>> {
  fn swap_window(&mut self);
  fn create_buffers(&mut self, count: usize) -> Vec<D::CommandBuffer>;
  fn set_controls(&mut self, controls: controls::TilemapControls);
  fn get_viewport_size(&mut self) -> (f32, f32);
  fn get_device(&mut self) -> &mut D;
  fn get_factory(&mut self) -> &mut F;
  fn get_hidpi_factor(&mut self) -> f32;
  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<D::Resources, ColorFormat>;
  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<D::Resources, DepthFormat>;
  fn poll_events(&mut self) -> WindowStatus;
}

impl Window<gfx_device_gl::Device, gfx_device_gl::Factory> for WindowContext {
  fn swap_window(&mut self) {
    use gfx::Device;
    self.window
        .swap_buffers()
        .expect("Unable to swap buffers");
    self.device.cleanup();
  }

  fn create_buffers(&mut self, count: usize) -> Vec<gfx_device_gl::CommandBuffer> {
    let mut bufs = Vec::new();
    for _ in 0..count {
      bufs.push(self.factory.create_command_buffer());
    }
    bufs
  }

  fn set_controls(&mut self, controls: controls::TilemapControls) {
    self.controls = Some(controls);
  }

  fn get_viewport_size(&mut self) -> (f32, f32) {
    if cfg!(feature = "windowed") {
      (RESOLUTION_X as f32, RESOLUTION_Y as f32)
    } else {
      let monitor = self.events_loop.get_available_monitors().nth(0).expect("No monitor found");
      let monitor_resolution = monitor.get_dimensions();
      (monitor_resolution.width as f32, monitor_resolution.height as f32)
    }
  }

  fn get_device(&mut self) -> &mut gfx_device_gl::Device {
    &mut self.device
  }

  fn get_factory(&mut self) -> &mut gfx_device_gl::Factory {
    &mut self.factory
  }

  fn get_hidpi_factor(&mut self) -> f32 {
    self.window.get_hidpi_factor() as f32
  }

  fn get_render_target_view(&mut self) -> gfx::handle::RenderTargetView<gfx_device_gl::Resources, ColorFormat> {
    self.render_target_view.clone()
  }

  fn get_depth_stencil_view(&mut self) -> gfx::handle::DepthStencilView<gfx_device_gl::Resources, DepthFormat> {
    self.depth_stencil_view.clone()
  }

  fn poll_events(&mut self) -> WindowStatus {
    use glutin::WindowEvent::{CursorMoved, CloseRequested, MouseInput};

    // Hack for MacOS 10.14
    let resolution = self.get_viewport_size();
    self.window.resize(PhysicalSize::new(resolution.0.into(), resolution.1.into()));

    let controls = match self.controls {
      Some(ref mut c) => c,
      None => panic!("Terrain controls have not been initialized"),
    };

    let m_pos = &mut self.mouse_pos;
    let mut game_status = WindowStatus::Open;

    self.events_loop.poll_events(|event| {
      game_status = if let glutin::Event::WindowEvent { event, .. } = event {
        match event {
          glutin::WindowEvent::KeyboardInput { input, .. } => { process_keyboard_input(input, controls) },
          MouseInput { state: Pressed, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Left, .. } => {
            controls.mouse_left_click(None);
            WindowStatus::Open
          }
          MouseInput { state: Pressed, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(Some(*m_pos));
            WindowStatus::Open
          }
          MouseInput { state: Released, button: MouseButton::Right, .. } => {
            controls.mouse_right_click(None);
            WindowStatus::Open
          }
          CursorMoved { position, .. } => {
            *m_pos = ((position.x as f32).into(), (position.y as f32).into());
            WindowStatus::Open
          }
          CloseRequested => WindowStatus::Close,
          _ => WindowStatus::Open,
        }
      } else {
        WindowStatus::Open
      };
    });
    game_status
  }
}

fn process_keyboard_input(input: glutin::KeyboardInput, controls: &mut TilemapControls) -> WindowStatus {
  match input {
    KeyboardInput { state: Pressed, virtual_keycode: Some(Z), .. } => {
      controls.zoom(&Control::Negative);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(X), .. } => {
      controls.zoom(&Control::Plus);
    }
    KeyboardInput { state: Released, virtual_keycode: Some(Z), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(X), .. } => {
      controls.zoom(&Control::Released);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(W), .. } => {
      controls.move_character(CharacterControl::Up);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(S), .. } => {
      controls.move_character(CharacterControl::Down);
    }
    KeyboardInput { state: Released, virtual_keycode: Some(W), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(S), .. } => {
      controls.move_character(CharacterControl::YMoveStop);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(A), .. } => {
      controls.move_character(CharacterControl::Left);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(D), .. } => {
      controls.move_character(CharacterControl::Right);
    }
    KeyboardInput { state: Released, virtual_keycode: Some(A), .. } |
    KeyboardInput { state: Released, virtual_keycode: Some(D), .. } => {
      controls.move_character(CharacterControl::XMoveStop);
    }
    KeyboardInput { state: Pressed, virtual_keycode: Some(R), .. } => {
      controls.reload_weapon(true);
    },
    KeyboardInput { state: Released, virtual_keycode: Some(R), .. } => {
      controls.reload_weapon(false);
    },
    KeyboardInput { state: Pressed, modifiers, .. } => {
      if modifiers.ctrl {
        controls.ctrl_pressed(true);
      }
    }
    KeyboardInput { state: Released, modifiers, .. } => {
      if !modifiers.ctrl {
        controls.ctrl_pressed(false);
      }
    }
  }
  if let Some(Escape) = input.virtual_keycode {
    WindowStatus::Close
  } else {
    WindowStatus::Open
  }
}

<|endoftext|>


<commit_before>// Copyright 2022 Google LLC
//
// 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
//
//    https://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.

use cxx::{memory::UniquePtrTarget, UniquePtr};
use moveit::{CopyNew, New};
use std::{marker::PhantomPinned, mem::MaybeUninit, pin::Pin};

/// A trait representing a parameter to a C++ function which is received
/// by value.
///
/// Rust has the concept of receiving parameters by _move_ or by _reference_.
/// C++ has the concept of receiving a parameter by 'value', which means
/// the parameter gets copied.
///
/// To make it easy to pass such parameters from Rust, this trait exists.
/// It is implemented both for references `&T` and for `UniquePtr<T>`,
/// subject to the presence or absence of suitable copy and move constructors.
/// This allows you to pass in parameters by copy (as is ergonomic and normal
/// in C++) retaining the original parameter; or by move semantics thus
/// destroying the object you're passing in. Simply use a reference if you want
/// copy semantics, or the item itself if you want move semantics.
///
/// It is not recommended that you implement this trait, nor that you directly
/// use its methods, which are for use by `autocxx` generated code only.
///
/// # Use of `moveit` traits
///
/// Most of the implementations of this trait require the type to implement
/// [`CopyNew`], which is simply the `autocxx`/`moveit` way of saying that
/// the type has a copy constructor in C++.
///
/// # Performance
///
/// At present, some additional copying occurs for all implementations of
/// this trait other than that for [`cxx::UniquePtr`]. In the future it's
/// hoped that the implementation for `&T where T: CopyNew` can also avoid
/// this extra copying.
///
/// # Panics
///
/// The implementations of this trait which take a [`cxx::UniquePtr`] will
/// panic if the pointer is NULL.
///
/// # Safety
///
/// Implementations of this trait must guarantee that `StackStorage` is the
/// same alignment and size as T, so long as `needs_stack_space` returns true.
/// Otherwise `StackStorage` is unused.
pub unsafe trait ValueParam<T> {
    /// Any stack storage required. If, as part of passing to C++,
    /// we need to store a temporary copy of the value, this will be `T`,
    /// otherwise `()`.
    #[doc(hidden)]
    type StackStorage;
    /// Whether this `ValueParam` requires temporary Rust-side storage of
    /// an extra copy of this value.
    #[doc(hidden)]
    fn needs_stack_space(&self) -> bool;
    /// Populate the stack storage given as a parameter. Only called if you
    /// return `true` from `needs_stack_space`.
    ///
    /// # Safety
    ///
    /// This is unsafe because callers must call this exactly once, and once
    /// only, to avoid reinitializing the 'this' parameter.
    #[doc(hidden)]
    unsafe fn populate_stack_space(&self, this: Pin<&mut MaybeUninit<Self::StackStorage>>);
    /// Return a pointer to the storage.
    /// Only called if `needs_stack_space` returns `false`, otherwise the pointer
    /// to the stack space will be used.
    /// Note that this returns a _mutable_ pointer. This is a big deal. That's
    /// because, on the C++ side, we'll call `std::move(*ptr)` on this pointer.
    /// This is unlikely to be semantically acceptable long-term, but currently
    /// makes it 'quicker' when a [`cxx::UniquePtr`] is used as a value parameter.
    #[doc(hidden)]
    fn get_ptr(&mut self) -> *mut T;
}

unsafe impl<T> ValueParam<T> for &T
where
    T: CopyNew,
{
    type StackStorage = T;

    fn needs_stack_space(&self) -> bool {
        true
    }

    unsafe fn populate_stack_space(&self, this: Pin<&mut MaybeUninit<Self::StackStorage>>) {
        crate::moveit::new::copy(*self).new(this)
    }

    fn get_ptr(&mut self) -> *mut T {
        unreachable!()
    }
}

unsafe impl<T> ValueParam<T> for UniquePtr<T>
where
    T: UniquePtrTarget,
{
    type StackStorage = ();

    fn needs_stack_space(&self) -> bool {
        false
    }

    unsafe fn populate_stack_space(&self, _: Pin<&mut MaybeUninit<Self::StackStorage>>) {}

    fn get_ptr(&mut self) -> *mut T {
        (unsafe {
            Pin::into_inner_unchecked(
                self.as_mut()
                    .expect("Passed a NULL UniquePtr as a C++ value parameter"),
            )
        }) as *mut T
    }
}

unsafe impl<T> ValueParam<T> for &UniquePtr<T>
where
    T: UniquePtrTarget + CopyNew,
{
    type StackStorage = T;

    fn needs_stack_space(&self) -> bool {
        true
    }

    unsafe fn populate_stack_space(&self, this: Pin<&mut MaybeUninit<Self::StackStorage>>) {
        // Invoke a copy constructor on the Rust side, because we'll use std::move on the C++
        // side.
        crate::moveit::new::copy(
            self.as_ref()
                .expect("Passed a NULL &UniquePtr as a C++ value parameter"),
        )
        .new(this)
    }

    fn get_ptr(&mut self) -> *mut T {
        unreachable!()
    }
}

/// Implementation detail for how we pass value parameters into C++.
/// This type is instantiated by auto-generated autocxx code each time we
/// need to pass a value parameter into C++, and will take responsibility
/// for extracting that value parameter from the [`ValueParam`] and doing
/// any later cleanup.
#[doc(hidden)]
pub struct ValueParamHandler<T, VP: ValueParam<T>> {
    param: VP,
    space: Option<MaybeUninit<VP::StackStorage>>,
    _pinned: PhantomPinned,
}

impl<T, VP: ValueParam<T>> ValueParamHandler<T, VP> {
    /// Create a new storage space for something that's about to be passed
    /// by value to C++. Depending on the `ValueParam` type passed in,
    /// this may be largely a no-op or it may involve storing a whole
    /// extra copy of the type.
    pub fn new(param: VP) -> Self {
        let mut this = Self {
            param,
            space: None,
            _pinned: PhantomPinned,
        };
        if this.param.needs_stack_space() {
            this.space = Some(MaybeUninit::uninit());
        }
        this
    }

    /// Populate this stack space if needs be.
    ///
    /// # Safety
    ///
    /// Callers must guarantee that this type will not move
    /// in memory between calls to [`populate`] and [`get_ptr`].
    /// Callers must call this exactly once prior to calling [`get_ptr`].
    pub unsafe fn populate(&mut self) {
        if self.param.needs_stack_space() {
            self.param
                .populate_stack_space(Pin::new_unchecked(self.space.as_mut().unwrap()));
        }
    }

    /// Return a pointer to the underlying value which can be passed to C++.
    /// Per the unsafety contract of [`populate`], the object must not have moved
    /// since it was created, and [`populate`] has been called exactly once
    /// prior to this call.
    pub fn get_ptr(&mut self) -> *mut T {
        if let Some(ref mut space) = self.space {
            // Safety: 'space' is guaranteed to be populated due to the unsafety
            // contract of 'populate'.
            let ptr =
                unsafe { space.assume_init_mut() } as *mut <VP as ValueParam<T>>::StackStorage;
            // Safety: per the unsafety contract of ValueParam, <VP as ValueParam<T>>::StackStorage
            // is guanteed to == T in the case that needs_stack_space returns true.
            unsafe { std::mem::transmute(ptr) }
        } else {
            self.param.get_ptr()
        }
    }
}

impl<T, VP: ValueParam<T>> Drop for ValueParamHandler<T, VP> {
    fn drop(&mut self) {
        if let Some(space) = self.space.take() {
            unsafe { space.assume_init() };
        }
    }
}
<commit_msg>ValueParam::drop moved a value => corruption.<commit_after>// Copyright 2022 Google LLC
//
// 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
//
//    https://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.

use cxx::{memory::UniquePtrTarget, UniquePtr};
use moveit::{CopyNew, New};
use std::{marker::PhantomPinned, mem::MaybeUninit, pin::Pin};

/// A trait representing a parameter to a C++ function which is received
/// by value.
///
/// Rust has the concept of receiving parameters by _move_ or by _reference_.
/// C++ has the concept of receiving a parameter by 'value', which means
/// the parameter gets copied.
///
/// To make it easy to pass such parameters from Rust, this trait exists.
/// It is implemented both for references `&T` and for `UniquePtr<T>`,
/// subject to the presence or absence of suitable copy and move constructors.
/// This allows you to pass in parameters by copy (as is ergonomic and normal
/// in C++) retaining the original parameter; or by move semantics thus
/// destroying the object you're passing in. Simply use a reference if you want
/// copy semantics, or the item itself if you want move semantics.
///
/// It is not recommended that you implement this trait, nor that you directly
/// use its methods, which are for use by `autocxx` generated code only.
///
/// # Use of `moveit` traits
///
/// Most of the implementations of this trait require the type to implement
/// [`CopyNew`], which is simply the `autocxx`/`moveit` way of saying that
/// the type has a copy constructor in C++.
///
/// # Performance
///
/// At present, some additional copying occurs for all implementations of
/// this trait other than that for [`cxx::UniquePtr`]. In the future it's
/// hoped that the implementation for `&T where T: CopyNew` can also avoid
/// this extra copying.
///
/// # Panics
///
/// The implementations of this trait which take a [`cxx::UniquePtr`] will
/// panic if the pointer is NULL.
///
/// # Safety
///
/// Implementations of this trait must guarantee that `StackStorage` is the
/// same alignment and size as T, so long as `needs_stack_space` returns true.
/// Otherwise `StackStorage` is unused.
pub unsafe trait ValueParam<T> {
    /// Any stack storage required. If, as part of passing to C++,
    /// we need to store a temporary copy of the value, this will be `T`,
    /// otherwise `()`.
    #[doc(hidden)]
    type StackStorage;
    /// Whether this `ValueParam` requires temporary Rust-side storage of
    /// an extra copy of this value.
    #[doc(hidden)]
    fn needs_stack_space(&self) -> bool;
    /// Populate the stack storage given as a parameter. Only called if you
    /// return `true` from `needs_stack_space`.
    ///
    /// # Safety
    ///
    /// This is unsafe because callers must call this exactly once, and once
    /// only, to avoid reinitializing the 'this' parameter.
    #[doc(hidden)]
    unsafe fn populate_stack_space(&self, this: Pin<&mut MaybeUninit<Self::StackStorage>>);
    /// Return a pointer to the storage.
    /// Only called if `needs_stack_space` returns `false`, otherwise the pointer
    /// to the stack space will be used.
    /// Note that this returns a _mutable_ pointer. This is a big deal. That's
    /// because, on the C++ side, we'll call `std::move(*ptr)` on this pointer.
    /// This is unlikely to be semantically acceptable long-term, but currently
    /// makes it 'quicker' when a [`cxx::UniquePtr`] is used as a value parameter.
    #[doc(hidden)]
    fn get_ptr(&mut self) -> *mut T;
}

unsafe impl<T> ValueParam<T> for &T
where
    T: CopyNew,
{
    type StackStorage = T;

    fn needs_stack_space(&self) -> bool {
        true
    }

    unsafe fn populate_stack_space(&self, this: Pin<&mut MaybeUninit<Self::StackStorage>>) {
        crate::moveit::new::copy(*self).new(this)
    }

    fn get_ptr(&mut self) -> *mut T {
        unreachable!()
    }
}

unsafe impl<T> ValueParam<T> for UniquePtr<T>
where
    T: UniquePtrTarget,
{
    type StackStorage = ();

    fn needs_stack_space(&self) -> bool {
        false
    }

    unsafe fn populate_stack_space(&self, _: Pin<&mut MaybeUninit<Self::StackStorage>>) {}

    fn get_ptr(&mut self) -> *mut T {
        (unsafe {
            Pin::into_inner_unchecked(
                self.as_mut()
                    .expect("Passed a NULL UniquePtr as a C++ value parameter"),
            )
        }) as *mut T
    }
}

unsafe impl<T> ValueParam<T> for &UniquePtr<T>
where
    T: UniquePtrTarget + CopyNew,
{
    type StackStorage = T;

    fn needs_stack_space(&self) -> bool {
        true
    }

    unsafe fn populate_stack_space(&self, this: Pin<&mut MaybeUninit<Self::StackStorage>>) {
        // Invoke a copy constructor on the Rust side, because we'll use std::move on the C++
        // side.
        crate::moveit::new::copy(
            self.as_ref()
                .expect("Passed a NULL &UniquePtr as a C++ value parameter"),
        )
        .new(this)
    }

    fn get_ptr(&mut self) -> *mut T {
        unreachable!()
    }
}

/// Implementation detail for how we pass value parameters into C++.
/// This type is instantiated by auto-generated autocxx code each time we
/// need to pass a value parameter into C++, and will take responsibility
/// for extracting that value parameter from the [`ValueParam`] and doing
/// any later cleanup.
#[doc(hidden)]
pub struct ValueParamHandler<T, VP: ValueParam<T>> {
    param: VP,
    space: Option<MaybeUninit<VP::StackStorage>>,
    _pinned: PhantomPinned,
}

impl<T, VP: ValueParam<T>> ValueParamHandler<T, VP> {
    /// Create a new storage space for something that's about to be passed
    /// by value to C++. Depending on the `ValueParam` type passed in,
    /// this may be largely a no-op or it may involve storing a whole
    /// extra copy of the type.
    pub fn new(param: VP) -> Self {
        let mut this = Self {
            param,
            space: None,
            _pinned: PhantomPinned,
        };
        if this.param.needs_stack_space() {
            this.space = Some(MaybeUninit::uninit());
        }
        this
    }

    /// Populate this stack space if needs be.
    ///
    /// # Safety
    ///
    /// Callers must guarantee that this type will not move
    /// in memory between calls to [`populate`] and [`get_ptr`].
    /// Callers must call this exactly once prior to calling [`get_ptr`].
    pub unsafe fn populate(&mut self) {
        if self.param.needs_stack_space() {
            self.param
                .populate_stack_space(Pin::new_unchecked(self.space.as_mut().unwrap()));
        }
    }

    /// Return a pointer to the underlying value which can be passed to C++.
    /// Per the unsafety contract of [`populate`], the object must not have moved
    /// since it was created, and [`populate`] has been called exactly once
    /// prior to this call.
    pub fn get_ptr(&mut self) -> *mut T {
        if let Some(space) = &mut self.space {
            // Safety: 'space' is guaranteed to be populated due to the unsafety
            // contract of 'populate'.
            let ptr =
                unsafe { space.assume_init_mut() } as *mut <VP as ValueParam<T>>::StackStorage;
            // Safety: per the unsafety contract of ValueParam, <VP as ValueParam<T>>::StackStorage
            // is guanteed to == T in the case that needs_stack_space returns true.
            unsafe { std::mem::transmute(ptr) }
        } else {
            self.param.get_ptr()
        }
    }
}

impl<T, VP: ValueParam<T>> Drop for ValueParamHandler<T, VP> {
    fn drop(&mut self) {
        if let Some(space) = self.space.as_mut() {
            // Switch to MaybeUninit::assume_init_drop when stabilized
            unsafe { std::ptr::drop_in_place(space.assume_init_mut()) };
        }
    }
}
<|endoftext|>


<commit_before>//! Module holding a lazy implementation of a NIfTI volume.

use super::util::coords_to_index;
use super::NiftiVolume;
use byteordered::{ByteOrdered, Endianness};
use error::{NiftiError, Result};
use extension::{Extender, ExtensionSequence};
use flate2::bufread::GzDecoder;
use header::NiftiHeader;
use num_traits::{AsPrimitive, Num};
use std::fs::File;
use std::io::{BufReader, Read, Seek};
use std::ops::{Add, Mul};
use std::path::Path;
use typedef::NiftiType;
use util::nb_bytes_for_data;
use volume::element::DataElement;

#[cfg(feature = "ndarray_volumes")]
use ndarray::{Array, Ix, IxDyn, ShapeBuilder};
#[cfg(feature = "ndarray_volumes")]
use volume::ndarray::IntoNdArray;

/// A data type for a NIFTI-1 volume which reads data from a file on-demand.
/// The underlying data source is enquired when using one of the available
/// reading methods or [converting it to an `ndarray`] (only with the
/// `ndarray_volumes` feature).
///
/// [converting it to an `ndarray`]: ../ndarray/index.html
///
#[derive(Debug, PartialEq, Clone)]
pub struct LazyNiftiVolume<F> {
    dim: [u16; 8],
    datatype: NiftiType,
    scl_slope: f32,
    scl_inter: f32,
    endianness: Endianness,
    voxel_offset: u64,
    source: F,
}

impl<F> LazyNiftiVolume<F>
where
    F: Read,
    F: Seek,
{

}<commit_msg>Remove volume/lazy.rs<commit_after><|endoftext|>


<commit_before>//! Module containing HTTP client implementations
use std::net::{SocketAddr, ToSocketAddrs};
use std::io::prelude::*;
use std::collections::HashMap;
use std::collections::hash_map::{Iter, Keys};
use std::io::{Error, BufWriter, BufReader, ErrorKind};

use super::methods::Method;
use super::messages::HttpReply;
use super::constants::properties;
use super::streams::*;

/// Trait for object capable of sending HttpRequests
pub trait HttpSend {
	/// Start a new request and return `BufWriter` to the underlying stream
	/// so you can write the request body.
	///
	/// When done, don't forget to call `flush()` on the `BufWriter` in order to flush all the buffer
	fn send_stream(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>) -> Result<BufWriter<&mut Write>, Error>;

	/// Send a full request and return the `HttpReply`.
	///
	/// If some `data` are provided, they are written to the request body, and the corresponding
	/// `Content-Lenth` header is inserted nto request's properties
	fn send(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>, data: Option<&[u8]>) -> Result<HttpReply<&mut Read>, Error>;

	/// Get the reply from stream. Must be called only after a request has been sent
	fn get_reply(&mut self) -> Result<HttpReply<&mut Read>, Error>;
}

/// Represent object with properties. Provides methods for accessing those properties
pub trait WithHeader {
	/// Get a property from client permanent header
	fn get_property(&self, key: &String) -> Option<&String>;

	/// Set a property in permanent header
	fn set_property(&mut self, key: String, value: String);

	/// Remove a property from permanent header
	fn unset_property(&mut self, key: &String);

	/// Return an iterator over properties names from permanent header
	fn get_properties_name(&self) -> Keys<String, String>;

	/// Return an iterator over properties from permanent header
	fn iter(&self) -> Iter<String, String>;
}

/// Represent and Http object with send capability and properties in header
pub trait Http: HttpSend+WithHeader{}

/// A simple and low-level HTTP client implementation
struct BaseClient<S: Stream> {
	addr: SocketAddr,
	header: HashMap<String, String>,
	stream: Option<S>,
}

/// Client for unsecured HTTP
pub type HttpClient = BaseClient<HttpStream>;
/// Client for secured HTTP
pub type HttpsClient = BaseClient<HttpsStream>;

impl <S: Stream> BaseClient<S> {

	/// Create a new HTTP client that will send requests to `addr`.
	/// # Example
	/// ```no_run
	/// use http::client::HttpClient;
	/// let mut client = HttpClient::new("www.google.com:80");
	/// // Send some requests
	/// ```
	pub fn new<A: ToSocketAddrs>(addr: A) -> Result<Self, Error> {
		let address = option!(try!(addr.to_socket_addrs()).next(), "Cannot resolve address");
		let client = BaseClient{
			addr: address,
			header: HashMap::new(),
			stream: None,
		};
		return Ok(client);
	}

	fn update_properties(&self, header: Option<&HashMap<String, String>>) -> HashMap<String, String> {
		let mut hdr = match header {
			Some(h) => h.clone(),
			None => HashMap::new()
		};
		for (k, v) in &self.header {
			if ! hdr.contains_key(k) {
				hdr.insert(k.clone(), v.clone());
			}
		}
		return hdr;
	}

	/// Open an `HttpStream` to remote host
	fn connect(&mut self) -> Result<&mut S, Error> {
		self.stream = Some(try!(S::open(self.addr)));
		return Ok(self.stream.as_mut().unwrap());
	}
}

impl <S: Stream> WithHeader for BaseClient<S> {
	fn get_property(&self, key: &String) -> Option<&String> {
		return self.header.get(key);
	}

	fn set_property(&mut self, key: String, value: String) {
		self.header.insert(key, value);
	}

	fn unset_property(&mut self, key: &String) {
		self.header.remove(key);
	}

	fn get_properties_name(&self) -> Keys<String, String> {
		return self.header.keys();
	}

	fn iter(&self) -> Iter<String, String> {
		return self.header.iter();
	}
}

impl <S: Stream> HttpSend for BaseClient<S>	{
	fn send_stream(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>) -> Result<BufWriter<&mut Write>, Error> {
		let hdr = self.update_properties(header);
		let stream: &mut Write = try!(self.connect());
		let mut w = BufWriter::new(stream);
		{
			let mut writer = &mut w;
			try!(writer.write(method.as_bytes()));
			try!(writer.write(b" "));
			try!(writer.write(path.as_bytes()));
			try!(writer.write(b"\r\n"));

			//Write header
			for (k, v) in hdr {
				try!(writer.write(k.as_bytes()));
				try!(writer.write(b": "));
				try!(writer.write(v.as_bytes()));
				try!(writer.write(b"\r\n"));
			}
			try!(writer.write(b"\r\n"));
		}
		return Ok(w);
	}

	fn get_reply(&mut self) -> Result<HttpReply<&mut Read>, Error> {
		let stream: &mut Read = match self.stream.as_mut() {
			Some(s) => s,
			None => return Err(Error::new(ErrorKind::NotConnected, "Cannot get reply since no stream is opened"))
		};
		return HttpReply::parse(BufReader::new(stream));
	}

	fn send(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>, data: Option<&[u8]>) -> Result<HttpReply<&mut Read>, Error> {
		{
			let mut hdr = match header {
				Some(h) => h.clone(),
				None => HashMap::new()
			};
			if let Some(d) = data {
				hdr.insert(properties::CONTENT_LENGTH.to_string(), d.len().to_string());
			}
			let mut writer = try!(self.send_stream(method, path, Some(&hdr)));
			if let Some(d) = data {
				try!(writer.write(d));
			}
			try!(writer.flush());
		}
		return self.get_reply();
	}
}

impl <S: Stream> Http for BaseClient<S>{}<commit_msg>Moved send method implementation to HttpSend trait default implementation. Only get_reply and send_stream need to be implemented by structs implementations<commit_after>//! Module containing HTTP client implementations
use std::net::{SocketAddr, ToSocketAddrs};
use std::io::prelude::*;
use std::collections::HashMap;
use std::collections::hash_map::{Iter, Keys};
use std::io::{Error, BufWriter, BufReader, ErrorKind};

use super::methods::Method;
use super::messages::HttpReply;
use super::constants::properties;
use super::streams::*;

/// Trait for object capable of sending HttpRequests
pub trait HttpSend {
	/// Start a new request and return `BufWriter` to the underlying stream
	/// so you can write the request body.
	///
	/// When done, don't forget to call `flush()` on the `BufWriter` in order to flush all the buffer
	fn send_stream(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>) -> Result<BufWriter<&mut Write>, Error>;

	/// Get the reply from stream. Must be called only after a request has been sent
	fn get_reply(&mut self) -> Result<HttpReply<&mut Read>, Error>;

	/// Send a full request and return the `HttpReply`.
	///
	/// If some `data` are provided, they are written to the request body, and the corresponding
	/// `Content-Lenth` header is inserted nto request's properties
	fn send(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>, data: Option<&[u8]>) -> Result<HttpReply<&mut Read>, Error> {
		{
			let mut hdr = match header {
				Some(h) => h.clone(),
				None => HashMap::new()
			};
			if let Some(d) = data {
				hdr.insert(properties::CONTENT_LENGTH.to_string(), d.len().to_string());
			}
			let mut writer = try!(self.send_stream(method, path, Some(&hdr)));
			if let Some(d) = data {
				try!(writer.write(d));
			}
			try!(writer.flush());
		}
		return self.get_reply();
	}
}

/// Represent object with properties. Provides methods for accessing those properties
pub trait WithHeader {
	/// Get a property from client permanent header
	fn get_property(&self, key: &String) -> Option<&String>;

	/// Set a property in permanent header
	fn set_property(&mut self, key: String, value: String);

	/// Remove a property from permanent header
	fn unset_property(&mut self, key: &String);

	/// Return an iterator over properties names from permanent header
	fn get_properties_name(&self) -> Keys<String, String>;

	/// Return an iterator over properties from permanent header
	fn iter(&self) -> Iter<String, String>;
}

/// Represent and Http object with send capability and properties in header
pub trait Http: HttpSend+WithHeader{}

/// A simple and low-level HTTP client implementation
struct BaseClient<S: Stream> {
	addr: SocketAddr,
	header: HashMap<String, String>,
	stream: Option<S>,
}

/// Client for unsecured HTTP
pub type HttpClient = BaseClient<HttpStream>;
/// Client for secured HTTP
pub type HttpsClient = BaseClient<HttpsStream>;

impl <S: Stream> BaseClient<S> {

	/// Create a new HTTP client that will send requests to `addr`.
	/// # Example
	/// ```no_run
	/// use http::client::HttpClient;
	/// let mut client = HttpClient::new("www.google.com:80");
	/// // Send some requests
	/// ```
	pub fn new<A: ToSocketAddrs>(addr: A) -> Result<Self, Error> {
		let address = option!(try!(addr.to_socket_addrs()).next(), "Cannot resolve address");
		let client = BaseClient{
			addr: address,
			header: HashMap::new(),
			stream: None,
		};
		return Ok(client);
	}

	fn update_properties(&self, header: Option<&HashMap<String, String>>) -> HashMap<String, String> {
		let mut hdr = match header {
			Some(h) => h.clone(),
			None => HashMap::new()
		};
		for (k, v) in &self.header {
			if ! hdr.contains_key(k) {
				hdr.insert(k.clone(), v.clone());
			}
		}
		return hdr;
	}

	/// Open an `HttpStream` to remote host
	fn connect(&mut self) -> Result<&mut S, Error> {
		self.stream = Some(try!(S::open(self.addr)));
		return Ok(self.stream.as_mut().unwrap());
	}
}

impl <S: Stream> WithHeader for BaseClient<S> {
	fn get_property(&self, key: &String) -> Option<&String> {
		return self.header.get(key);
	}

	fn set_property(&mut self, key: String, value: String) {
		self.header.insert(key, value);
	}

	fn unset_property(&mut self, key: &String) {
		self.header.remove(key);
	}

	fn get_properties_name(&self) -> Keys<String, String> {
		return self.header.keys();
	}

	fn iter(&self) -> Iter<String, String> {
		return self.header.iter();
	}
}

impl <S: Stream> HttpSend for BaseClient<S>	{
	fn send_stream(&mut self, method: Method, path: &str, header: Option<&HashMap<String, String>>) -> Result<BufWriter<&mut Write>, Error> {
		let hdr = self.update_properties(header);
		let stream: &mut Write = try!(self.connect());
		let mut w = BufWriter::new(stream);
		{
			let mut writer = &mut w;
			try!(writer.write(method.as_bytes()));
			try!(writer.write(b" "));
			try!(writer.write(path.as_bytes()));
			try!(writer.write(b"\r\n"));

			//Write header
			for (k, v) in hdr {
				try!(writer.write(k.as_bytes()));
				try!(writer.write(b": "));
				try!(writer.write(v.as_bytes()));
				try!(writer.write(b"\r\n"));
			}
			try!(writer.write(b"\r\n"));
		}
		return Ok(w);
	}

	fn get_reply(&mut self) -> Result<HttpReply<&mut Read>, Error> {
		let stream: &mut Read = match self.stream.as_mut() {
			Some(s) => s,
			None => return Err(Error::new(ErrorKind::NotConnected, "Cannot get reply since no stream is opened"))
		};
		return HttpReply::parse(BufReader::new(stream));
	}
}

impl <S: Stream> Http for BaseClient<S>{}<|endoftext|>


<commit_before>use crate::{
    api::Error,
    db::Database,
    model::{
        AnnoLichess, GameInfo, GameInfoPlayer, Mode, PersonalEntry, PersonalKeyBuilder, UserName,
    },
    util::NevermindExt as _,
};
use clap::Clap;
use futures_util::StreamExt;
use rustc_hash::FxHashMap;
use shakmaty::{
    uci::Uci, variant::VariantPosition, zobrist::Zobrist, ByColor, CastlingMode, Color, Outcome,
    Position,
};
use std::sync::Arc;
use std::time::Duration;
use tokio::{
    sync::{
        mpsc::{self, error::SendTimeoutError},
        oneshot,
    },
    task::JoinHandle,
    time::timeout,
};

mod lila;

use lila::{Game, Lila};

#[derive(Clap)]
pub struct IndexerOpt {
    #[clap(long = "lila", default_value = "https://lichess.org")]
    lila: String,
}

#[derive(Clone)]
pub struct IndexerStub {
    tx: mpsc::Sender<IndexerMessage>,
}

impl IndexerStub {
    pub fn spawn(db: Arc<Database>, opt: IndexerOpt) -> (IndexerStub, JoinHandle<()>) {
        let (tx, rx) = mpsc::channel(2);
        (
            IndexerStub { tx },
            tokio::spawn(
                IndexerActor {
                    rx,
                    db,
                    lila: Lila::new(opt),
                }
                .run(),
            ),
        )
    }

    pub async fn index_player(&self, player: UserName) -> Result<IndexerStatus, Error> {
        let (req, res) = oneshot::channel();

        self.tx
            .send_timeout(
                IndexerMessage::IndexPlayer {
                    player,
                    callback: req,
                },
                Duration::from_secs(2),
            )
            .await
            .map_err(|err| match err {
                SendTimeoutError::Timeout(_) => Error::IndexerQueueFull,
                SendTimeoutError::Closed(_) => panic!("indexer died"),
            })?;

        match timeout(Duration::from_secs(7), res).await {
            Ok(res) => match res.expect("indexer alive") {
                Ok(()) => Ok(IndexerStatus::Completed),
                Err(err) => Err(err),
            },
            Err(_) => return Ok(IndexerStatus::Ongoing),
        }
    }
}

struct IndexerActor {
    rx: mpsc::Receiver<IndexerMessage>,
    db: Arc<Database>,
    lila: Lila,
}

impl IndexerActor {
    async fn run(mut self) {
        while let Some(msg) = self.rx.recv().await {
            match msg {
                IndexerMessage::IndexPlayer { callback, player } => {
                    callback
                        .send(self.index_player(player).await)
                        .nevermind("requester gone away");
                }
            }
        }
    }

    async fn index_player(&self, player: UserName) -> Result<(), Error> {
        log::info!("starting to index {}", player);

        let hash = ByColor::new_with(|color| {
            PersonalKeyBuilder::with_user_pov(&player.to_owned().into(), color)
        });

        let mut num_games = 0;
        let mut games = self.lila.user_games(&player).await?;
        while let Some(game) = games.next().await {
            let game = match game {
                Ok(game) => game,
                Err(err) => {
                    log::error!("{}", err);
                    continue;
                }
            };

            self.index_game(&player, &hash, game);

            num_games += 1;
            if num_games % 1024 == 0 {
                log::info!("indexed {} games for {}", num_games, player);
            }
        }

        log::info!("finished indexing {} games for {}", num_games, player);
        Ok(())
    }

    fn index_game(&self, player: &UserName, hash: &ByColor<PersonalKeyBuilder>, game: Game) {
        if game.status.is_unindexable() || game.status.is_ongoing() {
            return;
        }

        let color = if Some(player) == game.user_name(Color::White) {
            Color::White
        } else if Some(player) == game.user_name(Color::Black) {
            Color::Black
        } else {
            return;
        };
        let year = AnnoLichess::from_time(game.last_move_at);
        let outcome = Outcome::from_winner(game.winner);

        let queryable = self.db.queryable();
        if queryable
            .get_game_info(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed.into_color(color))
        {
            log::debug!(
                "{}/{} already indexed",
                game.id,
                color.fold("white", "black")
            );
            return;
        }
        queryable
            .merge_game_info(
                game.id,
                GameInfo {
                    winner: outcome.winner(),
                    speed: game.speed,
                    rated: game.rated,
                    year: year.year(),
                    white: GameInfoPlayer {
                        name: game.players.white.user.map(|p| p.name.to_string()),
                        rating: game.players.white.rating,
                    },
                    black: GameInfoPlayer {
                        name: game.players.black.user.map(|p| p.name.to_string()),
                        rating: game.players.black.rating,
                    },
                    indexed: ByColor::new_with(|c| color == c),
                },
            )
            .expect("put game info");

        let variant = game.variant.into();
        let pos = match game.initial_fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960),
            None => Ok(VariantPosition::new(variant)),
        };

        let mut pos: Zobrist<_, u128> = match pos {
            Ok(pos) => Zobrist::new(pos),
            Err(err) => {
                log::warn!("not indexing {}: {}", game.id, err);
                return;
            }
        };

        // Build an intermediate table to remove loops (due to repetitions).
        let mut table: FxHashMap<u128, Uci> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());

        for (ply, san) in game.moves.into_iter().enumerate() {
            let m = match san.to_move(&pos) {
                Ok(m) => m,
                Err(err) => {
                    log::error!("not indexing {}: {} ({} at ply {})", game.id, err, san, ply);
                    return;
                }
            };

            let uci = m.to_uci(CastlingMode::Chess960);
            table.insert(pos.zobrist_hash(), uci);

            pos.play_unchecked(&m);
        }

        for (zobrist, uci) in table {
            queryable
                .merge_personal(
                    hash.by_color(color)
                        .with_zobrist(variant, zobrist)
                        .with_year(year),
                    PersonalEntry::new_single(
                        uci.clone(),
                        game.speed,
                        Mode::from_rated(game.rated),
                        game.id,
                        outcome,
                    ),
                )
                .expect("merge personal");
        }
    }
}

enum IndexerMessage {
    IndexPlayer {
        player: UserName,
        callback: oneshot::Sender<Result<(), Error>>,
    },
}

pub enum IndexerStatus {
    Ongoing,
    Completed,
    Failed,
}
<commit_msg>partially index games with illegal moves<commit_after>use crate::{
    api::Error,
    db::Database,
    model::{
        AnnoLichess, GameInfo, GameInfoPlayer, Mode, PersonalEntry, PersonalKeyBuilder, UserName,
    },
    util::NevermindExt as _,
};
use clap::Clap;
use futures_util::StreamExt;
use rustc_hash::FxHashMap;
use shakmaty::{
    uci::Uci, variant::VariantPosition, zobrist::Zobrist, ByColor, CastlingMode, Color, Outcome,
    Position,
};
use std::sync::Arc;
use std::time::Duration;
use tokio::{
    sync::{
        mpsc::{self, error::SendTimeoutError},
        oneshot,
    },
    task::JoinHandle,
    time::timeout,
};

mod lila;

use lila::{Game, Lila};

#[derive(Clap)]
pub struct IndexerOpt {
    #[clap(long = "lila", default_value = "https://lichess.org")]
    lila: String,
}

#[derive(Clone)]
pub struct IndexerStub {
    tx: mpsc::Sender<IndexerMessage>,
}

impl IndexerStub {
    pub fn spawn(db: Arc<Database>, opt: IndexerOpt) -> (IndexerStub, JoinHandle<()>) {
        let (tx, rx) = mpsc::channel(2);
        (
            IndexerStub { tx },
            tokio::spawn(
                IndexerActor {
                    rx,
                    db,
                    lila: Lila::new(opt),
                }
                .run(),
            ),
        )
    }

    pub async fn index_player(&self, player: UserName) -> Result<IndexerStatus, Error> {
        let (req, res) = oneshot::channel();

        self.tx
            .send_timeout(
                IndexerMessage::IndexPlayer {
                    player,
                    callback: req,
                },
                Duration::from_secs(2),
            )
            .await
            .map_err(|err| match err {
                SendTimeoutError::Timeout(_) => Error::IndexerQueueFull,
                SendTimeoutError::Closed(_) => panic!("indexer died"),
            })?;

        match timeout(Duration::from_secs(7), res).await {
            Ok(res) => match res.expect("indexer alive") {
                Ok(()) => Ok(IndexerStatus::Completed),
                Err(err) => Err(err),
            },
            Err(_) => return Ok(IndexerStatus::Ongoing),
        }
    }
}

struct IndexerActor {
    rx: mpsc::Receiver<IndexerMessage>,
    db: Arc<Database>,
    lila: Lila,
}

impl IndexerActor {
    async fn run(mut self) {
        while let Some(msg) = self.rx.recv().await {
            match msg {
                IndexerMessage::IndexPlayer { callback, player } => {
                    callback
                        .send(self.index_player(player).await)
                        .nevermind("requester gone away");
                }
            }
        }
    }

    async fn index_player(&self, player: UserName) -> Result<(), Error> {
        log::info!("starting to index {}", player);

        let hash = ByColor::new_with(|color| {
            PersonalKeyBuilder::with_user_pov(&player.to_owned().into(), color)
        });

        let mut num_games = 0;
        let mut games = self.lila.user_games(&player).await?;
        while let Some(game) = games.next().await {
            let game = match game {
                Ok(game) => game,
                Err(err) => {
                    log::error!("{}", err);
                    continue;
                }
            };

            self.index_game(&player, &hash, game);

            num_games += 1;
            if num_games % 1024 == 0 {
                log::info!("indexed {} games for {}", num_games, player);
            }
        }

        log::info!("finished indexing {} games for {}", num_games, player);
        Ok(())
    }

    fn index_game(&self, player: &UserName, hash: &ByColor<PersonalKeyBuilder>, game: Game) {
        if game.status.is_unindexable() || game.status.is_ongoing() {
            return;
        }

        let color = if Some(player) == game.user_name(Color::White) {
            Color::White
        } else if Some(player) == game.user_name(Color::Black) {
            Color::Black
        } else {
            return;
        };
        let year = AnnoLichess::from_time(game.last_move_at);
        let outcome = Outcome::from_winner(game.winner);

        let queryable = self.db.queryable();
        if queryable
            .get_game_info(game.id)
            .expect("get game info")
            .map_or(false, |info| info.indexed.into_color(color))
        {
            log::debug!(
                "{}/{} already indexed",
                game.id,
                color.fold("white", "black")
            );
            return;
        }
        queryable
            .merge_game_info(
                game.id,
                GameInfo {
                    winner: outcome.winner(),
                    speed: game.speed,
                    rated: game.rated,
                    year: year.year(),
                    white: GameInfoPlayer {
                        name: game.players.white.user.map(|p| p.name.to_string()),
                        rating: game.players.white.rating,
                    },
                    black: GameInfoPlayer {
                        name: game.players.black.user.map(|p| p.name.to_string()),
                        rating: game.players.black.rating,
                    },
                    indexed: ByColor::new_with(|c| color == c),
                },
            )
            .expect("put game info");

        let variant = game.variant.into();
        let pos = match game.initial_fen {
            Some(fen) => VariantPosition::from_setup(variant, &fen, CastlingMode::Chess960),
            None => Ok(VariantPosition::new(variant)),
        };

        let mut pos: Zobrist<_, u128> = match pos {
            Ok(pos) => Zobrist::new(pos),
            Err(err) => {
                log::warn!("not indexing {}: {}", game.id, err);
                return;
            }
        };

        // Build an intermediate table to remove loops (due to repetitions).
        let mut table: FxHashMap<u128, Uci> =
            FxHashMap::with_capacity_and_hasher(game.moves.len(), Default::default());

        for (ply, san) in game.moves.into_iter().enumerate() {
            let m = match san.to_move(&pos) {
                Ok(m) => m,
                Err(err) => {
                    log::warn!("cutting off {} at ply {}: {}: {}", game.id, ply, err, san);
                    break;
                }
            };

            let uci = m.to_uci(CastlingMode::Chess960);
            table.insert(pos.zobrist_hash(), uci);

            pos.play_unchecked(&m);
        }

        for (zobrist, uci) in table {
            queryable
                .merge_personal(
                    hash.by_color(color)
                        .with_zobrist(variant, zobrist)
                        .with_year(year),
                    PersonalEntry::new_single(
                        uci.clone(),
                        game.speed,
                        Mode::from_rated(game.rated),
                        game.id,
                        outcome,
                    ),
                )
                .expect("merge personal");
        }
    }
}

enum IndexerMessage {
    IndexPlayer {
        player: UserName,
        callback: oneshot::Sender<Result<(), Error>>,
    },
}

pub enum IndexerStatus {
    Ongoing,
    Completed,
    Failed,
}
<|endoftext|>


<commit_before>//! Support for mock HTTP servers that verify pacts.

use pact_matching::models::*;
use pact_mock_server::*;
use pact_mock_server::matching::MatchResult;
use std::{
  fmt::Write as FmtWrite,
  env,
  io::{self, prelude::*},
  thread
};
use url::Url;
use tokio;
use tokio::runtime::current_thread::{Runtime as CurrentThreadRuntime};

/// This trait is implemented by types which allow us to start a mock server.
pub trait StartMockServer {
    /// Start a mock server running in a background thread.
    fn start_mock_server(&self) -> ValidatingMockServer;
    /// Start a new async mock server running in a tokio runtime
    fn start_mock_server_async(&self, runtime: &mut CurrentThreadRuntime) -> ValidatingMockServer;
}

impl StartMockServer for Pact {
    fn start_mock_server(&self) -> ValidatingMockServer {
        ValidatingMockServer::new(self.clone())
    }
    fn start_mock_server_async(&self, runtime: &mut CurrentThreadRuntime) -> ValidatingMockServer {
        ValidatingMockServer::new_async(self.clone(), runtime)
    }
}

enum Mode {
    Background(tokio::runtime::Runtime),
    Async
}

/// A mock HTTP server that handles the requests described in a `Pact`, intended
/// for use in tests, and validates that the requests made to that server are
/// correct.
///
/// Because this is intended for use in tests, it will panic if something goes
/// wrong.
pub struct ValidatingMockServer {
    // A description of our mock server, for use in error messages.
    description: String,
    // The ID of our mock server.
    port: i32,
    // The URL of our mock server.
    url: Url,
    // The mock server instance
    mock_server: Option<mock_server::MockServer>,
    // The running mode of our mock server.
    mode: Mode
}

impl ValidatingMockServer {
    /// Create a new mock server which handles requests as described in the
    /// pact, and runs in a background thread
    pub fn new(pact: Pact) -> ValidatingMockServer {
        let mut runtime = tokio::runtime::Builder::new()
            .blocking_threads(1)
            .build()
            .unwrap();

        let mock_server = mock_server::MockServer::spawn("".into(), pact, 0, &mut runtime)
            .expect("error starting mock server");

        ValidatingMockServer::from_mock_server(mock_server, Mode::Background(runtime))
    }

    /// Create a new async mock server that runs in the current thread
    pub fn new_async(pact: Pact, runtime: &mut CurrentThreadRuntime) -> ValidatingMockServer {
        let mock_server = mock_server::MockServer::spawn_current_thread("".into(), pact, 0, runtime)
            .expect("error starting mock server");

        ValidatingMockServer::from_mock_server(mock_server, Mode::Async)
    }

    fn from_mock_server(mock_server: mock_server::MockServer, mode: Mode) -> ValidatingMockServer {
        let description = format!("{}/{}", mock_server.pact.consumer.name, mock_server.pact.provider.name);
        let url_str = mock_server.url();
        ValidatingMockServer {
            description,
            port: mock_server.addr.port() as i32,
            url: url_str.parse().expect("invalid mock server URL"),
            mock_server: Some(mock_server),
            mode: mode
        }
    }

    /// The URL of our mock server. You can make normal HTTP requests using this
    /// as the base URL.
    pub fn url(&self) -> &Url {
        &self.url
    }

    /// Given a path string, return a URL pointing to that path on the mock
    /// server. If the `path` cannot be parsed as URL, **this function will
    /// panic**. For a non-panicking version, call `.url()` instead and build
    /// this path yourself.
    pub fn path<P: AsRef<str>>(&self, path: P) -> Url {
        // We panic here because this a _test_ library, the `?` operator is
        // useless in tests, and filling up our test code with piles of `unwrap`
        // calls is ugly.
        self.url.join(path.as_ref()).expect("could not parse URL")
    }

    /// Helper function called by our `drop` implementation. This basically exists
    /// so that it can return `Err(message)` whenever needed without making the
    /// flow control in `drop` ultra-complex.
    fn drop_helper(&mut self) -> Result<(), String> {
        let mut mock_server = self.mock_server.take()
            .ok_or("Mock server already dropped")?;

        // Kill the server
        mock_server.shutdown()?;

        // Read in all match results
        mock_server.read_match_results_from_server();

        // Look up any mismatches which occurred.
        let mismatches = mock_server.mismatches();

        if mismatches.is_empty() {
            // Success! Write out the generated pact file.
            mock_server.write_pact(&Some(env::var("PACT_OUTPUT_DIR").unwrap_or("target/pacts".to_owned())))
                .map_err(|err| format!("error writing pact: {}", err))?;
            Ok(())
        } else {
            // Failure. Format our errors.
            let mut msg = format!(
                "mock server {} failed verification:\n",
                self.description,
            );
            for mismatch in mismatches {
                match mismatch {
                    MatchResult::RequestMatch(_) => {
                        unreachable!("list of mismatches contains a match");
                    }
                    MatchResult::RequestMismatch(interaction, mismatches) => {
                        let _ = writeln!(
                            &mut msg,
                            "- interaction {:?}:",
                            interaction.description,
                        );
                        for m in mismatches {
                            let _ = writeln!(&mut msg, "  - {}", m.description());
                        }
                    }
                    MatchResult::RequestNotFound(request) => {
                        let _ = writeln!(&mut msg, "- received unexpected request:");
                        let _ = writeln!(&mut msg, "{:#?}", request);
                    }
                    MatchResult::MissingRequest(interaction) => {
                        let _ = writeln!(
                            &mut msg,
                            "- interaction {:?} expected, but never occurred",
                            interaction.description,
                        );
                        let _ = writeln!(&mut msg, "{:#?}", interaction.request);
                    }
                }
            }
            Err(msg)
        }
    }
}

/// Either panic with `msg`, or if we're already in the middle of a panic,
/// just print `msg` to standard error.
fn panic_or_print_error(msg: &str) {
    if thread::panicking() {
        // The current thread is panicking, so don't try to panic again, because
        // double panics don't print useful explanations of why the test failed.
        // Instead, just print to `stderr`. Ignore any errors, because there's
        // not much we can do if we can't panic and we can't write to `stderr`.
        let _ = writeln!(io::stderr(), "{}", msg);
    } else {
        panic!("{}", msg);
    }
}

impl Drop for ValidatingMockServer {
    fn drop(&mut self) {
        let result = self.drop_helper();
        //shutdown_mock_server_by_port(self.port);
        if let Err(msg) = result {
            panic_or_print_error(&msg);
        }
    }
}
<commit_msg>No point having MockServer in an Option, as shutdown signal consumption is now encapsulated<commit_after>//! Support for mock HTTP servers that verify pacts.

use pact_matching::models::*;
use pact_mock_server::*;
use pact_mock_server::matching::MatchResult;
use std::{
  fmt::Write as FmtWrite,
  env,
  io::{self, prelude::*},
  thread
};
use url::Url;
use tokio;
use tokio::runtime::current_thread::{Runtime as CurrentThreadRuntime};

/// This trait is implemented by types which allow us to start a mock server.
pub trait StartMockServer {
    /// Start a mock server running in a background thread.
    fn start_mock_server(&self) -> ValidatingMockServer;
    /// Start a new async mock server running in a tokio runtime
    fn start_mock_server_async(&self, runtime: &mut CurrentThreadRuntime) -> ValidatingMockServer;
}

impl StartMockServer for Pact {
    fn start_mock_server(&self) -> ValidatingMockServer {
        ValidatingMockServer::new(self.clone())
    }
    fn start_mock_server_async(&self, runtime: &mut CurrentThreadRuntime) -> ValidatingMockServer {
        ValidatingMockServer::new_async(self.clone(), runtime)
    }
}

enum Mode {
    Background(tokio::runtime::Runtime),
    Async
}

/// A mock HTTP server that handles the requests described in a `Pact`, intended
/// for use in tests, and validates that the requests made to that server are
/// correct.
///
/// Because this is intended for use in tests, it will panic if something goes
/// wrong.
pub struct ValidatingMockServer {
    // A description of our mock server, for use in error messages.
    description: String,
    // The URL of our mock server.
    url: Url,
    // The mock server instance
    mock_server: mock_server::MockServer,
    // The running mode of our mock server.
    _mode: Mode
}

impl ValidatingMockServer {
    /// Create a new mock server which handles requests as described in the
    /// pact, and runs in a background thread
    pub fn new(pact: Pact) -> ValidatingMockServer {
        let mut runtime = tokio::runtime::Builder::new()
            .blocking_threads(1)
            .build()
            .unwrap();

        let mock_server = mock_server::MockServer::spawn("".into(), pact, 0, &mut runtime)
            .expect("error starting mock server");

        ValidatingMockServer::from_mock_server(mock_server, Mode::Background(runtime))
    }

    /// Create a new async mock server that runs in the current thread
    pub fn new_async(pact: Pact, runtime: &mut CurrentThreadRuntime) -> ValidatingMockServer {
        let mock_server = mock_server::MockServer::spawn_current_thread("".into(), pact, 0, runtime)
            .expect("error starting mock server");

        ValidatingMockServer::from_mock_server(mock_server, Mode::Async)
    }

    fn from_mock_server(mock_server: mock_server::MockServer, mode: Mode) -> ValidatingMockServer {
        let description = format!("{}/{}", mock_server.pact.consumer.name, mock_server.pact.provider.name);
        let url_str = mock_server.url();
        ValidatingMockServer {
            description,
            url: url_str.parse().expect("invalid mock server URL"),
            mock_server: mock_server,
            _mode: mode
        }
    }

    /// The URL of our mock server. You can make normal HTTP requests using this
    /// as the base URL.
    pub fn url(&self) -> &Url {
        &self.url
    }

    /// Given a path string, return a URL pointing to that path on the mock
    /// server. If the `path` cannot be parsed as URL, **this function will
    /// panic**. For a non-panicking version, call `.url()` instead and build
    /// this path yourself.
    pub fn path<P: AsRef<str>>(&self, path: P) -> Url {
        // We panic here because this a _test_ library, the `?` operator is
        // useless in tests, and filling up our test code with piles of `unwrap`
        // calls is ugly.
        self.url.join(path.as_ref()).expect("could not parse URL")
    }

    /// Helper function called by our `drop` implementation. This basically exists
    /// so that it can return `Err(message)` whenever needed without making the
    /// flow control in `drop` ultra-complex.
    fn drop_helper(&mut self) -> Result<(), String> {
        // Kill the server
        self.mock_server.shutdown()?;

        // Read in all match results
        self.mock_server.read_match_results_from_server();

        // Look up any mismatches which occurred.
        let mismatches = self.mock_server.mismatches();

        if mismatches.is_empty() {
            // Success! Write out the generated pact file.
            self.mock_server.write_pact(&Some(env::var("PACT_OUTPUT_DIR").unwrap_or("target/pacts".to_owned())))
                .map_err(|err| format!("error writing pact: {}", err))?;
            Ok(())
        } else {
            // Failure. Format our errors.
            let mut msg = format!(
                "mock server {} failed verification:\n",
                self.description,
            );
            for mismatch in mismatches {
                match mismatch {
                    MatchResult::RequestMatch(_) => {
                        unreachable!("list of mismatches contains a match");
                    }
                    MatchResult::RequestMismatch(interaction, mismatches) => {
                        let _ = writeln!(
                            &mut msg,
                            "- interaction {:?}:",
                            interaction.description,
                        );
                        for m in mismatches {
                            let _ = writeln!(&mut msg, "  - {}", m.description());
                        }
                    }
                    MatchResult::RequestNotFound(request) => {
                        let _ = writeln!(&mut msg, "- received unexpected request:");
                        let _ = writeln!(&mut msg, "{:#?}", request);
                    }
                    MatchResult::MissingRequest(interaction) => {
                        let _ = writeln!(
                            &mut msg,
                            "- interaction {:?} expected, but never occurred",
                            interaction.description,
                        );
                        let _ = writeln!(&mut msg, "{:#?}", interaction.request);
                    }
                }
            }
            Err(msg)
        }
    }
}

/// Either panic with `msg`, or if we're already in the middle of a panic,
/// just print `msg` to standard error.
fn panic_or_print_error(msg: &str) {
    if thread::panicking() {
        // The current thread is panicking, so don't try to panic again, because
        // double panics don't print useful explanations of why the test failed.
        // Instead, just print to `stderr`. Ignore any errors, because there's
        // not much we can do if we can't panic and we can't write to `stderr`.
        let _ = writeln!(io::stderr(), "{}", msg);
    } else {
        panic!("{}", msg);
    }
}

impl Drop for ValidatingMockServer {
    fn drop(&mut self) {
        let result = self.drop_helper();
        if let Err(msg) = result {
            panic_or_print_error(&msg);
        }
    }
}
<|endoftext|>