<commit_before>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) whitequark <whitequark@whitequark.org>
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Generators.
//!
//! Generators allow repeatedly suspending the execution of a function,
//! returning a value to the caller, and resuming the suspended function
//! afterwards.

use core::marker::PhantomData;
use core::{ptr, mem};
use core::cell::Cell;

use stack;
use debug;
use arch::{self, StackPointer};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
  /// Generator can be resumed. This is the initial state.
  Runnable,
  /// Generator cannot be resumed. This is the state of the generator after
  /// the generator function has returned or panicked.
  Unavailable
}

/// Generator wraps a function and allows suspending its execution more than once, returning
/// a value each time.
///
/// The first time `resume(input0)` is called, the function is called as `f(yielder, input0)`.
/// It runs until it suspends its execution through `yielder.suspend(output0)`, after which
/// `resume(input0)` returns `output0`. The function can be resumed again using `resume(input1)`,
/// after which `yielder.suspend(output0)` returns `input1`, and so on. Once the function returns,
/// the `resume()` call will return `None`, and it will return `None` every time it is called
/// after that.
///
/// If the generator function panics, the panic is propagated through the `resume()` call as usual.
///
/// After the generator function returns or panics, it is safe to reclaim the generator stack
/// using `unwrap()`.
///
/// `state()` can be used to determine whether the generator function has returned;
/// the state is `State::Runnable` after creation and suspension, and `State::Unavailable`
/// once the generator function returns or panics.
///
/// When the input type is `()`, a generator implements the Iterator trait.
///
/// # Example
///
/// ```
/// use fringe::{OsStack, Generator};
///
/// let stack = OsStack::new(0).unwrap();
/// let mut add_one = Generator::new(stack, move |yielder, mut input| {
///   loop {
///     if input == 0 { break }
///     input = yielder.suspend(input + 1)
///   }
/// });
/// println!("{:?}", add_one.resume(2)); // prints Some(3)
/// println!("{:?}", add_one.resume(3)); // prints Some(4)
/// println!("{:?}", add_one.resume(0)); // prints None
/// ```
///
/// # Iterator example
///
/// ```
/// use fringe::{OsStack, Generator};
/// use std::mem;
///
/// let stack = OsStack::new(0).unwrap();
/// let mut nat = Generator::new(stack, move |yielder, ()| {
///   for i in 1.. { yielder.suspend(i) }
/// });
/// println!("{:?}", nat.next()); // prints Some(0)
/// println!("{:?}", nat.next()); // prints Some(1)
/// println!("{:?}", nat.next()); // prints Some(2)
/// mem::forget(nat); // we can't drop a running Generator, so we leak it
/// ```
#[derive(Debug)]
pub struct Generator<'a, Input: 'a, Output: 'a, Stack: stack::Stack> {
  state:     State,
  stack:     NoDrop<Stack>,
  stack_id:  NoDrop<debug::StackId>,
  stack_ptr: arch::StackPointer,
  phantom:   PhantomData<(&'a (), *mut Input, *const Output)>
}

#[allow(unions_with_drop_fields)]
union NoDrop<T> {
  inner: T
  _empty: ()
}

impl<T: ::core::fmt::Debug> ::core::fmt::Debug for NoDrop<T> {
  fn fmt(&self, w: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
    unsafe {
      // this is safe because we never invoke formatting on the empty variant
      self.inner.fmt(w)
    }
  }
}

impl<'a, Input, Output, Stack> Generator<'a, Input, Output, Stack>
    where Input: 'a, Output: 'a, Stack: stack::Stack {
  /// Creates a new generator.
  ///
  /// See also the [contract](../trait.GuardedStack.html) that needs to be fulfilled by `stack`.
  pub fn new<F>(stack: Stack, f: F) -> Generator<'a, Input, Output, Stack>
      where Stack: stack::GuardedStack + 'static,
            F: FnOnce(&Yielder<Input, Output>, Input) + 'a {
    unsafe { Generator::unsafe_new(stack, f) }
  }

  /// Same as `new`, but does not require `stack` to have a guard page.
  ///
  /// This function is unsafe because the generator function can easily violate
  /// memory safety by overflowing the stack. It is useful in environments where
  /// guarded stacks do not exist, e.g. in absence of an MMU.
  ///
  /// See also the [contract](../trait.Stack.html) that needs to be fulfilled by `stack`.
  pub unsafe fn unsafe_new<F>(stack: Stack, f: F) -> Generator<'a, Input, Output, Stack>
      where F: FnOnce(&Yielder<Input, Output>, Input) + 'a {
    unsafe extern "C" fn generator_wrapper<Input, Output, Stack, F>(env: usize, stack_ptr: StackPointer) -> !
        where Stack: stack::Stack, F: FnOnce(&Yielder<Input, Output>, Input) {
      // Retrieve our environment from the callee and return control to it.
      let f = ptr::read(env as *const F);
      let (data, stack_ptr) = arch::swap(0, stack_ptr, None);
      // See the second half of Yielder::suspend_bare.
      let input = ptr::read(data as *const Input);
      // Run the body of the generator.
      let yielder = Yielder::new(stack_ptr);
      f(&yielder, input);
      // Past this point, the generator has dropped everything it has held.
      loop { yielder.suspend_bare(None); }
    }

    let stack_id  = debug::StackId::register(&stack);
    let stack_ptr = arch::init(&stack, generator_wrapper::<Input, Output, Stack, F>);

    // Transfer environment to the callee.
    let stack_ptr = arch::swap(&f as *const F as usize, stack_ptr, Some(&stack)).1;
    mem::forget(f);

    Generator {
      state:     State::Runnable,
      stack:     NoDrop { inner: stack },
      stack_id:  NoDrop { inner: stack_id },
      stack_ptr: stack_ptr,
      phantom:   PhantomData
    }
  }

  /// Resumes the generator and return the next value it yields.
  /// If the generator function has returned, returns `None`.
  #[inline]
  pub fn resume(&mut self, input: Input) -> Option<Output> {
    match self.state {
      State::Runnable => {
        // Set the state to Unavailable. Since we have exclusive access to the generator,
        // the only case where this matters is the generator function panics, after which
        // it must not be invocable again.
        self.state = State::Unavailable;

        // Switch to the generator function, and retrieve the yielded value.
        let val = unsafe {
          let (data_out, stack_ptr) = arch::swap(&input as *const Input as usize, self.stack_ptr, Some(&self.stack.inner));
          self.stack_ptr = stack_ptr;
          mem::forget(input);
          ptr::read(data_out as *const Option<Output>)
        };

        // Unless the generator function has returned, it can be switched to again, so
        // set the state to Runnable.
        if val.is_some() { self.state = State::Runnable }

        val
      }
      State::Unavailable => None
    }
  }

  /// Returns the state of the generator.
  #[inline]
  pub fn state(&self) -> State { self.state }

  /// Extracts the stack from a generator when the generator function has returned.
  /// If the generator function has not returned
  /// (i.e. `self.state() == State::Runnable`), panics.
  pub fn unwrap(self) -> Stack {
    match self.state {
      State::Runnable => {
        mem::forget(self);
        panic!("Argh! Bastard! Don't touch that!")
      }
      State::Unavailable => unsafe { self.unsafe_unwrap() }
    }
  }

  /// Extracts the stack from a generator without checking if the generator function has returned.
  /// This will leave any pointers into the generator stack dangling, and won't run destructors.
  pub unsafe fn unsafe_unwrap(mut self) -> Stack {
    ptr::drop_in_place(&mut self.stack_id.inner);
    let stack = ptr::read(&mut self.stack.inner);
    mem::forget(self);
    stack
  }
}

impl<'a, Input, Output, Stack> Drop for Generator<'a, Input, Output, Stack>
    where Input: 'a, Output: 'a, Stack: stack::Stack {
  fn drop(&mut self) {
    unsafe {
      ptr::drop_in_place(&mut self.stack_id.inner);
      match self.state {
        State::Runnable    => panic!("dropped unfinished Generator"),
        State::Unavailable => ptr::drop_in_place(&mut self.stack.inner)
      }
    }
  }
}

/// Yielder is an interface provided to every generator through which it
/// returns a value.
#[derive(Debug)]
pub struct Yielder<Input, Output> {
  stack_ptr: Cell<StackPointer>,
  phantom: PhantomData<(*const Input, *mut Output)>
}

impl<Input, Output> Yielder<Input, Output> {
  fn new(stack_ptr: StackPointer) -> Yielder<Input, Output> {
    Yielder {
      stack_ptr: Cell::new(stack_ptr),
      phantom: PhantomData
    }
  }

  #[inline(always)]
  fn suspend_bare(&self, val: Option<Output>) -> Input {
    unsafe {
      let (data, stack_ptr) = arch::swap(&val as *const Option<Output> as usize, self.stack_ptr.get(), None);
      self.stack_ptr.set(stack_ptr);
      mem::forget(val);
      ptr::read(data as *const Input)
    }
  }

  /// Suspends the generator and returns `Some(item)` from the `resume()`
  /// invocation that resumed the generator.
  #[inline(always)]
  pub fn suspend(&self, item: Output) -> Input {
    self.suspend_bare(Some(item))
  }
}

impl<'a, Output, Stack> Iterator for Generator<'a, (), Output, Stack>
    where Output: 'a, Stack: stack::Stack {
  type Item = Output;

  fn next(&mut self) -> Option<Self::Item> { self.resume(()) }
}
<commit_msg>Remove NoDrop's unnecessary empty variant<commit_after>// This file is part of libfringe, a low-level green threading library.
// Copyright (c) whitequark <whitequark@whitequark.org>
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

//! Generators.
//!
//! Generators allow repeatedly suspending the execution of a function,
//! returning a value to the caller, and resuming the suspended function
//! afterwards.

use core::marker::PhantomData;
use core::{ptr, mem};
use core::cell::Cell;

use stack;
use debug;
use arch::{self, StackPointer};

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum State {
  /// Generator can be resumed. This is the initial state.
  Runnable,
  /// Generator cannot be resumed. This is the state of the generator after
  /// the generator function has returned or panicked.
  Unavailable
}

/// Generator wraps a function and allows suspending its execution more than once, returning
/// a value each time.
///
/// The first time `resume(input0)` is called, the function is called as `f(yielder, input0)`.
/// It runs until it suspends its execution through `yielder.suspend(output0)`, after which
/// `resume(input0)` returns `output0`. The function can be resumed again using `resume(input1)`,
/// after which `yielder.suspend(output0)` returns `input1`, and so on. Once the function returns,
/// the `resume()` call will return `None`, and it will return `None` every time it is called
/// after that.
///
/// If the generator function panics, the panic is propagated through the `resume()` call as usual.
///
/// After the generator function returns or panics, it is safe to reclaim the generator stack
/// using `unwrap()`.
///
/// `state()` can be used to determine whether the generator function has returned;
/// the state is `State::Runnable` after creation and suspension, and `State::Unavailable`
/// once the generator function returns or panics.
///
/// When the input type is `()`, a generator implements the Iterator trait.
///
/// # Example
///
/// ```
/// use fringe::{OsStack, Generator};
///
/// let stack = OsStack::new(0).unwrap();
/// let mut add_one = Generator::new(stack, move |yielder, mut input| {
///   loop {
///     if input == 0 { break }
///     input = yielder.suspend(input + 1)
///   }
/// });
/// println!("{:?}", add_one.resume(2)); // prints Some(3)
/// println!("{:?}", add_one.resume(3)); // prints Some(4)
/// println!("{:?}", add_one.resume(0)); // prints None
/// ```
///
/// # Iterator example
///
/// ```
/// use fringe::{OsStack, Generator};
/// use std::mem;
///
/// let stack = OsStack::new(0).unwrap();
/// let mut nat = Generator::new(stack, move |yielder, ()| {
///   for i in 1.. { yielder.suspend(i) }
/// });
/// println!("{:?}", nat.next()); // prints Some(0)
/// println!("{:?}", nat.next()); // prints Some(1)
/// println!("{:?}", nat.next()); // prints Some(2)
/// mem::forget(nat); // we can't drop a running Generator, so we leak it
/// ```
#[derive(Debug)]
pub struct Generator<'a, Input: 'a, Output: 'a, Stack: stack::Stack> {
  state:     State,
  stack:     NoDrop<Stack>,
  stack_id:  NoDrop<debug::StackId>,
  stack_ptr: arch::StackPointer,
  phantom:   PhantomData<(&'a (), *mut Input, *const Output)>
}

#[allow(unions_with_drop_fields)]
union NoDrop<T> {
  inner: T
}

impl<T: ::core::fmt::Debug> ::core::fmt::Debug for NoDrop<T> {
  fn fmt(&self, w: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
    unsafe {
      // this is safe because we never invoke formatting on the empty variant
      self.inner.fmt(w)
    }
  }
}

impl<'a, Input, Output, Stack> Generator<'a, Input, Output, Stack>
    where Input: 'a, Output: 'a, Stack: stack::Stack {
  /// Creates a new generator.
  ///
  /// See also the [contract](../trait.GuardedStack.html) that needs to be fulfilled by `stack`.
  pub fn new<F>(stack: Stack, f: F) -> Generator<'a, Input, Output, Stack>
      where Stack: stack::GuardedStack + 'static,
            F: FnOnce(&Yielder<Input, Output>, Input) + 'a {
    unsafe { Generator::unsafe_new(stack, f) }
  }

  /// Same as `new`, but does not require `stack` to have a guard page.
  ///
  /// This function is unsafe because the generator function can easily violate
  /// memory safety by overflowing the stack. It is useful in environments where
  /// guarded stacks do not exist, e.g. in absence of an MMU.
  ///
  /// See also the [contract](../trait.Stack.html) that needs to be fulfilled by `stack`.
  pub unsafe fn unsafe_new<F>(stack: Stack, f: F) -> Generator<'a, Input, Output, Stack>
      where F: FnOnce(&Yielder<Input, Output>, Input) + 'a {
    unsafe extern "C" fn generator_wrapper<Input, Output, Stack, F>(env: usize, stack_ptr: StackPointer) -> !
        where Stack: stack::Stack, F: FnOnce(&Yielder<Input, Output>, Input) {
      // Retrieve our environment from the callee and return control to it.
      let f = ptr::read(env as *const F);
      let (data, stack_ptr) = arch::swap(0, stack_ptr, None);
      // See the second half of Yielder::suspend_bare.
      let input = ptr::read(data as *const Input);
      // Run the body of the generator.
      let yielder = Yielder::new(stack_ptr);
      f(&yielder, input);
      // Past this point, the generator has dropped everything it has held.
      loop { yielder.suspend_bare(None); }
    }

    let stack_id  = debug::StackId::register(&stack);
    let stack_ptr = arch::init(&stack, generator_wrapper::<Input, Output, Stack, F>);

    // Transfer environment to the callee.
    let stack_ptr = arch::swap(&f as *const F as usize, stack_ptr, Some(&stack)).1;
    mem::forget(f);

    Generator {
      state:     State::Runnable,
      stack:     NoDrop { inner: stack },
      stack_id:  NoDrop { inner: stack_id },
      stack_ptr: stack_ptr,
      phantom:   PhantomData
    }
  }

  /// Resumes the generator and return the next value it yields.
  /// If the generator function has returned, returns `None`.
  #[inline]
  pub fn resume(&mut self, input: Input) -> Option<Output> {
    match self.state {
      State::Runnable => {
        // Set the state to Unavailable. Since we have exclusive access to the generator,
        // the only case where this matters is the generator function panics, after which
        // it must not be invocable again.
        self.state = State::Unavailable;

        // Switch to the generator function, and retrieve the yielded value.
        let val = unsafe {
          let (data_out, stack_ptr) = arch::swap(&input as *const Input as usize, self.stack_ptr, Some(&self.stack.inner));
          self.stack_ptr = stack_ptr;
          mem::forget(input);
          ptr::read(data_out as *const Option<Output>)
        };

        // Unless the generator function has returned, it can be switched to again, so
        // set the state to Runnable.
        if val.is_some() { self.state = State::Runnable }

        val
      }
      State::Unavailable => None
    }
  }

  /// Returns the state of the generator.
  #[inline]
  pub fn state(&self) -> State { self.state }

  /// Extracts the stack from a generator when the generator function has returned.
  /// If the generator function has not returned
  /// (i.e. `self.state() == State::Runnable`), panics.
  pub fn unwrap(self) -> Stack {
    match self.state {
      State::Runnable => {
        mem::forget(self);
        panic!("Argh! Bastard! Don't touch that!")
      }
      State::Unavailable => unsafe { self.unsafe_unwrap() }
    }
  }

  /// Extracts the stack from a generator without checking if the generator function has returned.
  /// This will leave any pointers into the generator stack dangling, and won't run destructors.
  pub unsafe fn unsafe_unwrap(mut self) -> Stack {
    ptr::drop_in_place(&mut self.stack_id.inner);
    let stack = ptr::read(&mut self.stack.inner);
    mem::forget(self);
    stack
  }
}

impl<'a, Input, Output, Stack> Drop for Generator<'a, Input, Output, Stack>
    where Input: 'a, Output: 'a, Stack: stack::Stack {
  fn drop(&mut self) {
    unsafe {
      ptr::drop_in_place(&mut self.stack_id.inner);
      match self.state {
        State::Runnable    => panic!("dropped unfinished Generator"),
        State::Unavailable => ptr::drop_in_place(&mut self.stack.inner)
      }
    }
  }
}

/// Yielder is an interface provided to every generator through which it
/// returns a value.
#[derive(Debug)]
pub struct Yielder<Input, Output> {
  stack_ptr: Cell<StackPointer>,
  phantom: PhantomData<(*const Input, *mut Output)>
}

impl<Input, Output> Yielder<Input, Output> {
  fn new(stack_ptr: StackPointer) -> Yielder<Input, Output> {
    Yielder {
      stack_ptr: Cell::new(stack_ptr),
      phantom: PhantomData
    }
  }

  #[inline(always)]
  fn suspend_bare(&self, val: Option<Output>) -> Input {
    unsafe {
      let (data, stack_ptr) = arch::swap(&val as *const Option<Output> as usize, self.stack_ptr.get(), None);
      self.stack_ptr.set(stack_ptr);
      mem::forget(val);
      ptr::read(data as *const Input)
    }
  }

  /// Suspends the generator and returns `Some(item)` from the `resume()`
  /// invocation that resumed the generator.
  #[inline(always)]
  pub fn suspend(&self, item: Output) -> Input {
    self.suspend_bare(Some(item))
  }
}

impl<'a, Output, Stack> Iterator for Generator<'a, (), Output, Stack>
    where Output: 'a, Stack: stack::Stack {
  type Item = Output;

  fn next(&mut self) -> Option<Self::Item> { self.resume(()) }
}
<|endoftext|>


<commit_before>use sapper::{Request, Response, Result};

pub use sapper::PathParams;
pub use sapper_query::QueryParams;
pub use sapper_body::FormParams;
pub use sapper_body::JsonParams;
pub use sapper_session::{ SessionVal, set_cookie };
pub use sapper_tmpl::{
    Context as WebContext,
    TERA,
    TeraResult,
    TeraValue,
    to_value,
    render,
};
pub use sapper_logger::splog;

pub fn init(req: &mut Request, cookie_key: Option<&'static str>) -> Result<()> {
    sapper_logger::init(req)?;
    sapper_query::parse(req)?;
    sapper_body::parse(req)?;
    sapper_session::session_val(req, cookie_key)?;

    Ok(())
}

pub fn finish(req: &Request, res: &mut Response) -> Result<()> {
    sapper_logger::splog(req, res.status())?;

    Ok(())
}

// ============ Status Code ============

#[macro_export]
macro_rules! res_redirect {
    ($redirect_uri:expr) => ({
        use sapper::Response;
        use sapper::status;
        use sapper::header::Location;

        let mut response = Response::new();

        response.set_status(status::Found);
        //response.set_status(status::TemporaryRedirect);
        response.headers_mut().set(Location($redirect_uri.to_owned()));
        response.write_body(format!("redirect to {}", $redirect_uri));

        Ok(response)
    })
}

#[macro_export]
macro_rules! res_400 {
    ($info:expr) => ({
        Err(SapperError::Break($info.to_string()))
    })
}

#[macro_export]
macro_rules! res_404 {
    () => ({
        Err(SapperError::NotFound)
    })
}

#[macro_export]
macro_rules! res_500 {
    ($info:expr) => ({
        Err(SapperError::InternalServerError($info.to_string()))
    })
}

#[macro_export]
macro_rules! set_response_redirect {
    ($response:expr, $redirect_uri:expr) => ({
        use sapper::status;
        use sapper::header::Location;

        $response.set_status(status::Found);
        //response.set_status(status::TemporaryRedirect);
        $response.headers_mut().set(Location($redirect_uri.to_owned()));
        $response.write_body(format!("redirect to {}", $redirect_uri));
    })
}

// ============ Json ============

#[macro_export]
macro_rules! res_json {
    ($json:expr) => ({
        use serde_json;
        use sapper::Response;
        use sapper::header::ContentType;

        let mut response = Response::new();
        response.headers_mut().set(ContentType::json());
        response.write_body(serde_json::to_string(&$json).unwrap());

        Ok(response)
    })
}

#[macro_export]
macro_rules! res_json_ok {
    ($info:expr) => ({
        use sapper::Response;
        use serde_json;

        let json2ret = json!({
            "success": true,
            "info": $info
        });

        res_json!(json2ret)
    })
}

#[macro_export]
macro_rules! res_json_error {
    ($info:expr) => ({
        use sapper::Response;
        use serde_json;

        let json2ret = json!({
            "success": false,
            "info": $info
        });

        res_json!(json2ret)
    })
}

// ============ Page Render ============

#[macro_export]
macro_rules! res_html {
    ($html:expr, $context:expr) => ({
        use sapper::Response;
        use sapper::header::ContentType;

        let res_str = render($html, $context);

        let mut response = Response::new();
        response.headers_mut().set(ContentType::html());
        response.write_body(res_str);

        Ok(response)
    })
}

#[macro_export]
macro_rules! res_html_before {
    ($context:expr) => ({
        Err(SapperError::CustomHtml($context))
    })
}

#[macro_export]
macro_rules! res_xml_string {
    ($context:expr) => ({
        use sapper::Response;
        use sapper::status::StatusCode;

        let mut res = Response::new();
        res.set_status(StatusCode::Ok);
        res.headers_mut().set_raw("Content-Type", vec!["text/xml".as_bytes().to_vec()]);
        res.write_body($context);

        Ok(res)
    })
}

// ============ Params ============

#[macro_export]
macro_rules! get_params {
    ($req:expr, $tykey:ty) => ({
        match $req.ext().get::<$tykey>() {
            Some(params) => {
                params
            },
            None => {
                let info = "no params";
                info!("{}", info);
                return res_400!(info);
            }
        }
    })
}

#[macro_export]
macro_rules! get_path_params {
    ($req:expr) => ({
        get_params!($req, PathParams)
    })
}

#[macro_export]
macro_rules! get_query_params {
    ($req:expr) => ({
        get_params!($req, QueryParams)
    })
}

#[macro_export]
macro_rules! get_form_params {
    ($req:expr) => ({
        get_params!($req, FormParams)
    })
}

#[macro_export]
macro_rules! get_json_params {
    ($req:expr) => ({
        match serde_json::from_value(get_params!($req, JsonParams).clone()) {
            Ok(val) => val,
            Err(_) => {
                let info = "Json parameter not match to struct.";
                info!("{}", info);
                return res_400!(info);
            }
        }
    })
}

#[macro_export]
macro_rules! t_cond {
    ($bool:expr, $prompt:expr) => ({
        match $bool {
            true => (),
            false => {
                let info = format!("test param condition result: {}", $prompt);
                info!("{}", info);
                return res_400!(info);
            }
        }
    })
}

#[macro_export]
macro_rules! _missing_or_unrecognized {
    ($field:expr) => ({
        let info = format!("missing or unrecognized parameter {}.", $field);
        info!("{}",info);
        return res_400!(info);
    })
}

#[macro_export]
macro_rules! _using_default {
    ($field:expr, $default:expr) => ({
        info!("missing or unrecognized parameter {}, using default {}.", $field, $default);
        // return default
        $default
    })
}

// for PathParams, QueryParams and FormParams
#[macro_export]
macro_rules! t_param {
    ($params:expr, $field:expr) => ({
        match $params.get($field) {
            Some(ref astr) => &astr[0],
            None =>  _missing_or_unrecognized! ($field)
        }
    })
}

// for PathParams, QueryParams and FormParams, default version
#[macro_export]
macro_rules! t_param_default {
    ($params:expr, $field:expr, $default:expr) => ({
        match $params.get($field) {
            Some(ref astr) => &astr[0],
            None =>  _using_default! ($field, $default)
        }
    })
}

#[macro_export]
macro_rules! t_param_parse {
    ($params:expr, $field:expr, $tykey:ty) => ({
        match $params.get($field) {
            Some(ref astr) => {
                let _t = &astr[0];
                match _t.parse::<$tykey>() {
                    Ok(output) => output,
                    Err(_) => {
                        let info = format!("parse parameter type error {}.", $field);
                        info!("{}", info);
                        return res_400!(info);
                    }
                }
            },
            None =>  _missing_or_unrecognized! ($field)
        }
    })
}

#[macro_export]
macro_rules! t_param_parse_default {
    ($params:expr, $field:expr, $tykey:ty, $default:expr) => ({
        match $params.get($field) {
            Some(ref astr) => {
                let _t = &astr[0];
                match _t.parse::<$tykey>() {
                    Ok(output) => output,
                    Err(_) => {
                        _using_default! ($field, $default)
                    }
                }
            },
            None =>  _using_default! ($field, $default)
        }
    })
}

#[macro_export]
macro_rules! t_arr_param {
    ($params:expr, $field:expr) => ({
        match $params.get($field) {
            Some(ref arr) => *arr,
            None =>  _missing_or_unrecognized! ($field)
        }
    })
}

#[macro_export]
macro_rules! t_arr_param_default {
    ($params:expr, $field:expr, $default:expr) => ({
        match $params.get($field) {
            Some(ref arr) => *arr,
            None =>  _using_default! ($field, $default)
        }
    })
}

#[macro_export]
macro_rules! t_has_param {
    ($params:expr, $field:expr) => ({
        match $params.get($field) {
            Some(_) => true,
            None => false
        }
    })
}

// ============ Ext Type Params ============
#[macro_export]
macro_rules! ext_type {
    ($req:expr, $tykey:ty) => ({
        match $req.ext().get::<$tykey>() {
            Some(entity) => {
                Some(entity)
            },
            None => None
        }
    })
}

#[macro_export]
macro_rules! ext_type_owned {
    ($req:expr, $tykey:ty) => ({
        match $req.ext().get::<$tykey>() {
            Some(entity) => {
                Some(entity.clone())
            },
            None => None
        }
    })
}

<commit_msg>fix path errors.<commit_after>use sapper::{Request, Response, Result};

pub use sapper::PathParams;
pub use sapper_query::QueryParams;
pub use sapper_body::FormParams;
pub use sapper_body::JsonParams;
pub use sapper_session::{ SessionVal, set_cookie };
pub use sapper_tmpl::{
    Context as WebContext,
    TERA,
    TeraResult,
    TeraValue,
    to_value,
    render,
};
pub use sapper_logger::splog;

pub fn init(req: &mut Request, cookie_key: Option<&'static str>) -> Result<()> {
    sapper_logger::init(req)?;
    sapper_query::parse(req)?;
    sapper_body::parse(req)?;
    sapper_session::session_val(req, cookie_key)?;

    Ok(())
}

pub fn finish(req: &Request, res: &mut Response) -> Result<()> {
    sapper_logger::splog(req, res.status())?;

    Ok(())
}

// ============ Status Code ============

#[macro_export]
macro_rules! res_redirect {
    ($redirect_uri:expr) => ({
        use sapper::Response;
        use sapper::status;
        use sapper::header::Location;

        let mut response = Response::new();

        response.set_status(status::Found);
        //response.set_status(status::TemporaryRedirect);
        response.headers_mut().set(Location($redirect_uri.to_owned()));
        response.write_body(format!("redirect to {}", $redirect_uri));

        Ok(response)
    })
}

#[macro_export]
macro_rules! res_400 {
    ($info:expr) => ({
        Err(sapper::Error::Break($info.to_string()))
    })
}

#[macro_export]
macro_rules! res_404 {
    () => ({
        Err(sapper::Error::NotFound)
    })
}

#[macro_export]
macro_rules! res_500 {
    ($info:expr) => ({
        Err(sapper::Error::InternalServerError($info.to_string()))
    })
}

#[macro_export]
macro_rules! set_response_redirect {
    ($response:expr, $redirect_uri:expr) => ({
        use sapper::status;
        use sapper::header::Location;

        $response.set_status(status::Found);
        //response.set_status(status::TemporaryRedirect);
        $response.headers_mut().set(Location($redirect_uri.to_owned()));
        $response.write_body(format!("redirect to {}", $redirect_uri));
    })
}

// ============ Json ============

#[macro_export]
macro_rules! res_json {
    ($json:expr) => ({
        use serde_json;
        use sapper::Response;
        use sapper::header::ContentType;

        let mut response = Response::new();
        response.headers_mut().set(ContentType::json());
        response.write_body(serde_json::to_string(&$json).unwrap());

        Ok(response)
    })
}

#[macro_export]
macro_rules! res_json_ok {
    ($info:expr) => ({
        use sapper::Response;
        use serde_json;

        let json2ret = json!({
            "success": true,
            "info": $info
        });

        res_json!(json2ret)
    })
}

#[macro_export]
macro_rules! res_json_error {
    ($info:expr) => ({
        use sapper::Response;
        use serde_json;

        let json2ret = json!({
            "success": false,
            "info": $info
        });

        res_json!(json2ret)
    })
}

// ============ Page Render ============

#[macro_export]
macro_rules! res_html {
    ($html:expr, $context:expr) => ({
        use sapper::Response;
        use sapper::header::ContentType;

        let res_str = render($html, $context);

        let mut response = Response::new();
        response.headers_mut().set(ContentType::html());
        response.write_body(res_str);

        Ok(response)
    })
}

#[macro_export]
macro_rules! res_html_before {
    ($context:expr) => ({
        Err(sapper::Error::CustomHtml($context))
    })
}

#[macro_export]
macro_rules! res_xml_string {
    ($context:expr) => ({
        use sapper::Response;
        use sapper::status::StatusCode;

        let mut res = Response::new();
        res.set_status(StatusCode::Ok);
        res.headers_mut().set_raw("Content-Type", vec!["text/xml".as_bytes().to_vec()]);
        res.write_body($context);

        Ok(res)
    })
}

// ============ Params ============

#[macro_export]
macro_rules! get_params {
    ($req:expr, $tykey:ty) => ({
        match $req.ext().get::<$tykey>() {
            Some(params) => {
                params
            },
            None => {
                let info = "no params";
                info!("{}", info);
                return res_400!(info);
            }
        }
    })
}

#[macro_export]
macro_rules! get_path_params {
    ($req:expr) => ({
        get_params!($req, PathParams)
    })
}

#[macro_export]
macro_rules! get_query_params {
    ($req:expr) => ({
        get_params!($req, QueryParams)
    })
}

#[macro_export]
macro_rules! get_form_params {
    ($req:expr) => ({
        get_params!($req, FormParams)
    })
}

#[macro_export]
macro_rules! get_json_params {
    ($req:expr) => ({
        match serde_json::from_value(get_params!($req, JsonParams).clone()) {
            Ok(val) => val,
            Err(_) => {
                let info = "Json parameter not match to struct.";
                info!("{}", info);
                return res_400!(info);
            }
        }
    })
}

#[macro_export]
macro_rules! t_cond {
    ($bool:expr, $prompt:expr) => ({
        match $bool {
            true => (),
            false => {
                let info = format!("test param condition result: {}", $prompt);
                info!("{}", info);
                return res_400!(info);
            }
        }
    })
}

#[macro_export]
macro_rules! _missing_or_unrecognized {
    ($field:expr) => ({
        let info = format!("missing or unrecognized parameter {}.", $field);
        info!("{}",info);
        return res_400!(info);
    })
}

#[macro_export]
macro_rules! _using_default {
    ($field:expr, $default:expr) => ({
        info!("missing or unrecognized parameter {}, using default {}.", $field, $default);
        // return default
        $default
    })
}

// for PathParams, QueryParams and FormParams
#[macro_export]
macro_rules! t_param {
    ($params:expr, $field:expr) => ({
        match $params.get($field) {
            Some(ref astr) => &astr[0],
            None =>  _missing_or_unrecognized! ($field)
        }
    })
}

// for PathParams, QueryParams and FormParams, default version
#[macro_export]
macro_rules! t_param_default {
    ($params:expr, $field:expr, $default:expr) => ({
        match $params.get($field) {
            Some(ref astr) => &astr[0],
            None =>  _using_default! ($field, $default)
        }
    })
}

#[macro_export]
macro_rules! t_param_parse {
    ($params:expr, $field:expr, $tykey:ty) => ({
        match $params.get($field) {
            Some(ref astr) => {
                let _t = &astr[0];
                match _t.parse::<$tykey>() {
                    Ok(output) => output,
                    Err(_) => {
                        let info = format!("parse parameter type error {}.", $field);
                        info!("{}", info);
                        return res_400!(info);
                    }
                }
            },
            None =>  _missing_or_unrecognized! ($field)
        }
    })
}

#[macro_export]
macro_rules! t_param_parse_default {
    ($params:expr, $field:expr, $tykey:ty, $default:expr) => ({
        match $params.get($field) {
            Some(ref astr) => {
                let _t = &astr[0];
                match _t.parse::<$tykey>() {
                    Ok(output) => output,
                    Err(_) => {
                        _using_default! ($field, $default)
                    }
                }
            },
            None =>  _using_default! ($field, $default)
        }
    })
}

#[macro_export]
macro_rules! t_arr_param {
    ($params:expr, $field:expr) => ({
        match $params.get($field) {
            Some(ref arr) => *arr,
            None =>  _missing_or_unrecognized! ($field)
        }
    })
}

#[macro_export]
macro_rules! t_arr_param_default {
    ($params:expr, $field:expr, $default:expr) => ({
        match $params.get($field) {
            Some(ref arr) => *arr,
            None =>  _using_default! ($field, $default)
        }
    })
}

#[macro_export]
macro_rules! t_has_param {
    ($params:expr, $field:expr) => ({
        match $params.get($field) {
            Some(_) => true,
            None => false
        }
    })
}

// ============ Ext Type Params ============
#[macro_export]
macro_rules! ext_type {
    ($req:expr, $tykey:ty) => ({
        match $req.ext().get::<$tykey>() {
            Some(entity) => {
                Some(entity)
            },
            None => None
        }
    })
}

#[macro_export]
macro_rules! ext_type_owned {
    ($req:expr, $tykey:ty) => ({
        match $req.ext().get::<$tykey>() {
            Some(entity) => {
                Some(entity.clone())
            },
            None => None
        }
    })
}

<|endoftext|>


<commit_before>use core::char;

/// An optional event
#[derive(Copy, Clone)]
pub enum EventOption {
    /// A mouse event
    Mouse(MouseEvent),
    /// A key event
    Key(KeyEvent),
    /// An unknown event
    Unknown(Event),
    /// No event
    None,
}

/// An event
#[derive(Copy, Clone)]
#[repr(packed)]
pub struct Event {
    pub code: char,
    pub a: isize,
    pub b: isize,
    pub c: isize,
}

impl Event {
    /// Create a null event
    pub fn new() -> Event {
        Event {
            code: '\0',
            a: 0,
            b: 0,
            c: 0,
        }
    }

    /// Convert the event ot an optional event
    // TODO: Consider doing this via a From trait.
    pub fn to_option(self) -> EventOption {
        match self.code {
            'm' => EventOption::Mouse(MouseEvent::from_event(self)),
            'k' => EventOption::Key(KeyEvent::from_event(self)),
            '\0' => EventOption::None,
            _ => EventOption::Unknown(self),
        }
    }
}

/// A event related to the mouse
#[derive(Copy, Clone)]
pub struct MouseEvent {
    /// The x coordinate of the mouse
    pub x: isize,
    /// The y coordinate of the mouse
    pub y: isize,
    /// Was the left button pressed?
    pub left_button: bool,
    /// Was the right button pressed?
    pub right_button: bool,
    /// Was the middle button pressed?
    pub middle_button: bool,
}

impl MouseEvent {
    /// Convert to an `Event`
    pub fn to_event(&self) -> Event {
        Event {
            code: 'm',
            a: self.x,
            b: self.y,
            c: self.left_button as isize | (self.right_button as isize) << 1 | (self.right_button as isize) << 2,
        }
    }

    /// Convert an `Event` to a `MouseEvent`
    pub fn from_event(event: Event) -> MouseEvent {
        MouseEvent {
            x: event.a,
            y: event.b,
            left_button: event.c & 1 == 1,
            middle_button: event.c & 4 == 4,
            right_button: event.c & 2 == 2,
        }
    }
}

/// Escape key
pub const K_ESC: u8 = 0x01;
/// Backspace key
pub const K_BKSP: u8 = 0x0E;
/// Tab key
pub const K_TAB: u8 = 0x0F;
/// Control key
pub const K_CTRL: u8 = 0x1D;
/// Alt key
pub const K_ALT: u8 = 0x38;
/// F1 key
pub const K_F1: u8 = 0x3B;
/// F2 key
pub const K_F2: u8 = 0x3C;
/// F3 key
pub const K_F3: u8 = 0x3D;
/// F4 key
pub const K_F4: u8 = 0x3E;
/// F5 key
pub const K_F5: u8 = 0x3F;
/// F6 key
pub const K_F6: u8 = 0x40;
/// F7 key
pub const K_F7: u8 = 0x41;
/// F8 key
pub const K_F8: u8 = 0x42;
/// F9 key
pub const K_F9: u8 = 0x43;
/// F10 key
pub const K_F10: u8 = 0x44;
/// Home key
pub const K_HOME: u8 = 0x47;
/// Up key
pub const K_UP: u8 = 0x48;
/// Page up key
pub const K_PGUP: u8 = 0x49;
/// Left key
pub const K_LEFT: u8 = 0x4B;
/// Right key
pub const K_RIGHT: u8 = 0x4D;
/// End key
pub const K_END: u8 = 0x4F;
/// Down key
pub const K_DOWN: u8 = 0x50;
/// Page down key
pub const K_PGDN: u8 = 0x51;
/// Delete key
pub const K_DEL: u8 = 0x53;
/// F11 key
pub const K_F11: u8 = 0x57;
/// F12 key
pub const K_F12: u8 = 0x58;
/// Left shift
pub const K_LEFT_SHIFT: u8 = 0x2A;
/// Right shift
pub const K_RIGHT_SHIFT: u8 = 0x36;

/// A key event (such as a pressed key)
#[derive(Copy, Clone)]
pub struct KeyEvent {
    /// The charecter of the key
    pub character: char,
    /// The scancode of the key
    pub scancode: u8,
    /// Was it pressed?
    pub pressed: bool,
}

impl KeyEvent {
    /// Convert to an `Event`
    pub fn to_event(&self) -> Event {
        Event {
            code: 'k',
            a: self.character as isize,
            b: self.scancode as isize,
            c: self.pressed as isize,
        }
    }

    /// Convert from an `Event`
    pub fn from_event(event: Event) -> KeyEvent {
        let mut pr_enc = false;
        KeyEvent {
            character: match event.b as u8 {
                K_BKSP => '\u{0008}',
                K_ESC => '\u{001B}',
                K_TAB => '\t',
                K_CTRL => '\u{0080}',
                K_LEFT_SHIFT | K_RIGHT_SHIFT => {
                    if event.c > 0 {
                        '\u{000E}'
                    } else {
                        pr_enc = true;
                        '\u{000F}'
                    }
                },
                K_DEL => {
                    '\u{007F}'
                },
                _ => char::from_u32(event.a as u32).unwrap_or('\0'),
            },
            scancode: event.b as u8,
            pressed: if pr_enc { true } else { event.c > 0 },
        }
    }
}
<commit_msg>Roll back unicode changes<commit_after>use core::char;

/// An optional event
#[derive(Copy, Clone)]
pub enum EventOption {
    /// A mouse event
    Mouse(MouseEvent),
    /// A key event
    Key(KeyEvent),
    /// An unknown event
    Unknown(Event),
    /// No event
    None,
}

/// An event
#[derive(Copy, Clone)]
#[repr(packed)]
pub struct Event {
    pub code: char,
    pub a: isize,
    pub b: isize,
    pub c: isize,
}

impl Event {
    /// Create a null event
    pub fn new() -> Event {
        Event {
            code: '\0',
            a: 0,
            b: 0,
            c: 0,
        }
    }

    /// Convert the event ot an optional event
    // TODO: Consider doing this via a From trait.
    pub fn to_option(self) -> EventOption {
        match self.code {
            'm' => EventOption::Mouse(MouseEvent::from_event(self)),
            'k' => EventOption::Key(KeyEvent::from_event(self)),
            '\0' => EventOption::None,
            _ => EventOption::Unknown(self),
        }
    }
}

/// A event related to the mouse
#[derive(Copy, Clone)]
pub struct MouseEvent {
    /// The x coordinate of the mouse
    pub x: isize,
    /// The y coordinate of the mouse
    pub y: isize,
    /// Was the left button pressed?
    pub left_button: bool,
    /// Was the right button pressed?
    pub right_button: bool,
    /// Was the middle button pressed?
    pub middle_button: bool,
}

impl MouseEvent {
    /// Convert to an `Event`
    pub fn to_event(&self) -> Event {
        Event {
            code: 'm',
            a: self.x,
            b: self.y,
            c: self.left_button as isize | (self.right_button as isize) << 1 | (self.right_button as isize) << 2,
        }
    }

    /// Convert an `Event` to a `MouseEvent`
    pub fn from_event(event: Event) -> MouseEvent {
        MouseEvent {
            x: event.a,
            y: event.b,
            left_button: event.c & 1 == 1,
            middle_button: event.c & 4 == 4,
            right_button: event.c & 2 == 2,
        }
    }
}

/// Escape key
pub const K_ESC: u8 = 0x01;
/// Backspace key
pub const K_BKSP: u8 = 0x0E;
/// Tab key
pub const K_TAB: u8 = 0x0F;
/// Control key
pub const K_CTRL: u8 = 0x1D;
/// Alt key
pub const K_ALT: u8 = 0x38;
/// F1 key
pub const K_F1: u8 = 0x3B;
/// F2 key
pub const K_F2: u8 = 0x3C;
/// F3 key
pub const K_F3: u8 = 0x3D;
/// F4 key
pub const K_F4: u8 = 0x3E;
/// F5 key
pub const K_F5: u8 = 0x3F;
/// F6 key
pub const K_F6: u8 = 0x40;
/// F7 key
pub const K_F7: u8 = 0x41;
/// F8 key
pub const K_F8: u8 = 0x42;
/// F9 key
pub const K_F9: u8 = 0x43;
/// F10 key
pub const K_F10: u8 = 0x44;
/// Home key
pub const K_HOME: u8 = 0x47;
/// Up key
pub const K_UP: u8 = 0x48;
/// Page up key
pub const K_PGUP: u8 = 0x49;
/// Left key
pub const K_LEFT: u8 = 0x4B;
/// Right key
pub const K_RIGHT: u8 = 0x4D;
/// End key
pub const K_END: u8 = 0x4F;
/// Down key
pub const K_DOWN: u8 = 0x50;
/// Page down key
pub const K_PGDN: u8 = 0x51;
/// Delete key
pub const K_DEL: u8 = 0x53;
/// F11 key
pub const K_F11: u8 = 0x57;
/// F12 key
pub const K_F12: u8 = 0x58;
/// Left shift
pub const K_LEFT_SHIFT: u8 = 0x2A;
/// Right shift
pub const K_RIGHT_SHIFT: u8 = 0x36;

/// A key event (such as a pressed key)
#[derive(Copy, Clone)]
pub struct KeyEvent {
    /// The charecter of the key
    pub character: char,
    /// The scancode of the key
    pub scancode: u8,
    /// Was it pressed?
    pub pressed: bool,
}

impl KeyEvent {
    /// Convert to an `Event`
    pub fn to_event(&self) -> Event {
        Event {
            code: 'k',
            a: self.character as isize,
            b: self.scancode as isize,
            c: self.pressed as isize,
        }
    }

    /// Convert from an `Event`
    pub fn from_event(event: Event) -> KeyEvent {
        KeyEvent {
            character: char::from_u32(event.a as u32).unwrap_or('\0'),
            scancode: event.b as u8,
            pressed: event.c > 0,
        }
    }
}
<|endoftext|>


<commit_before>use std::ffi::{CString, CStr};
use std::marker::PhantomData;
use std::path::Path;
use std::ptr;

use libc::c_char;

use openexr_sys::*;

use error::*;
use frame_buffer::FrameBuffer;
use Header;

#[allow(dead_code)]
pub struct InputFile<'a> {
    handle: *mut CEXR_InputFile,
    header_ref: Header,
    istream: Option<IStream<'a>>,
    _phantom: PhantomData<CEXR_InputFile>,
}

impl<'a> InputFile<'a> {
    pub fn new(path: &Path) -> Result<InputFile<'static>> {
        let c_path = CString::new(path.to_str()
                                      .expect("non-unicode path handling is unimplemented")
                                      .as_bytes())
                .unwrap();
        let mut error_out = ptr::null();
        let mut out = ptr::null_mut();
        let error =
            unsafe { CEXR_InputFile_from_file(c_path.as_ptr(), 1, &mut out, &mut error_out) };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(InputFile {
                   handle: out,
                   header_ref: Header {
                       // NOTE: We're casting to *mut here to satisfy the
                       // field's type, but importantly we only return a
                       // const & of the Header so it retains const semantics.
                       handle: unsafe { CEXR_InputFile_header(out) } as *mut CEXR_Header,
                       owned: false,
                       _phantom: PhantomData,
                   },
                   istream: None,
                   _phantom: PhantomData,
               })
        }
    }

    pub fn from_slice(slice: &'a [u8]) -> Result<InputFile<'a>> {
        let istream = IStream::from_slice(slice);
        let mut error_out = ptr::null();
        let mut out = ptr::null_mut();
        let error =
            unsafe { CEXR_InputFile_from_stream(istream.handle, 1, &mut out, &mut error_out) };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(InputFile {
                   handle: out,
                   header_ref: Header {
                       // NOTE: We're casting to *mut here to satisfy the
                       // field's type, but importantly we only return a
                       // const & of the Header so it retains const semantics.
                       handle: unsafe { CEXR_InputFile_header(out) } as *mut CEXR_Header,
                       owned: false,
                       _phantom: PhantomData,
                   },
                   istream: Some(istream),
                   _phantom: PhantomData,
               })
        }
    }

    pub fn read_pixels(&self, framebuffer: &mut FrameBuffer) -> Result<()> {
        let w = self.header().data_window();
        if (w.max.x - w.min.x) as usize != framebuffer.dimensions().0 - 1 ||
           (w.max.y - w.min.y) as usize != framebuffer.dimensions().1 - 1 {
            panic!("framebuffer size {}x{} does not match input file dimensions {}x{}",
                   framebuffer.dimensions().0,
                   framebuffer.dimensions().1,
                   w.max.x - w.min.x,
                   w.max.y - w.min.y)
        }

        let mut error_out = ptr::null();

        let error = unsafe {
            CEXR_InputFile_set_framebuffer(self.handle, framebuffer.handle_mut(), &mut error_out)
        };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            return Err(Error::Generic(msg.to_string_lossy().into_owned()));
        }

        let error =
            unsafe { CEXR_InputFile_read_pixels(self.handle, w.min.y, w.max.y, &mut error_out) };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(())
        }
    }

    pub fn header(&self) -> &Header {
        &self.header_ref
    }
}

impl<'a> Drop for InputFile<'a> {
    fn drop(&mut self) {
        unsafe { CEXR_InputFile_delete(self.handle) };
    }
}

struct IStream<'a> {
    handle: *mut CEXR_IStream,
    _phantom: PhantomData<&'a ()>,
}

impl<'a> IStream<'a> {
    fn from_slice(slice: &'a [u8]) -> IStream<'a> {
        IStream {
            handle: unsafe {
                CEXR_IStream_from_memory(b"in-memory data\0".as_ptr() as *const c_char,
                                         slice.as_ptr() as *mut u8 as *mut c_char,
                                         slice.len())
            },
            _phantom: PhantomData,
        }
    }
}

impl<'a> Drop for IStream<'a> {
    fn drop(&mut self) {
        unsafe { CEXR_IStream_delete(self.handle) };
    }
}
<commit_msg>Add doc example to `InputFile`<commit_after>use std::ffi::{CString, CStr};
use std::marker::PhantomData;
use std::path::Path;
use std::ptr;

use libc::c_char;

use openexr_sys::*;

use error::*;
use frame_buffer::FrameBuffer;
use Header;

/// Common input interface for all types of OpenEXR files
///
/// # Examples
/// ```rust,no_run
/// # use openexr::{InputFile, FrameBuffer};
/// # use std::path::Path;
/// # let path = "/path/to/file.exr";
/// # let path = Path::new(&path);
/// let file = InputFile::new(path).unwrap();
/// let window = file.header().data_window();
/// let width = window.max.x - window.min.x + 1;
/// let height = window.max.y - window.min.y + 1;
///
/// let mut pixel_data: Vec<[f32; 4]> = vec![[0.0, 0.0, 0.0, 0.0]; (width*height) as usize];
/// let mut fb = FrameBuffer::new(width as usize, height as usize);
/// fb.insert_pixels(&[("R", 0.0), ("G", 0.0), ("B", 0.0), ("A", 0.0)], &mut pixel_data);
/// file.read_pixels(&mut fb).unwrap();
/// ```
#[allow(dead_code)]
pub struct InputFile<'a> {
    handle: *mut CEXR_InputFile,
    header_ref: Header,
    istream: Option<IStream<'a>>,
    _phantom: PhantomData<CEXR_InputFile>,
}

impl<'a> InputFile<'a> {
    pub fn new(path: &Path) -> Result<InputFile<'static>> {
        let c_path = CString::new(path.to_str()
                                      .expect("non-unicode path handling is unimplemented")
                                      .as_bytes())
                .unwrap();
        let mut error_out = ptr::null();
        let mut out = ptr::null_mut();
        let error =
            unsafe { CEXR_InputFile_from_file(c_path.as_ptr(), 1, &mut out, &mut error_out) };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(InputFile {
                   handle: out,
                   header_ref: Header {
                       // NOTE: We're casting to *mut here to satisfy the
                       // field's type, but importantly we only return a
                       // const & of the Header so it retains const semantics.
                       handle: unsafe { CEXR_InputFile_header(out) } as *mut CEXR_Header,
                       owned: false,
                       _phantom: PhantomData,
                   },
                   istream: None,
                   _phantom: PhantomData,
               })
        }
    }

    pub fn from_slice(slice: &'a [u8]) -> Result<InputFile<'a>> {
        let istream = IStream::from_slice(slice);
        let mut error_out = ptr::null();
        let mut out = ptr::null_mut();
        let error =
            unsafe { CEXR_InputFile_from_stream(istream.handle, 1, &mut out, &mut error_out) };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(InputFile {
                   handle: out,
                   header_ref: Header {
                       // NOTE: We're casting to *mut here to satisfy the
                       // field's type, but importantly we only return a
                       // const & of the Header so it retains const semantics.
                       handle: unsafe { CEXR_InputFile_header(out) } as *mut CEXR_Header,
                       owned: false,
                       _phantom: PhantomData,
                   },
                   istream: Some(istream),
                   _phantom: PhantomData,
               })
        }
    }

    pub fn read_pixels(&self, framebuffer: &mut FrameBuffer) -> Result<()> {
        let w = self.header().data_window();
        if (w.max.x - w.min.x) as usize != framebuffer.dimensions().0 - 1 ||
           (w.max.y - w.min.y) as usize != framebuffer.dimensions().1 - 1 {
            panic!("framebuffer size {}x{} does not match input file dimensions {}x{}",
                   framebuffer.dimensions().0,
                   framebuffer.dimensions().1,
                   w.max.x - w.min.x,
                   w.max.y - w.min.y)
        }

        let mut error_out = ptr::null();

        let error = unsafe {
            CEXR_InputFile_set_framebuffer(self.handle, framebuffer.handle_mut(), &mut error_out)
        };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            return Err(Error::Generic(msg.to_string_lossy().into_owned()));
        }

        let error =
            unsafe { CEXR_InputFile_read_pixels(self.handle, w.min.y, w.max.y, &mut error_out) };
        if error != 0 {
            let msg = unsafe { CStr::from_ptr(error_out) };
            Err(Error::Generic(msg.to_string_lossy().into_owned()))
        } else {
            Ok(())
        }
    }

    pub fn header(&self) -> &Header {
        &self.header_ref
    }
}

impl<'a> Drop for InputFile<'a> {
    fn drop(&mut self) {
        unsafe { CEXR_InputFile_delete(self.handle) };
    }
}

struct IStream<'a> {
    handle: *mut CEXR_IStream,
    _phantom: PhantomData<&'a ()>,
}

impl<'a> IStream<'a> {
    fn from_slice(slice: &'a [u8]) -> IStream<'a> {
        IStream {
            handle: unsafe {
                CEXR_IStream_from_memory(b"in-memory data\0".as_ptr() as *const c_char,
                                         slice.as_ptr() as *mut u8 as *mut c_char,
                                         slice.len())
            },
            _phantom: PhantomData,
        }
    }
}

impl<'a> Drop for IStream<'a> {
    fn drop(&mut self) {
        unsafe { CEXR_IStream_delete(self.handle) };
    }
}
<|endoftext|>


<commit_before>use std::boxed::Box;
use std::cmp;
use std::collections::BTreeSet;
use std::ops::Range;

pub struct ItemList {
    clipping_range_max_len: usize,
    clipping_range_start: usize,
    highlighted_row: usize,
    line_indices: Box<Indices>,
    marked_line_indices: BTreeSet<usize>,
}

impl ItemList {
    pub fn new(clipping_range_max_len: usize) -> Self {
        ItemList {
            clipping_range_max_len: clipping_range_max_len,
            clipping_range_start: 0,
            highlighted_row: 0,
            line_indices: Box::new(0..0),
            marked_line_indices: BTreeSet::new(),
        }
    }

    pub fn highlighted_row(&self) -> usize {
        self.highlighted_row
    }

    pub fn len(&self) -> usize {
        self.line_indices.count()
    }

    pub fn line_indices_in_clipping_range(&self) -> Vec<usize> {
        self.line_indices.boxed_iter()
            .skip(self.clipping_range_start)
            .take(self.clipping_range_end())
            .collect()
    }

    pub fn selected_line_indices(&self) -> Vec<usize> {
        match self.marked_line_indices.len() {
            0 => {
                match self.clipping_range_len() {
                    0 => Vec::new(),
                    _ => {
                        let i = self.clipping_range_start + self.highlighted_row;
                        vec![self.line_indices.at(i)]
                    }
                }
            }
            _ => self.marked_line_indices.iter().cloned().collect(),
        }
    }

    pub fn marked_rows(&self) -> Vec<usize> {
        self.line_indices_in_clipping_range().iter()
            .enumerate()
            .filter_map(|(i, idx)| {
                if self.marked_line_indices.contains(idx) { Some(i) } else { None }
            })
            .collect()
    }

    pub fn toggle_mark(&mut self) {
        if self.clipping_range_len() > 0 {
            let i = self.clipping_range_start + self.highlighted_row;
            let line_index = self.line_indices.at(i);
            if !self.marked_line_indices.remove(&line_index) {
                self.marked_line_indices.insert(line_index);
            }
        }
    }

    pub fn set_line_indices(&mut self, line_indices: Vec<usize>) {
        self.set_line_indices_with_box::<Vec<usize>>(Box::new(line_indices));
    }

    pub fn set_line_index_range(&mut self, range: Range<usize>) {
        self.set_line_indices_with_box::<Range<usize>>(Box::new(range));
    }

    pub fn move_highlight_backward(&mut self) {
        if self.highlighted_row == 0 {
            self.scroll_backward();
            return;
        }
        self.highlighted_row -= 1;
    }

    pub fn move_highlight_forward(&mut self) {
        if Some(self.highlighted_row) == self.max_row() {
            self.scroll_forward();
            return;
        }
        self.highlighted_row += 1;
    }

    pub fn scroll_backward(&mut self) {
        if self.clipping_range_start > 0 {
            self.clipping_range_start -= 1;
        }
    }

    pub fn scroll_forward(&mut self) {
        if self.clipping_range_end() < self.len() {
            self.clipping_range_start += 1;
        }
    }

    fn set_line_indices_with_box<T: Indices>(&mut self, line_indices: Box<Indices>) {
        self.line_indices = line_indices;
        let len = self.len();
        let range_end = self.clipping_range_end();
        if len < range_end {
            let diff = range_end - len;
            if diff > self.clipping_range_start {
                self.clipping_range_start = 0;
            } else {
                self.clipping_range_start -= diff;
            }
        }
        if let Some(max_row) = self.max_row() {
            if self.highlighted_row > max_row {
                self.highlighted_row = max_row;
            }
        }
    }

    fn clipping_range_end(&self) -> usize {
        self.clipping_range_start + self.clipping_range_len()
    }

    fn clipping_range_len(&self) -> usize {
        cmp::min(self.len(), self.clipping_range_max_len)
    }

    fn max_row(&self) -> Option<usize> {
        match self.clipping_range_len() {
            0 => None,
            i => Some(i - 1),
        }
    }
}

pub trait Indices {
    fn at(&self, i: usize) -> usize;
    fn boxed_iter<'a>(&'a self) -> Box<Iterator<Item=usize> + 'a>;
    fn count(&self) -> usize;
}

impl Indices for Range<usize> {
    fn at(&self, i: usize) -> usize {
        i
    }

    fn boxed_iter<'a>(&'a self) -> Box<Iterator<Item=usize> + 'a> {
        Box::new(self.clone())
    }

    fn count(&self) -> usize {
        self.clone().count()
    }
}

impl Indices for Vec<usize> {
    fn at(&self, i: usize) -> usize {
        unsafe { *self.get_unchecked(i) }
    }

    fn boxed_iter<'a>(&'a self) -> Box<Iterator<Item=usize> + 'a> {
        Box::new(self.iter().map(|i| *i))
    }

    fn count(&self) -> usize {
        self.len()
    }
}
<commit_msg>Add assertion to Indices::at()<commit_after>use std::boxed::Box;
use std::cmp;
use std::collections::BTreeSet;
use std::ops::Range;

pub struct ItemList {
    clipping_range_max_len: usize,
    clipping_range_start: usize,
    highlighted_row: usize,
    line_indices: Box<Indices>,
    marked_line_indices: BTreeSet<usize>,
}

impl ItemList {
    pub fn new(clipping_range_max_len: usize) -> Self {
        ItemList {
            clipping_range_max_len: clipping_range_max_len,
            clipping_range_start: 0,
            highlighted_row: 0,
            line_indices: Box::new(0..0),
            marked_line_indices: BTreeSet::new(),
        }
    }

    pub fn highlighted_row(&self) -> usize {
        self.highlighted_row
    }

    pub fn len(&self) -> usize {
        self.line_indices.count()
    }

    pub fn line_indices_in_clipping_range(&self) -> Vec<usize> {
        self.line_indices.boxed_iter()
            .skip(self.clipping_range_start)
            .take(self.clipping_range_end())
            .collect()
    }

    pub fn selected_line_indices(&self) -> Vec<usize> {
        match self.marked_line_indices.len() {
            0 => {
                match self.clipping_range_len() {
                    0 => Vec::new(),
                    _ => {
                        let i = self.clipping_range_start + self.highlighted_row;
                        vec![self.line_indices.at(i)]
                    }
                }
            }
            _ => self.marked_line_indices.iter().cloned().collect(),
        }
    }

    pub fn marked_rows(&self) -> Vec<usize> {
        self.line_indices_in_clipping_range().iter()
            .enumerate()
            .filter_map(|(i, idx)| {
                if self.marked_line_indices.contains(idx) { Some(i) } else { None }
            })
            .collect()
    }

    pub fn toggle_mark(&mut self) {
        if self.clipping_range_len() > 0 {
            let i = self.clipping_range_start + self.highlighted_row;
            let line_index = self.line_indices.at(i);
            if !self.marked_line_indices.remove(&line_index) {
                self.marked_line_indices.insert(line_index);
            }
        }
    }

    pub fn set_line_indices(&mut self, line_indices: Vec<usize>) {
        self.set_line_indices_with_box::<Vec<usize>>(Box::new(line_indices));
    }

    pub fn set_line_index_range(&mut self, range: Range<usize>) {
        self.set_line_indices_with_box::<Range<usize>>(Box::new(range));
    }

    pub fn move_highlight_backward(&mut self) {
        if self.highlighted_row == 0 {
            self.scroll_backward();
            return;
        }
        self.highlighted_row -= 1;
    }

    pub fn move_highlight_forward(&mut self) {
        if Some(self.highlighted_row) == self.max_row() {
            self.scroll_forward();
            return;
        }
        self.highlighted_row += 1;
    }

    pub fn scroll_backward(&mut self) {
        if self.clipping_range_start > 0 {
            self.clipping_range_start -= 1;
        }
    }

    pub fn scroll_forward(&mut self) {
        if self.clipping_range_end() < self.len() {
            self.clipping_range_start += 1;
        }
    }

    fn set_line_indices_with_box<T: Indices>(&mut self, line_indices: Box<Indices>) {
        self.line_indices = line_indices;
        let len = self.len();
        let range_end = self.clipping_range_end();
        if len < range_end {
            let diff = range_end - len;
            if diff > self.clipping_range_start {
                self.clipping_range_start = 0;
            } else {
                self.clipping_range_start -= diff;
            }
        }
        if let Some(max_row) = self.max_row() {
            if self.highlighted_row > max_row {
                self.highlighted_row = max_row;
            }
        }
    }

    fn clipping_range_end(&self) -> usize {
        self.clipping_range_start + self.clipping_range_len()
    }

    fn clipping_range_len(&self) -> usize {
        cmp::min(self.len(), self.clipping_range_max_len)
    }

    fn max_row(&self) -> Option<usize> {
        match self.clipping_range_len() {
            0 => None,
            i => Some(i - 1),
        }
    }
}

pub trait Indices {
    fn at(&self, i: usize) -> usize;
    fn boxed_iter<'a>(&'a self) -> Box<Iterator<Item=usize> + 'a>;
    fn count(&self) -> usize;
}

impl Indices for Range<usize> {
    fn at(&self, i: usize) -> usize {
        assert!(i < self.end);
        i
    }

    fn boxed_iter<'a>(&'a self) -> Box<Iterator<Item=usize> + 'a> {
        Box::new(self.clone())
    }

    fn count(&self) -> usize {
        self.clone().count()
    }
}

impl Indices for Vec<usize> {
    fn at(&self, i: usize) -> usize {
        unsafe { *self.get_unchecked(i) }
    }

    fn boxed_iter<'a>(&'a self) -> Box<Iterator<Item=usize> + 'a> {
        Box::new(self.iter().map(|i| *i))
    }

    fn count(&self) -> usize {
        self.len()
    }
}
<|endoftext|>


<commit_before>use std::path::{Path, PathBuf};
use serde_json;
use directory::error::{OpenReadError, DeleteError, OpenWriteError};
use directory::{ReadOnlySource, WritePtr};
use std::result;
use std::io;
use Directory;
use std::sync::{Arc, RwLock};
use std::collections::HashSet;
use std::sync::RwLockWriteGuard;
use std::io::Write;
use core::MANAGED_FILEPATH;
use std::collections::HashMap;
use std::fmt;
use Result;
use Error;

/// Wrapper of directories that keeps track of files created by Tantivy.
///
/// A managed directory is just a wrapper of a directory
/// that keeps a (persisted) list of the files that
/// have been created (and not deleted) by tantivy so far.
///
/// Thanks to this list, it implements a `garbage_collect` method
/// that removes the files that were created by tantivy and are not
/// useful anymore.
#[derive(Debug)]
pub struct ManagedDirectory {
    directory: Box<Directory>,
    meta_informations: Arc<RwLock<MetaInformation>>,
}

#[derive(Debug, Default)]
struct MetaInformation {
    managed_paths: HashSet<PathBuf>,
    protected_files: HashMap<PathBuf, usize>,
}

/// A `FileProtection` prevents the garbage collection of a file.
///
/// See `ManagedDirectory.protect_file_from_delete`.
pub struct FileProtection {
    directory: ManagedDirectory,
    path: PathBuf,
}

fn unprotect_file_from_delete(directory: &ManagedDirectory, path: &Path) {
    let mut meta_informations_wlock = directory
        .meta_informations
        .write()
        .expect("Managed file lock poisoned");
    if let Some(counter_ref_mut) = meta_informations_wlock.protected_files.get_mut(path) {
        (*counter_ref_mut) -= 1;
    }
}

impl fmt::Debug for FileProtection {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        write!(formatter, "FileProtectionFor({:?})", self.path)
    }
}

impl Drop for FileProtection {
    fn drop(&mut self) {
        unprotect_file_from_delete(&self.directory, &*self.path);
    }
}

/// Saves the file containing the list of existing files
/// that were created by tantivy.
fn save_managed_paths(directory: &mut Directory,
                      wlock: &RwLockWriteGuard<MetaInformation>)
                      -> io::Result<()> {
    let mut w = serde_json::to_vec(&wlock.managed_paths)?;
    write!(&mut w, "\n")?;
    directory.atomic_write(&MANAGED_FILEPATH, &w[..])?;
    Ok(())
}

impl ManagedDirectory {
    /// Wraps a directory as managed directory.
    pub fn new<Dir: Directory>(directory: Dir) -> Result<ManagedDirectory> {
        match directory.atomic_read(&MANAGED_FILEPATH) {
            Ok(data) => {
                let managed_files_json = String::from_utf8_lossy(&data);
                let managed_files: HashSet<PathBuf> =
                    serde_json::from_str(&managed_files_json)
                        .map_err(|e| Error::CorruptedFile(MANAGED_FILEPATH.clone(), Box::new(e)))?;
                Ok(ManagedDirectory {
                       directory: box directory,
                       meta_informations: Arc::new(RwLock::new(MetaInformation {
                                                                   managed_paths: managed_files,
                                                                   protected_files:
                                                                       HashMap::default(),
                                                               })),
                   })
            }
            Err(OpenReadError::FileDoesNotExist(_)) => {
                Ok(ManagedDirectory {
                       directory: box directory,
                       meta_informations: Arc::default(),
                   })
            }
            Err(OpenReadError::IOError(e)) => Err(From::from(e)),
        }
    }

    /// Garbage collect unused files.
    ///
    /// Removes the files that were created by `tantivy` and are not
    /// used by any segment anymore.
    ///
    /// * `living_files` - List of files that are still used by the index.
    ///
    /// This method does not panick nor returns errors.
    /// If a file cannot be deleted (for permission reasons for instance)
    /// an error is simply logged, and the file remains in the list of managed
    /// files.
    pub fn garbage_collect(&mut self, living_files: HashSet<PathBuf>) {
        let mut files_to_delete = vec![];
        {
            // releasing the lock as .delete() will use it too.
            let meta_informations_rlock =
                self.meta_informations
                    .read()
                    .expect("Managed directory rlock poisoned in garbage collect.");
            for managed_path in &meta_informations_rlock.managed_paths {
                if !living_files.contains(managed_path) {
                    files_to_delete.push(managed_path.clone());
                }
            }
        }

        let mut deleted_files = vec![];
        {
            for file_to_delete in files_to_delete {
                match self.delete(&file_to_delete) {
                    Ok(_) => {
                        info!("Deleted {:?}", file_to_delete);
                        deleted_files.push(file_to_delete);
                    }
                    Err(file_error) => {
                        match file_error {
                            DeleteError::FileDoesNotExist(_) => {
                                deleted_files.push(file_to_delete);
                            }
                            DeleteError::IOError(_) => {
                                if !cfg!(target_os = "windows") {
                                    // On windows, delete is expected to fail if the file
                                    // is mmapped.
                                    error!("Failed to delete {:?}", file_to_delete);
                                }
                            }
                            DeleteError::FileProtected(_) => {
                                // this is expected.
                            }
                        }

                    }
                }
            }
        }

        if !deleted_files.is_empty() {
            // update the list of managed files by removing
            // the file that were removed.
            let mut meta_informations_wlock = self.meta_informations
                .write()
                .expect("Managed directory wlock poisoned (2).");
            {
                let managed_paths_write = &mut meta_informations_wlock.managed_paths;
                for delete_file in &deleted_files {
                    managed_paths_write.remove(delete_file);
                }
            }
            if let Err(_) = save_managed_paths(self.directory.as_mut(), &meta_informations_wlock) {
                error!("Failed to save the list of managed files.");
            }
        }

    }

    /// Protects a file from being garbage collected.
    ///
    /// The method returns a `FileProtection` object.
    /// The file will not be garbage collected as long as the
    /// `FileProtection` object is kept alive.
    pub fn protect_file_from_delete(&self, path: &Path) -> FileProtection {
        let pathbuf = path.to_owned();
        {
            let mut meta_informations_wlock = self.meta_informations
                .write()
                .expect("Managed file lock poisoned on protect");
            *meta_informations_wlock
                 .protected_files
                 .entry(pathbuf.clone())
                 .or_insert(0) += 1;
        }
        FileProtection {
            directory: self.clone(),
            path: pathbuf.clone(),
        }
    }

    /// Registers a file as managed
    ///
    /// This method must be called before the file is
    /// actually created to ensure that a failure between
    /// registering the filepath and creating the file
    /// will not lead to garbage files that will
    /// never get removed.
    fn register_file_as_managed(&mut self, filepath: &Path) -> io::Result<()> {
        let mut meta_wlock = self.meta_informations
            .write()
            .expect("Managed file lock poisoned");
        let has_changed = meta_wlock.managed_paths.insert(filepath.to_owned());
        if has_changed {
            save_managed_paths(self.directory.as_mut(), &meta_wlock)?;
        }
        Ok(())
    }
}

impl Directory for ManagedDirectory {
    fn open_read(&self, path: &Path) -> result::Result<ReadOnlySource, OpenReadError> {
        self.directory.open_read(path)
    }

    fn open_write(&mut self, path: &Path) -> result::Result<WritePtr, OpenWriteError> {
        self.register_file_as_managed(path)?;
        self.directory.open_write(path)
    }

    fn atomic_write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
        self.register_file_as_managed(path)?;
        self.directory.atomic_write(path, data)
    }

    fn atomic_read(&self, path: &Path) -> result::Result<Vec<u8>, OpenReadError> {
        self.directory.atomic_read(path)
    }

    fn delete(&self, path: &Path) -> result::Result<(), DeleteError> {
        {
            let metas_rlock = self.meta_informations
                .read()
                .expect("poisoned lock in managed directory meta");
            if let Some(counter) = metas_rlock.protected_files.get(path) {
                if *counter > 0 {
                    return Err(DeleteError::FileProtected(path.to_owned()));
                }
            }
        }
        self.directory.delete(path)
    }

    fn exists(&self, path: &Path) -> bool {
        self.directory.exists(path)
    }

    fn box_clone(&self) -> Box<Directory> {
        box self.clone()
    }
}

impl Clone for ManagedDirectory {
    fn clone(&self) -> ManagedDirectory {
        ManagedDirectory {
            directory: self.directory.box_clone(),
            meta_informations: self.meta_informations.clone(),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use directory::MmapDirectory;
    use std::path::Path;
    use std::io::Write;
    use tempdir::TempDir;

    lazy_static! {
        static ref TEST_PATH1: &'static Path = Path::new("some_path_for_test");
        static ref TEST_PATH2: &'static Path = Path::new("some_path_for_test2");
    }

    #[test]
    fn test_managed_directory() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        {
            let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
            let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
            {
                let mut write_file = managed_directory.open_write(*TEST_PATH1).unwrap();
                write_file.flush().unwrap();
            }
            {
                managed_directory
                    .atomic_write(*TEST_PATH2, &vec![0u8, 1u8])
                    .unwrap();
            }
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(managed_directory.exists(*TEST_PATH2));
            }
            {
                let living_files: HashSet<PathBuf> =
                    [TEST_PATH1.to_owned()].into_iter().cloned().collect();
                managed_directory.garbage_collect(living_files);
            }
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
        }
        {
            let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
            let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
            {
                let living_files: HashSet<PathBuf> = HashSet::new();
                managed_directory.garbage_collect(living_files);
            }
            {
                assert!(!managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
        }
    }

    #[test]
    fn test_managed_directory_gc_while_mmapped() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        let living_files = HashSet::new();

        let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
        let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
        managed_directory
            .atomic_write(*TEST_PATH1, &vec![0u8, 1u8])
            .unwrap();
        assert!(managed_directory.exists(*TEST_PATH1));

        let _mmap_read = managed_directory.open_read(*TEST_PATH1).unwrap();
        managed_directory.garbage_collect(living_files.clone());
        if cfg!(target_os = "windows") {
            // On Windows, gc should try and fail the file as it is mmapped.
            assert!(managed_directory.exists(*TEST_PATH1));
            // unmap should happen here.
            drop(_mmap_read);
            // The file should still be in the list of managed file and
            // eventually be deleted once mmap is released.
            managed_directory.garbage_collect(living_files);
            assert!(!managed_directory.exists(*TEST_PATH1));
        } else {
            assert!(!managed_directory.exists(*TEST_PATH1));
        }

    }

    #[test]
    fn test_managed_directory_protect() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        let living_files = HashSet::new();

        let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
        let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
        managed_directory
            .atomic_write(*TEST_PATH1, &vec![0u8, 1u8])
            .unwrap();
        assert!(managed_directory.exists(*TEST_PATH1));

        {
            let _file_protection = managed_directory.protect_file_from_delete(*TEST_PATH1);
            managed_directory.garbage_collect(living_files.clone());
            assert!(managed_directory.exists(*TEST_PATH1));
        }

        managed_directory.garbage_collect(living_files.clone());
        assert!(!managed_directory.exists(*TEST_PATH1));

    }

}
<commit_msg>clippy: fix if_let_redundant_pattern_matching warnings<commit_after>use std::path::{Path, PathBuf};
use serde_json;
use directory::error::{OpenReadError, DeleteError, OpenWriteError};
use directory::{ReadOnlySource, WritePtr};
use std::result;
use std::io;
use Directory;
use std::sync::{Arc, RwLock};
use std::collections::HashSet;
use std::sync::RwLockWriteGuard;
use std::io::Write;
use core::MANAGED_FILEPATH;
use std::collections::HashMap;
use std::fmt;
use Result;
use Error;

/// Wrapper of directories that keeps track of files created by Tantivy.
///
/// A managed directory is just a wrapper of a directory
/// that keeps a (persisted) list of the files that
/// have been created (and not deleted) by tantivy so far.
///
/// Thanks to this list, it implements a `garbage_collect` method
/// that removes the files that were created by tantivy and are not
/// useful anymore.
#[derive(Debug)]
pub struct ManagedDirectory {
    directory: Box<Directory>,
    meta_informations: Arc<RwLock<MetaInformation>>,
}

#[derive(Debug, Default)]
struct MetaInformation {
    managed_paths: HashSet<PathBuf>,
    protected_files: HashMap<PathBuf, usize>,
}

/// A `FileProtection` prevents the garbage collection of a file.
///
/// See `ManagedDirectory.protect_file_from_delete`.
pub struct FileProtection {
    directory: ManagedDirectory,
    path: PathBuf,
}

fn unprotect_file_from_delete(directory: &ManagedDirectory, path: &Path) {
    let mut meta_informations_wlock = directory
        .meta_informations
        .write()
        .expect("Managed file lock poisoned");
    if let Some(counter_ref_mut) = meta_informations_wlock.protected_files.get_mut(path) {
        (*counter_ref_mut) -= 1;
    }
}

impl fmt::Debug for FileProtection {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
        write!(formatter, "FileProtectionFor({:?})", self.path)
    }
}

impl Drop for FileProtection {
    fn drop(&mut self) {
        unprotect_file_from_delete(&self.directory, &*self.path);
    }
}

/// Saves the file containing the list of existing files
/// that were created by tantivy.
fn save_managed_paths(directory: &mut Directory,
                      wlock: &RwLockWriteGuard<MetaInformation>)
                      -> io::Result<()> {
    let mut w = serde_json::to_vec(&wlock.managed_paths)?;
    write!(&mut w, "\n")?;
    directory.atomic_write(&MANAGED_FILEPATH, &w[..])?;
    Ok(())
}

impl ManagedDirectory {
    /// Wraps a directory as managed directory.
    pub fn new<Dir: Directory>(directory: Dir) -> Result<ManagedDirectory> {
        match directory.atomic_read(&MANAGED_FILEPATH) {
            Ok(data) => {
                let managed_files_json = String::from_utf8_lossy(&data);
                let managed_files: HashSet<PathBuf> =
                    serde_json::from_str(&managed_files_json)
                        .map_err(|e| Error::CorruptedFile(MANAGED_FILEPATH.clone(), Box::new(e)))?;
                Ok(ManagedDirectory {
                       directory: box directory,
                       meta_informations: Arc::new(RwLock::new(MetaInformation {
                                                                   managed_paths: managed_files,
                                                                   protected_files:
                                                                       HashMap::default(),
                                                               })),
                   })
            }
            Err(OpenReadError::FileDoesNotExist(_)) => {
                Ok(ManagedDirectory {
                       directory: box directory,
                       meta_informations: Arc::default(),
                   })
            }
            Err(OpenReadError::IOError(e)) => Err(From::from(e)),
        }
    }

    /// Garbage collect unused files.
    ///
    /// Removes the files that were created by `tantivy` and are not
    /// used by any segment anymore.
    ///
    /// * `living_files` - List of files that are still used by the index.
    ///
    /// This method does not panick nor returns errors.
    /// If a file cannot be deleted (for permission reasons for instance)
    /// an error is simply logged, and the file remains in the list of managed
    /// files.
    pub fn garbage_collect(&mut self, living_files: HashSet<PathBuf>) {
        let mut files_to_delete = vec![];
        {
            // releasing the lock as .delete() will use it too.
            let meta_informations_rlock =
                self.meta_informations
                    .read()
                    .expect("Managed directory rlock poisoned in garbage collect.");
            for managed_path in &meta_informations_rlock.managed_paths {
                if !living_files.contains(managed_path) {
                    files_to_delete.push(managed_path.clone());
                }
            }
        }

        let mut deleted_files = vec![];
        {
            for file_to_delete in files_to_delete {
                match self.delete(&file_to_delete) {
                    Ok(_) => {
                        info!("Deleted {:?}", file_to_delete);
                        deleted_files.push(file_to_delete);
                    }
                    Err(file_error) => {
                        match file_error {
                            DeleteError::FileDoesNotExist(_) => {
                                deleted_files.push(file_to_delete);
                            }
                            DeleteError::IOError(_) => {
                                if !cfg!(target_os = "windows") {
                                    // On windows, delete is expected to fail if the file
                                    // is mmapped.
                                    error!("Failed to delete {:?}", file_to_delete);
                                }
                            }
                            DeleteError::FileProtected(_) => {
                                // this is expected.
                            }
                        }

                    }
                }
            }
        }

        if !deleted_files.is_empty() {
            // update the list of managed files by removing
            // the file that were removed.
            let mut meta_informations_wlock = self.meta_informations
                .write()
                .expect("Managed directory wlock poisoned (2).");
            {
                let managed_paths_write = &mut meta_informations_wlock.managed_paths;
                for delete_file in &deleted_files {
                    managed_paths_write.remove(delete_file);
                }
            }
            if save_managed_paths(self.directory.as_mut(), &meta_informations_wlock).is_err() {
                error!("Failed to save the list of managed files.");
            }
        }

    }

    /// Protects a file from being garbage collected.
    ///
    /// The method returns a `FileProtection` object.
    /// The file will not be garbage collected as long as the
    /// `FileProtection` object is kept alive.
    pub fn protect_file_from_delete(&self, path: &Path) -> FileProtection {
        let pathbuf = path.to_owned();
        {
            let mut meta_informations_wlock = self.meta_informations
                .write()
                .expect("Managed file lock poisoned on protect");
            *meta_informations_wlock
                 .protected_files
                 .entry(pathbuf.clone())
                 .or_insert(0) += 1;
        }
        FileProtection {
            directory: self.clone(),
            path: pathbuf.clone(),
        }
    }

    /// Registers a file as managed
    ///
    /// This method must be called before the file is
    /// actually created to ensure that a failure between
    /// registering the filepath and creating the file
    /// will not lead to garbage files that will
    /// never get removed.
    fn register_file_as_managed(&mut self, filepath: &Path) -> io::Result<()> {
        let mut meta_wlock = self.meta_informations
            .write()
            .expect("Managed file lock poisoned");
        let has_changed = meta_wlock.managed_paths.insert(filepath.to_owned());
        if has_changed {
            save_managed_paths(self.directory.as_mut(), &meta_wlock)?;
        }
        Ok(())
    }
}

impl Directory for ManagedDirectory {
    fn open_read(&self, path: &Path) -> result::Result<ReadOnlySource, OpenReadError> {
        self.directory.open_read(path)
    }

    fn open_write(&mut self, path: &Path) -> result::Result<WritePtr, OpenWriteError> {
        self.register_file_as_managed(path)?;
        self.directory.open_write(path)
    }

    fn atomic_write(&mut self, path: &Path, data: &[u8]) -> io::Result<()> {
        self.register_file_as_managed(path)?;
        self.directory.atomic_write(path, data)
    }

    fn atomic_read(&self, path: &Path) -> result::Result<Vec<u8>, OpenReadError> {
        self.directory.atomic_read(path)
    }

    fn delete(&self, path: &Path) -> result::Result<(), DeleteError> {
        {
            let metas_rlock = self.meta_informations
                .read()
                .expect("poisoned lock in managed directory meta");
            if let Some(counter) = metas_rlock.protected_files.get(path) {
                if *counter > 0 {
                    return Err(DeleteError::FileProtected(path.to_owned()));
                }
            }
        }
        self.directory.delete(path)
    }

    fn exists(&self, path: &Path) -> bool {
        self.directory.exists(path)
    }

    fn box_clone(&self) -> Box<Directory> {
        box self.clone()
    }
}

impl Clone for ManagedDirectory {
    fn clone(&self) -> ManagedDirectory {
        ManagedDirectory {
            directory: self.directory.box_clone(),
            meta_informations: self.meta_informations.clone(),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::*;
    use directory::MmapDirectory;
    use std::path::Path;
    use std::io::Write;
    use tempdir::TempDir;

    lazy_static! {
        static ref TEST_PATH1: &'static Path = Path::new("some_path_for_test");
        static ref TEST_PATH2: &'static Path = Path::new("some_path_for_test2");
    }

    #[test]
    fn test_managed_directory() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        {
            let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
            let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
            {
                let mut write_file = managed_directory.open_write(*TEST_PATH1).unwrap();
                write_file.flush().unwrap();
            }
            {
                managed_directory
                    .atomic_write(*TEST_PATH2, &vec![0u8, 1u8])
                    .unwrap();
            }
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(managed_directory.exists(*TEST_PATH2));
            }
            {
                let living_files: HashSet<PathBuf> =
                    [TEST_PATH1.to_owned()].into_iter().cloned().collect();
                managed_directory.garbage_collect(living_files);
            }
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
        }
        {
            let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
            let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
            {
                assert!(managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
            {
                let living_files: HashSet<PathBuf> = HashSet::new();
                managed_directory.garbage_collect(living_files);
            }
            {
                assert!(!managed_directory.exists(*TEST_PATH1));
                assert!(!managed_directory.exists(*TEST_PATH2));
            }
        }
    }

    #[test]
    fn test_managed_directory_gc_while_mmapped() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        let living_files = HashSet::new();

        let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
        let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
        managed_directory
            .atomic_write(*TEST_PATH1, &vec![0u8, 1u8])
            .unwrap();
        assert!(managed_directory.exists(*TEST_PATH1));

        let _mmap_read = managed_directory.open_read(*TEST_PATH1).unwrap();
        managed_directory.garbage_collect(living_files.clone());
        if cfg!(target_os = "windows") {
            // On Windows, gc should try and fail the file as it is mmapped.
            assert!(managed_directory.exists(*TEST_PATH1));
            // unmap should happen here.
            drop(_mmap_read);
            // The file should still be in the list of managed file and
            // eventually be deleted once mmap is released.
            managed_directory.garbage_collect(living_files);
            assert!(!managed_directory.exists(*TEST_PATH1));
        } else {
            assert!(!managed_directory.exists(*TEST_PATH1));
        }

    }

    #[test]
    fn test_managed_directory_protect() {
        let tempdir = TempDir::new("index").unwrap();
        let tempdir_path = PathBuf::from(tempdir.path());
        let living_files = HashSet::new();

        let mmap_directory = MmapDirectory::open(&tempdir_path).unwrap();
        let mut managed_directory = ManagedDirectory::new(mmap_directory).unwrap();
        managed_directory
            .atomic_write(*TEST_PATH1, &vec![0u8, 1u8])
            .unwrap();
        assert!(managed_directory.exists(*TEST_PATH1));

        {
            let _file_protection = managed_directory.protect_file_from_delete(*TEST_PATH1);
            managed_directory.garbage_collect(living_files.clone());
            assert!(managed_directory.exists(*TEST_PATH1));
        }

        managed_directory.garbage_collect(living_files.clone());
        assert!(!managed_directory.exists(*TEST_PATH1));

    }

}
<|endoftext|>