<commit_before>use features::{BeliefState};

use rand::{Rng};
use std::rc::{Rc};

#[derive(Clone)]
pub struct ReplayFrame<F, Action, Res> {
  pub action:   Action,
  pub res:      Option<Res>,
  pub next_obs: Rc<F>,
  pub terminal: bool,
}

#[derive(Clone)]
pub struct ReplayEntry<F, Action, Res> {
  pub prev:     BeliefState<F>,
  pub action:   Action,
  pub res:      Option<Res>,
  pub next:     BeliefState<F>,
  pub terminal: bool,
  pub idx:      usize,
}

pub struct LinearReplayCache<F, Action, Res> {
  history_len:  usize,
  //frame_dim:    (usize, usize, usize),
  capacity:     usize,
  head:         usize,
  frames:       Vec<ReplayFrame<F, Action, Res>>,
}

impl<F, Action, Res> LinearReplayCache<F, Action, Res> where Action: Copy, Res: Copy {
  pub fn new(history_len: usize, /*frame_dim: (usize, usize, usize),*/ capacity: usize) -> Self {
    LinearReplayCache{
      history_len:  history_len,
      //frame_dim:    frame_dim,
      capacity:     capacity,
      head:         0,
      frames:       Vec::with_capacity(capacity),
    }
  }

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

  pub fn insert(&mut self, action: Action, res: Option<Res>, next_obs: Rc<F>, terminal: bool) {
    self.push(action, res, next_obs, terminal);
  }

  pub fn push(&mut self, action: Action, res: Option<Res>, next_obs: Rc<F>, terminal: bool) {
    let new_frame = ReplayFrame{
      action:   action,
      res:      res,
      next_obs: next_obs,
      terminal: terminal,
    };
    assert!(self.head < self.capacity);
    if self.head >= self.frames.len() {
      self.frames.push(new_frame);
    } else {
      self.frames[self.head] = new_frame;
    }
    assert!(self.frames.len() <= self.capacity);
    self.head = (self.head + 1) % self.capacity;
  }

  fn build_state(&self, idx: usize) -> BeliefState<F> {
    let mut state = BeliefState::new(Some(self.history_len), /*self.frame_dim*/);
    /*if idx >= self.history_len - 1 {
      for offset in 0 .. self.history_len {
        state.push(self.frames[idx - (self.history_len - 1) + offset].next_obs.clone());
      }
    } else {*/
    for offset in 0 .. self.history_len {
      let offset_idx = (self.frames.len() + idx - (self.history_len - 1) + offset) % self.frames.len();
      state.push(self.frames[offset_idx].next_obs.clone());
    }
    //}
    state
  }

  pub fn sample<R>(&self, rng: &mut R) -> ReplayEntry<F, Action, Res> where R: Rng {
    assert!(self.frames.len() >= self.history_len + 1);
    assert!(self.frames.len() <= self.capacity);
    let mut idx = 0;
    'outer_loop : loop {
      idx = rng.gen_range(0, self.frames.len());
      let last_idx = (self.frames.len() + self.head - 1) % self.frames.len();
      let first_valid_idx = (self.frames.len() + last_idx - (self.frames.len() - 1) + self.history_len) % self.frames.len();
      if (first_valid_idx < last_idx && idx >= first_valid_idx && idx <= last_idx) ||
          (last_idx < first_valid_idx && (idx >= first_valid_idx || idx <= last_idx)) {
        for offset in 0 .. self.history_len {
          let offset_idx = (self.frames.len() + idx - self.history_len + offset) % self.frames.len();
          if self.frames[offset_idx].terminal {
            continue 'outer_loop;
          }
        }
        break 'outer_loop;
      }
      /*// FIXME(20161029): this index is biased when the replay memory is full,
      // but ignore it for now because the effect should be tiny.
      idx = rng.gen_range(self.history_len, self.frames.len());
      if idx >= self.head && idx < self.head + self.history_len {
        continue;
      }
      for offset in 0 .. self.history_len {
        if self.frames[idx - self.history_len + offset].terminal {
          continue 'outer_loop;
        }
      }
      break;*/
    }
    let prev_state = self.build_state(idx - 1);
    let next_state = self.build_state(idx);
    ReplayEntry{
      prev:     prev_state,
      action:   self.frames[idx].action,
      res:      self.frames[idx].res,
      next:     next_state,
      terminal: self.frames[idx].terminal,
      idx:      idx,
    }
  }
}
<commit_msg>Add shared replay memory based on `MultiEnv`.<commit_after>use features::*;

use rand::{Rng};
use std::rc::{Rc};
use std::sync::{Arc};

#[derive(Clone)]
pub struct ReplayEntry<F, Action, Res> {
  pub prev:     BeliefState<F>,
  pub action:   Action,
  pub res:      Option<Res>,
  pub next:     BeliefState<F>,
  pub terminal: bool,
  pub idx:      usize,
}

#[derive(Clone)]
pub struct ReplayFrame<F, Action, Res> {
  pub action:   Action,
  pub res:      Option<Res>,
  pub next_obs: Rc<F>,
  pub terminal: bool,
}

pub struct LinearReplayCache<F, Action, Res> {
  history_len:  usize,
  //frame_dim:    (usize, usize, usize),
  capacity:     usize,
  head:         usize,
  frames:       Vec<ReplayFrame<F, Action, Res>>,
}

impl<F, Action, Res> LinearReplayCache<F, Action, Res> where Action: Copy, Res: Copy {
  pub fn new(history_len: usize, /*frame_dim: (usize, usize, usize),*/ capacity: usize) -> Self {
    LinearReplayCache{
      history_len:  history_len,
      //frame_dim:    frame_dim,
      capacity:     capacity,
      head:         0,
      frames:       Vec::with_capacity(capacity),
    }
  }

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

  pub fn insert(&mut self, action: Action, res: Option<Res>, next_obs: Rc<F>, terminal: bool) {
    self.push(action, res, next_obs, terminal);
  }

  pub fn push(&mut self, action: Action, res: Option<Res>, next_obs: Rc<F>, terminal: bool) {
    let new_frame = ReplayFrame{
      action:   action,
      res:      res,
      next_obs: next_obs,
      terminal: terminal,
    };
    assert!(self.head < self.capacity);
    if self.head >= self.frames.len() {
      self.frames.push(new_frame);
    } else {
      self.frames[self.head] = new_frame;
    }
    assert!(self.frames.len() <= self.capacity);
    self.head = (self.head + 1) % self.capacity;
  }

  fn build_state(&self, idx: usize) -> BeliefState<F> {
    let mut state = BeliefState::new(Some(self.history_len), /*self.frame_dim*/);
    /*if idx >= self.history_len - 1 {
      for offset in 0 .. self.history_len {
        state.push(self.frames[idx - (self.history_len - 1) + offset].next_obs.clone());
      }
    } else {*/
    for offset in 0 .. self.history_len {
      let offset_idx = (self.frames.len() + idx - (self.history_len - 1) + offset) % self.frames.len();
      state.push(self.frames[offset_idx].next_obs.clone());
    }
    //}
    state
  }

  pub fn sample<R>(&self, rng: &mut R) -> ReplayEntry<F, Action, Res> where R: Rng {
    assert!(self.frames.len() >= self.history_len + 1);
    assert!(self.frames.len() <= self.capacity);
    let mut idx = 0;
    'outer_loop : loop {
      idx = rng.gen_range(0, self.frames.len());
      let last_idx = (self.frames.len() + self.head - 1) % self.frames.len();
      let first_valid_idx = (self.frames.len() + last_idx - (self.frames.len() - 1) + self.history_len) % self.frames.len();
      if (first_valid_idx < last_idx && idx >= first_valid_idx && idx <= last_idx) ||
          (last_idx < first_valid_idx && (idx >= first_valid_idx || idx <= last_idx)) {
        for offset in 0 .. self.history_len {
          let offset_idx = (self.frames.len() + idx - self.history_len + offset) % self.frames.len();
          if self.frames[offset_idx].terminal {
            continue 'outer_loop;
          }
        }
        break 'outer_loop;
      }
      /*// FIXME(20161029): this index is biased when the replay memory is full,
      // but ignore it for now because the effect should be tiny.
      idx = rng.gen_range(self.history_len, self.frames.len());
      if idx >= self.head && idx < self.head + self.history_len {
        continue;
      }
      for offset in 0 .. self.history_len {
        if self.frames[idx - self.history_len + offset].terminal {
          continue 'outer_loop;
        }
      }
      break;*/
    }
    let prev_state = self.build_state(idx - 1);
    let next_state = self.build_state(idx);
    ReplayEntry{
      prev:     prev_state,
      action:   self.frames[idx].action,
      res:      self.frames[idx].res,
      next:     next_state,
      terminal: self.frames[idx].terminal,
      idx:      idx,
    }
  }
}

#[derive(Clone)]
pub struct SharedReplayEntry<State, Action, Res> {
  pub prev:     State,
  pub action:   Action,
  pub res:      Option<Res>,
  pub next:     State,
  pub terminal: bool,
  pub idx:      usize,
}

#[derive(Clone)]
pub struct SharedReplayFrame<Obs, Action, Res> {
  pub action:   Action,
  pub res:      Option<Res>,
  pub next_obs: Arc<Obs>,
  pub terminal: bool,
}

pub struct SharedLinearReplayCache<Obs, Action, Res> {
  history_len:  usize,
  //frame_dim:    (usize, usize, usize),
  capacity:     usize,
  head:         usize,
  frames:       Vec<SharedReplayFrame<Obs, Action, Res>>,
}

impl<F, Action, Res> SharedLinearReplayCache<F, Action, Res> where F: MultiObs, Action: Copy, Res: Copy {
  pub fn new(history_len: usize, /*frame_dim: (usize, usize, usize),*/ capacity: usize) -> Self {
    SharedLinearReplayCache{
      history_len:  history_len,
      //frame_dim:    frame_dim,
      capacity:     capacity,
      head:         0,
      frames:       Vec::with_capacity(capacity),
    }
  }

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

  pub fn insert(&mut self, action: Action, res: Option<Res>, next_obs: Arc<F>, terminal: bool) {
    self.push(action, res, next_obs, terminal);
  }

  pub fn push(&mut self, action: Action, res: Option<Res>, next_obs: Arc<F>, terminal: bool) {
    let new_frame = SharedReplayFrame{
      action:   action,
      res:      res,
      next_obs: next_obs,
      terminal: terminal,
    };
    assert!(self.head < self.capacity);
    if self.head >= self.frames.len() {
      self.frames.push(new_frame);
    } else {
      self.frames[self.head] = new_frame;
    }
    assert!(self.frames.len() <= self.capacity);
    self.head = (self.head + 1) % self.capacity;
  }

  fn build_state<State>(&self, idx: usize) -> State where State: SharedMultiBeliefState<F> {
    //let mut state = BeliefState::new(Some(self.history_len), /*self.frame_dim*/);
    let mut state = State::_build(Some(self.history_len));
    /*if idx >= self.history_len - 1 {
      for offset in 0 .. self.history_len {
        state.push(self.frames[idx - (self.history_len - 1) + offset].next_obs.clone());
      }
    } else {*/
    for offset in 0 .. self.history_len {
      let offset_idx = (self.frames.len() + idx - (self.history_len - 1) + offset) % self.frames.len();
      state.append(self.frames[offset_idx].next_obs.clone());
    }
    //}
    state
  }

  pub fn sample<R, State>(&self, rng: &mut R) -> SharedReplayEntry<State, Action, Res> where R: Rng, State: SharedMultiBeliefState<F> {
    assert!(self.frames.len() >= self.history_len + 1);
    assert!(self.frames.len() <= self.capacity);
    let mut idx = 0;
    'outer_loop : loop {
      idx = rng.gen_range(0, self.frames.len());
      let last_idx = (self.frames.len() + self.head - 1) % self.frames.len();
      let first_valid_idx = (self.frames.len() + last_idx - (self.frames.len() - 1) + self.history_len) % self.frames.len();
      if (first_valid_idx < last_idx && idx >= first_valid_idx && idx <= last_idx) ||
          (last_idx < first_valid_idx && (idx >= first_valid_idx || idx <= last_idx)) {
        for offset in 0 .. self.history_len {
          let offset_idx = (self.frames.len() + idx - self.history_len + offset) % self.frames.len();
          if self.frames[offset_idx].terminal {
            continue 'outer_loop;
          }
        }
        break 'outer_loop;
      }
      /*// FIXME(20161029): this index is biased when the replay memory is full,
      // but ignore it for now because the effect should be tiny.
      idx = rng.gen_range(self.history_len, self.frames.len());
      if idx >= self.head && idx < self.head + self.history_len {
        continue;
      }
      for offset in 0 .. self.history_len {
        if self.frames[idx - self.history_len + offset].terminal {
          continue 'outer_loop;
        }
      }
      break;*/
    }
    let prev_state = self.build_state(idx - 1);
    let next_state = self.build_state(idx);
    SharedReplayEntry{
      prev:     prev_state,
      action:   self.frames[idx].action,
      res:      self.frames[idx].res,
      next:     next_state,
      terminal: self.frames[idx].terminal,
      idx:      idx,
    }
  }
}
<|endoftext|>


<commit_before>use super::{AssertionFailure, Spec};

use std::fmt::Debug;

pub trait ResultAssertions<'s, T, E>
    where T: Debug,
          E: Debug
{
    fn is_ok(&mut self) -> Spec<'s, T>;
    fn is_error(&mut self) -> Spec<'s, E>;
}

pub trait ContainingResultAssertions<T, E>
    where T: Debug,
          E: Debug
{
    fn is_ok_containing(&mut self, expected_value: &T) where T: PartialEq;
    fn is_err_containing(&mut self, expected_value: &E) where E: PartialEq;
}

impl<'s, T, E> ContainingResultAssertions<T, E> for Spec<'s, Result<T, E>>
    where T: Debug,
          E: Debug
{
    /// Asserts that the subject is an `Ok` Result containing the expected value.
    /// The subject type must be a `Result`.
    ///
    /// ```rust,ignore
    /// assert_that(&Result::Ok::<usize, usize>(1)).is_ok_containing(&1);
    /// ```
    fn is_ok_containing(&mut self, expected_value: &T)
        where T: PartialEq
    {
        match *self.subject {
            Ok(ref val) => {
                if !val.eq(expected_value) {
                    AssertionFailure::from_spec(self)
                        .with_expected(build_detail_message("ok", expected_value))
                        .with_actual(build_detail_message("ok", val))
                        .fail();
                }
            }
            Err(ref val) => {
                AssertionFailure::from_spec(self)
                    .with_expected(build_detail_message("ok", expected_value))
                    .with_actual(build_detail_message("err", val))
                    .fail();
            }
        }
    }

    /// Asserts that the subject is an `Err` Result containing the expected value.
    /// The subject type must be a `Result`.
    ///
    /// ```rust,ignore
    /// assert_that(&Result::Err::<usize, usize>(1)).is_err_containing(&1);
    /// ```
    fn is_err_containing(&mut self, expected_value: &E)
        where E: PartialEq
    {
        match *self.subject {
            Err(ref val) => {
                if !val.eq(expected_value) {
                    AssertionFailure::from_spec(self)
                        .with_expected(build_detail_message("err", expected_value))
                        .with_actual(build_detail_message("err", val))
                        .fail();
                }
            }
            Ok(ref val) => {
                AssertionFailure::from_spec(self)
                    .with_expected(build_detail_message("err", expected_value))
                    .with_actual(build_detail_message("ok", val))
                    .fail();
            }
        }
    }
}

fn build_detail_message<T: Debug>(variant: &'static str, value: T) -> String {
    format!("Result[{}] containing <{:?}>", variant, value)
}

impl<'s, T, E> ResultAssertions<'s, T, E> for Spec<'s, Result<T, E>>
    where T: Debug,
          E: Debug
{
    /// Asserts that the subject is `Ok`. The value type must be a `Result`.
    ///
    /// This will return a new `Spec` containing the unwrapped value if it is `Ok`.
    ///
    /// ```rust,ignore
    /// assert_that(&Result::Ok::<usize, usize>(1)).is_ok();
    /// ```
    fn is_ok(&mut self) -> Spec<'s, T> {
        match *self.subject {
            Ok(ref val) => {
                Spec {
                    subject: val,
                    subject_name: self.subject_name,
                    location: self.location.clone(),
                    description: self.description,
                }
            }
            Err(ref err) => {
                AssertionFailure::from_spec(self)
                    .with_expected(format!("result[ok]"))
                    .with_actual(format!("result[error]<{:?}>", err))
                    .fail();

                unreachable!();
            }
        }
    }

    /// Asserts that the subject is `Err`. The value type must be a `Result`.
    ///
    /// This will return a new `Spec` containing the unwrapped value if it is `Err`.
    ///
    /// ```rust,ignore
    /// assert_that(&Result::Err::<usize, usize>(1)).is_error();
    /// ```
    fn is_error(&mut self) -> Spec<'s, E> {
        match *self.subject {
            Err(ref val) => {
                Spec {
                    subject: val,
                    subject_name: self.subject_name,
                    location: self.location.clone(),
                    description: self.description,
                }
            }
            Ok(ref val) => {
                AssertionFailure::from_spec(self)
                    .with_expected(format!("result[error]"))
                    .with_actual(format!("result[ok]<{:?}>", val))
                    .fail();

                unreachable!();
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use super::super::prelude::*;

    #[test]
    fn should_not_panic_if_result_is_expected_to_be_ok_and_is() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_ok();
    }

    #[test]
    #[should_panic(expected = "\n\texpected: result[ok]\n\t but was: result[error]<\"Oh no\">")]
    fn should_panic_if_result_is_expected_to_be_ok_and_is_not() {
        let result: Result<&str, &str> = Err("Oh no");
        assert_that(&result).is_ok();
    }

    #[test]
    fn should_return_unwrapped_value_if_subject_is_ok() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_ok().is_equal_to(&"Hello");
    }

    #[test]
    fn should_not_panic_if_result_is_expected_to_be_error_and_is() {
        let result: Result<&str, &str> = Err("Oh no");
        assert_that(&result).is_error();
    }

    #[test]
    #[should_panic(expected = "\n\texpected: result[error]\n\t but was: result[ok]<\"Hello\">")]
    fn should_panic_if_result_is_expected_to_be_error_and_is_not() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_error();
    }

    #[test]
    fn should_return_unwrapped_value_if_subject_is_err() {
        let result: Result<&str, &str> = Err("Hello");
        assert_that(&result).is_error().is_equal_to(&"Hello");
    }

    #[test]
    fn should_not_panic_if_result_is_ok_with_expected_value() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_ok_containing(&"Hello");
    }

    #[test]
    fn should_not_panic_if_result_is_ok_with_uncomparable_ok() {
        #[derive(Debug)]
        struct Incomporable;
        let result: Result<&str, Incomporable> = Ok("Hello");
        assert_that(&result).is_ok_containing(&"Hello");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: Result[ok] containing <\"Hi\">\
                   \n\t but was: Result[ok] containing <\"Hello\">")]
    fn should_panic_if_result_is_ok_without_expected_value() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_ok_containing(&"Hi");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: Result[ok] containing <\"Hi\">\
                   \n\t but was: Result[err] containing <\"Hi\">")]
    fn should_panic_if_result_is_err_if_ok_with_value_expected() {
        let result: Result<&str, &str> = Err("Hi");
        assert_that(&result).is_ok_containing(&"Hi");
    }

    #[test]
    fn should_not_panic_if_result_is_err_with_expected_value() {
        let result: Result<&str, &str> = Err("Oh no");
        assert_that(&result).is_err_containing(&"Oh no");
    }

    #[test]
    fn should_not_panic_if_result_is_err_with_uncomparable_ok() {
        #[derive(Debug)]
        struct Incomporable;
        let result: Result<Incomporable, &str> = Err("Oh no");
        assert_that(&result).is_err_containing(&"Oh no");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: Result[err] containing <\"Oh no\">\
                   \n\t but was: Result[err] containing <\"Whoops\">")]
    fn should_panic_if_result_is_err_without_expected_value() {
        let result: Result<&str, &str> = Err("Whoops");
        assert_that(&result).is_err_containing(&"Oh no");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: Result[err] containing <\"Oh no\">\
                   \n\t but was: Result[ok] containing <\"Oh no\">")]
    fn should_panic_if_result_is_ok_if_err_with_value_expected() {
        let result: Result<&str, &str> = Ok("Oh no");
        assert_that(&result).is_err_containing(&"Oh no");
    }

}
<commit_msg>Add Borrow trait to assertions on Result<commit_after>use super::{AssertionFailure, Spec};

use std::borrow::Borrow;
use std::fmt::Debug;

pub trait ResultAssertions<'s, T, E>
    where T: Debug,
          E: Debug
{
    fn is_ok(&mut self) -> Spec<'s, T>;
    fn is_error(&mut self) -> Spec<'s, E>;
}

pub trait ContainingResultAssertions<T, E>
    where T: Debug,
          E: Debug
{
    fn is_ok_containing<V: Borrow<T>>(&mut self, expected_value: V) where T: PartialEq;
    fn is_err_containing<V: Borrow<E>>(&mut self, expected_value: V) where E: PartialEq;
}

impl<'s, T, E> ContainingResultAssertions<T, E> for Spec<'s, Result<T, E>>
    where T: Debug,
          E: Debug
{
    /// Asserts that the subject is an `Ok` Result containing the expected value.
    /// The subject type must be a `Result`.
    ///
    /// ```rust,ignore
    /// assert_that(&Result::Ok::<usize, usize>(1)).is_ok_containing(&1);
    /// ```
    fn is_ok_containing<V: Borrow<T>>(&mut self, expected_value: V)
        where T: PartialEq
    {
        let borrowed_expected_value = expected_value.borrow();

        match *self.subject {
            Ok(ref val) => {
                if !val.eq(borrowed_expected_value) {
                    AssertionFailure::from_spec(self)
                        .with_expected(build_detail_message("ok", borrowed_expected_value))
                        .with_actual(build_detail_message("ok", val))
                        .fail();
                }
            }
            Err(ref val) => {
                AssertionFailure::from_spec(self)
                    .with_expected(build_detail_message("ok", borrowed_expected_value))
                    .with_actual(build_detail_message("err", val))
                    .fail();
            }
        }
    }

    /// Asserts that the subject is an `Err` Result containing the expected value.
    /// The subject type must be a `Result`.
    ///
    /// ```rust,ignore
    /// assert_that(&Result::Err::<usize, usize>(1)).is_err_containing(&1);
    /// ```
    fn is_err_containing<V: Borrow<E>>(&mut self, expected_value: V)
        where E: PartialEq
    {
        let borrowed_expected_value = expected_value.borrow();

        match *self.subject {
            Err(ref val) => {
                if !val.eq(borrowed_expected_value) {
                    AssertionFailure::from_spec(self)
                        .with_expected(build_detail_message("err", borrowed_expected_value))
                        .with_actual(build_detail_message("err", val))
                        .fail();
                }
            }
            Ok(ref val) => {
                AssertionFailure::from_spec(self)
                    .with_expected(build_detail_message("err", borrowed_expected_value))
                    .with_actual(build_detail_message("ok", val))
                    .fail();
            }
        }
    }
}

fn build_detail_message<T: Debug>(variant: &'static str, value: T) -> String {
    format!("Result[{}] containing <{:?}>", variant, value)
}

impl<'s, T, E> ResultAssertions<'s, T, E> for Spec<'s, Result<T, E>>
    where T: Debug,
          E: Debug
{
    /// Asserts that the subject is `Ok`. The value type must be a `Result`.
    ///
    /// This will return a new `Spec` containing the unwrapped value if it is `Ok`.
    ///
    /// ```rust,ignore
    /// assert_that(&Result::Ok::<usize, usize>(1)).is_ok();
    /// ```
    fn is_ok(&mut self) -> Spec<'s, T> {
        match *self.subject {
            Ok(ref val) => {
                Spec {
                    subject: val,
                    subject_name: self.subject_name,
                    location: self.location.clone(),
                    description: self.description,
                }
            }
            Err(ref err) => {
                AssertionFailure::from_spec(self)
                    .with_expected(format!("result[ok]"))
                    .with_actual(format!("result[error]<{:?}>", err))
                    .fail();

                unreachable!();
            }
        }
    }

    /// Asserts that the subject is `Err`. The value type must be a `Result`.
    ///
    /// This will return a new `Spec` containing the unwrapped value if it is `Err`.
    ///
    /// ```rust,ignore
    /// assert_that(&Result::Err::<usize, usize>(1)).is_error();
    /// ```
    fn is_error(&mut self) -> Spec<'s, E> {
        match *self.subject {
            Err(ref val) => {
                Spec {
                    subject: val,
                    subject_name: self.subject_name,
                    location: self.location.clone(),
                    description: self.description,
                }
            }
            Ok(ref val) => {
                AssertionFailure::from_spec(self)
                    .with_expected(format!("result[error]"))
                    .with_actual(format!("result[ok]<{:?}>", val))
                    .fail();

                unreachable!();
            }
        }
    }
}

#[cfg(test)]
mod tests {

    use super::super::prelude::*;

    #[test]
    fn should_not_panic_if_result_is_expected_to_be_ok_and_is() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_ok();
    }

    #[test]
    #[should_panic(expected = "\n\texpected: result[ok]\n\t but was: result[error]<\"Oh no\">")]
    fn should_panic_if_result_is_expected_to_be_ok_and_is_not() {
        let result: Result<&str, &str> = Err("Oh no");
        assert_that(&result).is_ok();
    }

    #[test]
    fn should_return_unwrapped_value_if_subject_is_ok() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_ok().is_equal_to(&"Hello");
    }

    #[test]
    fn should_not_panic_if_result_is_expected_to_be_error_and_is() {
        let result: Result<&str, &str> = Err("Oh no");
        assert_that(&result).is_error();
    }

    #[test]
    #[should_panic(expected = "\n\texpected: result[error]\n\t but was: result[ok]<\"Hello\">")]
    fn should_panic_if_result_is_expected_to_be_error_and_is_not() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_error();
    }

    #[test]
    fn should_return_unwrapped_value_if_subject_is_err() {
        let result: Result<&str, &str> = Err("Hello");
        assert_that(&result).is_error().is_equal_to(&"Hello");
    }

    #[test]
    fn is_ok_containing_should_allow_multiple_borrow_forms() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_ok_containing("Hello");
        assert_that(&result).is_ok_containing(&mut "Hello");
        assert_that(&result).is_ok_containing(&"Hello");
    }

    #[test]
    fn should_not_panic_if_result_is_ok_with_expected_value() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_ok_containing(&"Hello");
    }

    #[test]
    fn should_not_panic_if_result_is_ok_with_uncomparable_ok() {
        #[derive(Debug)]
        struct Incomparable;
        let result: Result<&str, Incomparable> = Ok("Hello");
        assert_that(&result).is_ok_containing(&"Hello");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: Result[ok] containing <\"Hi\">\
                   \n\t but was: Result[ok] containing <\"Hello\">")]
    fn should_panic_if_result_is_ok_without_expected_value() {
        let result: Result<&str, &str> = Ok("Hello");
        assert_that(&result).is_ok_containing(&"Hi");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: Result[ok] containing <\"Hi\">\
                   \n\t but was: Result[err] containing <\"Hi\">")]
    fn should_panic_if_result_is_err_if_ok_with_value_expected() {
        let result: Result<&str, &str> = Err("Hi");
        assert_that(&result).is_ok_containing(&"Hi");
    }

    #[test]
    fn is_error_containing_should_allow_multiple_borrow_forms() {
        let result: Result<&str, &str> = Err("Oh no");
        assert_that(&result).is_err_containing("Oh no");
        assert_that(&result).is_err_containing(&mut "Oh no");
        assert_that(&result).is_err_containing(&"Oh no");
    }

    #[test]
    fn should_not_panic_if_result_is_err_with_expected_value() {
        let result: Result<&str, &str> = Err("Oh no");
        assert_that(&result).is_err_containing(&"Oh no");
    }

    #[test]
    fn should_not_panic_if_result_is_err_with_uncomparable_ok() {
        #[derive(Debug)]
        struct Incomparable;
        let result: Result<Incomparable, &str> = Err("Oh no");
        assert_that(&result).is_err_containing(&"Oh no");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: Result[err] containing <\"Oh no\">\
                   \n\t but was: Result[err] containing <\"Whoops\">")]
    fn should_panic_if_result_is_err_without_expected_value() {
        let result: Result<&str, &str> = Err("Whoops");
        assert_that(&result).is_err_containing(&"Oh no");
    }

    #[test]
    #[should_panic(expected = "\n\texpected: Result[err] containing <\"Oh no\">\
                   \n\t but was: Result[ok] containing <\"Oh no\">")]
    fn should_panic_if_result_is_ok_if_err_with_value_expected() {
        let result: Result<&str, &str> = Ok("Oh no");
        assert_that(&result).is_err_containing(&"Oh no");
    }

}
<|endoftext|>


<commit_before>use std::env;
use std::fs::File;
use std::io::Write;
use std::process;

use applescript::{run as run_applescript};

use plist::Plist;

use tera::{Context, Tera};

use urls;

// http://stackoverflow.com/a/27590832/1558022
macro_rules! error(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
        process::exit(1);
    } }
);

/// Returns true/false if Safari is running.
pub fn is_safari_running() -> bool {
  let cmd_result = process::Command::new("ps")
    .arg("-eaf")
    .output()
    .expect("Unable to test if Safari is running.");
  for line in String::from_utf8(cmd_result.stdout).unwrap().lines() {
    if line.contains("Safari.app/Contents/MacOS/Safari") {
      return true
    }
  }
  false
}

/// Exits the program if Safari isn't running.
pub fn assert_safari_is_running() {
  if !is_safari_running() {
    error!("Safari is not running.");
  }
}

/// Return a URL from a Safari window.
///
/// Given a (window, tab) pair, this function looks up the URL of the tab.
/// Note that it doesn't do any error handling, so will throw an execution
/// error if it fails.
///
/// * `window` - Window index.  1 is frontmost.  If None, assumes the
///              frontmost window.
/// * `tab` - Tab index.  1 is leftmost.  If None, assumes the frontmost tab.
///
pub fn get_url(window: Option<i32>, tab: Option<i32>) -> String {
  // If a tab isn't specified, assume the user wants the frontmost tab.
  let command = match window {
    Some(w_idx) => {
      match tab {
        Some(t_idx) => format!("tell application \"Safari\" to get tab {} of window {}", t_idx, w_idx),
        None => format!("tell application \"Safari\" to get URL of document {}", w_idx),
      }
    },
    None => "tell application \"Safari\" to get URL of document 1".to_string(),
  };
  let output = run_applescript(&command);

  if output.status.success() {
    urls::tidy_url(output.stdout.trim())
  } else {
    if output.stderr.contains("Invalid index") {
      error!("Invalid index: no such window or tab.");
    } else {
      error!("Unexpected error from osascript: {:?}", output.stderr);
    }
  }
}

/// Return a list of URLs from Safari.
///
/// This returns a list of URLs, one for every tab in Safari.  Iteration
/// order depends on AppleScript, which I don't think is guaranteed to be
/// stable (in particular, I think it depends on which window is frontmost).
///
pub fn get_all_urls() -> Vec<String> {
  let script = include_str!("scripts/list-open-tabs.scpt");
  let output = run_applescript(&script);
  if output.status.success() {
    output.stdout.trim()
                 .split(", ")
                 .map(|url| urls::tidy_url(url))
                 .filter(|url| url != "favorites://")
                 .collect()
  } else {
    error!("Unexpected error from osascript: {:?}", output.stderr);
  }
}

/// Convert URL patterns into AppleScript conditions.
///
/// These conditions can be used in an `if` statement in AppleScript to
/// decide whether a tab should be closed.  Three patterns are supported,
/// a limited regex syntax:
///
///     example.com             matches anywhere in the URL
///     ^http://examples.com    matches at the start of the URL
///     example.com/$           matches at the end of the URL
///
fn parse_conditions(url_patterns: Vec<&str>) -> Vec<String> {
  url_patterns.iter().map(|p|
    if p.starts_with("^") {
      format!("starts with \"{}\"", p.replace("^", ""))
    } else if p.ends_with("$") {
      format!("ends with \"{}\"", p.replace("$", ""))
    } else {
      format!("contains \"{}\"", p)
    }
  ).collect()
}

/// Tests for parse_conditions().
#[cfg(test)]
mod tests {
  use safari::parse_conditions;

  #[test]
  fn test_parse_conditions() {
    let patterns = vec!["github.com", "^facebook.com", "twitter.com$"];
    let expected = vec!["contains \"github.com\"", "starts with \"facebook.com\"", "ends with \"twitter.com\""];
    let actual = parse_conditions(patterns);
    assert_eq!(actual, expected);
  }
}

/// Close tabs in Safari that match URL patterns.
///
/// Takes a list of URL patterns, and tries to close any matching Safari tabs.
/// Bugginess in AppleScript means this isn't always perfect, but we can
/// have a go.  In general it will fail to close tabs, rather than close
/// the wrong tabs.
///
pub fn close_tabs(url_patterns: Vec<&str>) {
  let conditions = parse_conditions(url_patterns);

  let clean_tabs_template = include_str!("scripts/clean-tabs.scpt");
  let mut context = Context::new();
  context.add("conditions", &conditions);
  let script = Tera::one_off(&clean_tabs_template, context, false).unwrap();

  // Run it twice to get around weird AppleScript bugs.
  run_applescript(&script);
  run_applescript(&script);
}

/// Get the Bookmarks.plist dict for a given title
fn read_bookmarks_plist(title: &str) -> Plist {

  // All Safari data lives at ~/Library/Safari/Bookmarks.plist
  // TODO: There's probably a more idiomatic Rust-like way to write this.
  let mut plist_path = match env::home_dir() {
    Some(v) => v,
    None => error!("Unable to get path to Bookmarks.plist?"),
  };
  plist_path.push("Library/Safari/Bookmarks.plist");

  let file = match File::open(plist_path) {
    Ok(v) => v,
    Err(e) => error!("Unable to open ~/Library/Safari/Bookmarks.plist: {:?}", e),
  };

  let plist = match Plist::read(file) {
    Ok(v) => v,
    Err(e) => error!("Unable to read ~/Library/Safari/Bookmarks.plist: {:?}", e),
  };

  let data = match plist.as_dictionary() {
    Some(v) => v,
    None => error!("Unable to parse ~/Library/Safari/Bookmarks.plist as dictionary?"),
  };

  // The structure of Bookmarks.plist is as follows:
  //
  //     <dict>
  //       <key>Children</key>
  //       <array>
  //         <dict>
  //           <key>Title</key><string>History</string>
  //           ... dict data ...
  //         </dict>
  //         <dict>
  //           <key>Title</key><string>com.apple.ReadingList</string>
  //           ... dict data ...
  //         </dict>
  //         ... other array items ...
  //       </array>
  //     </dict>
  //
  let children = match data.get("Children") {
    Some(child_key) => match child_key.as_array() {
      Some(v) => v,
      None => error!("Top-level children key in ~/Library/Safari/Bookmarks.plist isn't an array?"),
    },
    None => error!("Unable to find top-level Children key in ~/Library/Safari/Bookmarks.plist"),
  };

  let mut matching_children = children.iter().filter(|d|
    match d.as_dictionary() {
      Some(dict) => match dict.get("Title") {
        Some(title_elem) => match title_elem.as_string() {
          Some(v) => (v == title),
          None => false,
        },
        None => false,
      },
      None => false,
    }
  );

  // Check we got one, and only one result.
  let result = match matching_children.next() {
    Some(v) => v,
    None => error!("Unable to find key {} in Bookmarks.plist", title),
  };

  match matching_children.next() {
    Some(_) => error!("Got more than one result for {} in Bookmarks.plist", title),
    None => {},
  };

  result.to_owned()
}

/// Return a list of URLs from Reading List.
///
/// Iteration order depends on the order in which they're stored in
/// Bookmarks.plist, which is usually (but not guaranteed to be) newest first.
///
pub fn get_reading_list_urls() -> Vec<String> {
  let plist = read_bookmarks_plist("com.apple.ReadingList");

  // TODO: All these unwrap() calls should probably be handled better
  let children = plist
    .as_dictionary().unwrap()
    .get("Children").unwrap()
    .as_array().unwrap();

  children
    .iter()
    .map(|child|
      child
        .as_dictionary().unwrap()
        .get("URLString").unwrap()
        .as_string().unwrap()
    )
    .map(|url| urls::tidy_url(url))
    .collect()
}
<commit_msg>Implement a function to read iCloud tab data<commit_after>use std::collections::{BTreeMap, HashMap};
use std::env;
use std::fs::File;
use std::io::Write;
use std::process;

use applescript::{run as run_applescript};

use plist::Plist;

use tera::{Context, Tera};

use urls;

// http://stackoverflow.com/a/27590832/1558022
macro_rules! error(
    ($($arg:tt)*) => { {
        let r = writeln!(&mut ::std::io::stderr(), $($arg)*);
        r.expect("failed printing to stderr");
        process::exit(1);
    } }
);

/// Returns true/false if Safari is running.
pub fn is_safari_running() -> bool {
  let cmd_result = process::Command::new("ps")
    .arg("-eaf")
    .output()
    .expect("Unable to test if Safari is running.");
  for line in String::from_utf8(cmd_result.stdout).unwrap().lines() {
    if line.contains("Safari.app/Contents/MacOS/Safari") {
      return true
    }
  }
  false
}

/// Exits the program if Safari isn't running.
pub fn assert_safari_is_running() {
  if !is_safari_running() {
    error!("Safari is not running.");
  }
}

/// Return a URL from a Safari window.
///
/// Given a (window, tab) pair, this function looks up the URL of the tab.
/// Note that it doesn't do any error handling, so will throw an execution
/// error if it fails.
///
/// * `window` - Window index.  1 is frontmost.  If None, assumes the
///              frontmost window.
/// * `tab` - Tab index.  1 is leftmost.  If None, assumes the frontmost tab.
///
pub fn get_url(window: Option<i32>, tab: Option<i32>) -> String {
  // If a tab isn't specified, assume the user wants the frontmost tab.
  let command = match window {
    Some(w_idx) => {
      match tab {
        Some(t_idx) => format!("tell application \"Safari\" to get tab {} of window {}", t_idx, w_idx),
        None => format!("tell application \"Safari\" to get URL of document {}", w_idx),
      }
    },
    None => "tell application \"Safari\" to get URL of document 1".to_string(),
  };
  let output = run_applescript(&command);

  if output.status.success() {
    urls::tidy_url(output.stdout.trim())
  } else {
    if output.stderr.contains("Invalid index") {
      error!("Invalid index: no such window or tab.");
    } else {
      error!("Unexpected error from osascript: {:?}", output.stderr);
    }
  }
}

/// Return a list of URLs from Safari.
///
/// This returns a list of URLs, one for every tab in Safari.  Iteration
/// order depends on AppleScript, which I don't think is guaranteed to be
/// stable (in particular, I think it depends on which window is frontmost).
///
pub fn get_all_urls() -> Vec<String> {
  let script = include_str!("scripts/list-open-tabs.scpt");
  let output = run_applescript(&script);
  if output.status.success() {
    output.stdout.trim()
                 .split(", ")
                 .map(|url| urls::tidy_url(url))
                 .filter(|url| url != "favorites://")
                 .collect()
  } else {
    error!("Unexpected error from osascript: {:?}", output.stderr);
  }
}

/// Convert URL patterns into AppleScript conditions.
///
/// These conditions can be used in an `if` statement in AppleScript to
/// decide whether a tab should be closed.  Three patterns are supported,
/// a limited regex syntax:
///
///     example.com             matches anywhere in the URL
///     ^http://examples.com    matches at the start of the URL
///     example.com/$           matches at the end of the URL
///
fn parse_conditions(url_patterns: Vec<&str>) -> Vec<String> {
  url_patterns.iter().map(|p|
    if p.starts_with("^") {
      format!("starts with \"{}\"", p.replace("^", ""))
    } else if p.ends_with("$") {
      format!("ends with \"{}\"", p.replace("$", ""))
    } else {
      format!("contains \"{}\"", p)
    }
  ).collect()
}

/// Tests for parse_conditions().
#[cfg(test)]
mod tests {
  use safari::parse_conditions;

  #[test]
  fn test_parse_conditions() {
    let patterns = vec!["github.com", "^facebook.com", "twitter.com$"];
    let expected = vec!["contains \"github.com\"", "starts with \"facebook.com\"", "ends with \"twitter.com\""];
    let actual = parse_conditions(patterns);
    assert_eq!(actual, expected);
  }
}

/// Close tabs in Safari that match URL patterns.
///
/// Takes a list of URL patterns, and tries to close any matching Safari tabs.
/// Bugginess in AppleScript means this isn't always perfect, but we can
/// have a go.  In general it will fail to close tabs, rather than close
/// the wrong tabs.
///
pub fn close_tabs(url_patterns: Vec<&str>) {
  let conditions = parse_conditions(url_patterns);

  let clean_tabs_template = include_str!("scripts/clean-tabs.scpt");
  let mut context = Context::new();
  context.add("conditions", &conditions);
  let script = Tera::one_off(&clean_tabs_template, context, false).unwrap();

  // Run it twice to get around weird AppleScript bugs.
  run_applescript(&script);
  run_applescript(&script);
}

/// Get the Bookmarks.plist dict for a given title
fn read_bookmarks_plist(title: &str) -> Plist {

  // All Safari data lives at ~/Library/Safari/Bookmarks.plist
  // TODO: There's probably a more idiomatic Rust-like way to write this.
  let mut plist_path = match env::home_dir() {
    Some(v) => v,
    None => error!("Unable to get home directory?"),
  };
  plist_path.push("Library/Safari/Bookmarks.plist");

  let file = match File::open(plist_path) {
    Ok(v) => v,
    Err(e) => error!("Unable to open ~/Library/Safari/Bookmarks.plist: {:?}", e),
  };

  let plist = match Plist::read(file) {
    Ok(v) => v,
    Err(e) => error!("Unable to read ~/Library/Safari/Bookmarks.plist: {:?}", e),
  };

  let data = match plist.as_dictionary() {
    Some(v) => v,
    None => error!("Unable to parse ~/Library/Safari/Bookmarks.plist as dictionary?"),
  };

  // The structure of Bookmarks.plist is as follows:
  //
  //     <dict>
  //       <key>Children</key>
  //       <array>
  //         <dict>
  //           <key>Title</key><string>History</string>
  //           ... dict data ...
  //         </dict>
  //         <dict>
  //           <key>Title</key><string>com.apple.ReadingList</string>
  //           ... dict data ...
  //         </dict>
  //         ... other array items ...
  //       </array>
  //     </dict>
  //
  let children = match data.get("Children") {
    Some(child_key) => match child_key.as_array() {
      Some(v) => v,
      None => error!("Top-level children key in ~/Library/Safari/Bookmarks.plist isn't an array?"),
    },
    None => error!("Unable to find top-level Children key in ~/Library/Safari/Bookmarks.plist"),
  };

  let mut matching_children = children.iter().filter(|d|
    match d.as_dictionary() {
      Some(dict) => match dict.get("Title") {
        Some(title_elem) => match title_elem.as_string() {
          Some(v) => (v == title),
          None => false,
        },
        None => false,
      },
      None => false,
    }
  );

  // Check we got one, and only one result.
  let result = match matching_children.next() {
    Some(v) => v,
    None => error!("Unable to find key {} in Bookmarks.plist", title),
  };

  match matching_children.next() {
    Some(_) => error!("Got more than one result for {} in Bookmarks.plist", title),
    None => {},
  };

  result.to_owned()
}

/// Return a list of URLs from Reading List.
///
/// Iteration order depends on the order in which they're stored in
/// Bookmarks.plist, which is usually (but not guaranteed to be) newest first.
///
pub fn get_reading_list_urls() -> Vec<String> {
  let plist = read_bookmarks_plist("com.apple.ReadingList");

  // TODO: All these unwrap() calls should probably be handled better
  let children = plist
    .as_dictionary().unwrap()
    .get("Children").unwrap()
    .as_array().unwrap();

  children
    .iter()
    .map(|child|
      child
        .as_dictionary().unwrap()
        .get("URLString").unwrap()
        .as_string().unwrap()
    )
    .map(|url| urls::tidy_url(url))
    .collect()
}

/// Get the com.apple.Safari.plist preferences file
fn read_safari_plist() -> BTreeMap<String, Plist> {

  // All Safari data lives at ~/Library/Safari/Bookmarks.plist
  // TODO: There's probably a more idiomatic Rust-like way to write this.
  let mut plist_path = match env::home_dir() {
    Some(v) => v,
    None => error!("Unable to get home directory?"),
  };
  plist_path.push("Library/SyncedPreferences/com.apple.Safari.plist");

  let file = match File::open(plist_path) {
    Ok(v) => v,
    Err(e) => error!("Unable to open com.apple.Safari.plist: {:?}", e),
  };

  let plist = match Plist::read(file) {
    Ok(v) => v,
    Err(e) => error!("Unable to read com.apple.Safari.plist: {:?}", e),
  };

  let data = match plist.as_dictionary() {
    Some(v) => v,
    None => error!("Unable to parse com.apple.Safari.plist as dictionary?"),
  };

  // The structure of com.apple.Safari.plist is as follows:
  //
  //      <dict>
  //        <key>values</key>
  //        <dict>
  //          <dict>
  //            <key>[[ UUID ]]</key>
  //            <dict>
  //              ... dict data ...
  //              <key>value</key>
  //              <dict>
  //                ... dict data ...
  //                <key>DeviceName</key><string>[[ Device name ]]</string>
  //                <key>Tabs</key>
  //                <array>
  //                  <dict>
  //                    <key>Title</key><string>[[ Title ]]</string>
  //                    <key>URL</key><string>[[ URL ]]</string>
  //                  </dict>
  //                  ... array items for other tabs ...
  //                </array>
  //              </dict>
  //            </dict>
  //            ... dicts for other devices ...
  //          </dict>
  //        </dict>
  //      </dict>
  //
  match data.get("values") {
    Some(child_key) => match child_key.as_dictionary() {
      Some(v) => v.to_owned(),
      None => error!("Top-level values key in com.apple.Safari.plist isn't an dictionary?"),
    },
    None => error!("Unable to find top-level values key in com.apple.Safari.plist"),
  }
}

/// Return a list of devices in iCloud Tabs.
pub fn list_icloud_tabs_devices() -> Vec<String> {
  let plist = read_safari_plist();

  plist
    .values()
    .map(|value| value
      .as_dictionary().unwrap()
      .get("value").unwrap()
      .as_dictionary().unwrap()
      .get("DeviceName").unwrap()
      .as_string().unwrap()
      .to_owned()
    )
    .collect()
}

/// Return a list of URLs from iCloud Tabs.
pub fn get_icloud_tabs_urls() -> HashMap<String, Vec<String>> {
  let plist = read_safari_plist();
  let mut result: HashMap<String, Vec<String>> = HashMap::new();
  let devices_with_tabs = plist
    .values()
    .map(|value| value
      .as_dictionary().unwrap()
      .get("value").unwrap()
      .as_dictionary().unwrap()
    );
  for data in devices_with_tabs {
    let name: String = data
      .get("DeviceName").unwrap()
      .as_string().unwrap()
      .to_owned();
    let urls: Vec<String> = data
      .get("Tabs").unwrap()
      .as_array().unwrap()
      .iter()
      .map(|tab| tab
        .as_dictionary().unwrap()
        .get("URL").unwrap()
        .as_string().unwrap()
      )
      .map(|url| urls::tidy_url(url))
      .collect();
    result.insert(name, urls);
  }
  result
}
<|endoftext|>


<commit_before>use rocket_contrib::templates::handlebars::Handlebars;
use std::panic::catch_unwind;

pub fn serve() {
    // in debug builds this will force an init, good enough for testing
    let _hbars = &*TEMPLATES;

    loop {
        let port = std::env::var("ROCKET_PORT").unwrap_or_else(|_| String::from("OOPS"));
        info!("Attempting to launch Rocket at port {}...", &port);
        let result = catch_unwind(|| {
            rocket::ignite()
                .mount(
                    "/api",
                    routes![api::all_fcps, api::member_fcps, api::github_webhook],
                )
                .mount("/", routes![html::all_fcps, html::member_fcps])
                .launch();
        });

        ok_or!(result, why => error!("Rocket failed to ignite: {:?}", why));
    }
}

mod html {
    use super::TEMPLATES;
    use crate::error::DashResult;
    use crate::nag;
    use rocket::response::content;
    use std::collections::BTreeMap;

    type Html = content::Html<String>;

    #[get("/")]
    pub fn all_fcps() -> DashResult<Html> {
        let mut teams = BTreeMap::new();
        for fcp in nag::all_fcps()? {
            let nag::FcpWithInfo {
                fcp,
                reviews,
                issue,
                status_comment,
            } = fcp;

            let mut pending_reviewers = reviews
                .into_iter()
                .filter(|&(_, reviewed)| !reviewed)
                .map(|(user, _)| user.login)
                .collect::<Vec<String>>();

            pending_reviewers.sort();

            let record = json!({
                "disposition": fcp.disposition,
                "issue": issue,
                "statusComment": status_comment,
                "pendingReviewers": pending_reviewers,
            });

            for label in issue.labels.iter().filter(|l| l.starts_with("T-")).cloned() {
                teams
                    .entry(label)
                    .or_insert_with(Vec::new)
                    .push(record.clone());
            }
        }

        let context = teams
            .into_iter()
            .map(|(team_label, fcps)| {
                json!({
                    "team": team_label,
                    "fcps": fcps,
                })
            })
            .collect::<Vec<_>>();

        let rendered = TEMPLATES.render("all", &json!({ "model": context }))?;
        Ok(content::Html(rendered))
    }

    #[get("/fcp/<username>")]
    pub fn member_fcps(username: String) -> DashResult<Html> {
        let (user, fcps) = nag::individual_nags(&username)?;

        let context = json!({
            "model": {
                "user": user,
                "fcps": fcps,
            }
        });

        let rendered = TEMPLATES.render("user", &context)?;
        Ok(content::Html(rendered))
    }
}

mod api {
    use crate::domain::github::GitHubUser;
    use crate::error::DashResult;
    use crate::github::webhooks::{Event, Payload};
    use crate::github::{handle_comment, handle_issue, handle_pr};
    use crate::nag;
    use crate::DB_POOL;
    use rocket_contrib::json::Json;

    #[get("/all")]
    pub fn all_fcps() -> DashResult<Json<Vec<nag::FcpWithInfo>>> { Ok(Json(nag::all_fcps()?)) }

    #[get("/<username>")]
    pub fn member_fcps(
        username: String,
    ) -> DashResult<Json<(GitHubUser, Vec<nag::IndividualFcp>)>> {
        Ok(Json(nag::individual_nags(&username)?))
    }

    #[post("/github-webhook", data = "<event>")]
    pub fn github_webhook(event: Event) -> DashResult<()> {
        let conn = &*DB_POOL.get()?;

        match event.payload {
            Payload::Issues(issue_event) => {
                handle_issue(conn, issue_event.issue, &issue_event.repository.full_name)?;
            }

            Payload::PullRequest(pr_event) => {
                handle_pr(conn, pr_event.pull_request, &pr_event.repository.full_name)?;
            }

            Payload::IssueComment(comment_event) => {
                // possible race conditions if we get a comment hook before the issue one (or we
                // missed the issue one), so make sure the issue exists first

                if comment_event.action != "deleted" {
                    // TODO handle deleted comments properly
                    handle_issue(
                        conn,
                        comment_event.issue,
                        &comment_event.repository.full_name,
                    )?;
                    handle_comment(
                        conn,
                        comment_event.comment,
                        &comment_event.repository.full_name,
                    )?;
                }
            }

            Payload::Unsupported => (),
        }

        Ok(())
    }
}

lazy_static! {
    static ref TEMPLATES: Handlebars = {
        let mut hbars = Handlebars::new();
        let root_template = include_str!("templates/index.html");

        let all_fcps_fragment = include_str!("templates/fcp.hbs");
        let all_fcps_template = root_template.replace("{{content}}", all_fcps_fragment);

        let user_fcps_fragment = include_str!("templates/fcp-user.hbs");
        let user_fcps_template = root_template.replace("{{content}}", user_fcps_fragment);

        hbars
            .register_template_string("all", &all_fcps_template)
            .expect("unable to register all-fcps template");
        hbars
            .register_template_string("user", &user_fcps_template)
            .expect("unable to register user fcps template");

        hbars
    };
}
<commit_msg>Replace rocket's default 404 with one that doesn't log ERROR.<commit_after>use rocket_contrib::templates::handlebars::Handlebars;
use std::panic::catch_unwind;

pub fn serve() {
    // in debug builds this will force an init, good enough for testing
    let _hbars = &*TEMPLATES;

    loop {
        let port = std::env::var("ROCKET_PORT").unwrap_or_else(|_| String::from("OOPS"));
        info!("Attempting to launch Rocket at port {}...", &port);
        let result = catch_unwind(|| {
            rocket::ignite()
                .mount(
                    "/api",
                    routes![api::all_fcps, api::member_fcps, api::github_webhook],
                )
                .mount("/", routes![html::all_fcps, html::member_fcps])
                .register(catchers![not_found])
                .launch();
        });

        ok_or!(result, why => error!("Rocket failed to ignite: {:?}", why));
    }
}

#[catch(404)]
fn not_found(req: &rocket::Request<'_>) -> String {
    info!("No matching routes for {} {}", req.method(), req.uri());
    format!("`{}` is not a valid path.", req.uri())
}

mod html {
    use super::TEMPLATES;
    use crate::error::DashResult;
    use crate::nag;
    use rocket::response::content;
    use std::collections::BTreeMap;

    type Html = content::Html<String>;

    #[get("/")]
    pub fn all_fcps() -> DashResult<Html> {
        let mut teams = BTreeMap::new();
        for fcp in nag::all_fcps()? {
            let nag::FcpWithInfo {
                fcp,
                reviews,
                issue,
                status_comment,
            } = fcp;

            let mut pending_reviewers = reviews
                .into_iter()
                .filter(|&(_, reviewed)| !reviewed)
                .map(|(user, _)| user.login)
                .collect::<Vec<String>>();

            pending_reviewers.sort();

            let record = json!({
                "disposition": fcp.disposition,
                "issue": issue,
                "statusComment": status_comment,
                "pendingReviewers": pending_reviewers,
            });

            for label in issue.labels.iter().filter(|l| l.starts_with("T-")).cloned() {
                teams
                    .entry(label)
                    .or_insert_with(Vec::new)
                    .push(record.clone());
            }
        }

        let context = teams
            .into_iter()
            .map(|(team_label, fcps)| {
                json!({
                    "team": team_label,
                    "fcps": fcps,
                })
            })
            .collect::<Vec<_>>();

        let rendered = TEMPLATES.render("all", &json!({ "model": context }))?;
        Ok(content::Html(rendered))
    }

    #[get("/fcp/<username>")]
    pub fn member_fcps(username: String) -> DashResult<Html> {
        let (user, fcps) = nag::individual_nags(&username)?;

        let context = json!({
            "model": {
                "user": user,
                "fcps": fcps,
            }
        });

        let rendered = TEMPLATES.render("user", &context)?;
        Ok(content::Html(rendered))
    }
}

mod api {
    use crate::domain::github::GitHubUser;
    use crate::error::DashResult;
    use crate::github::webhooks::{Event, Payload};
    use crate::github::{handle_comment, handle_issue, handle_pr};
    use crate::nag;
    use crate::DB_POOL;
    use rocket_contrib::json::Json;

    #[get("/all")]
    pub fn all_fcps() -> DashResult<Json<Vec<nag::FcpWithInfo>>> { Ok(Json(nag::all_fcps()?)) }

    #[get("/<username>")]
    pub fn member_fcps(
        username: String,
    ) -> DashResult<Json<(GitHubUser, Vec<nag::IndividualFcp>)>> {
        Ok(Json(nag::individual_nags(&username)?))
    }

    #[post("/github-webhook", data = "<event>")]
    pub fn github_webhook(event: Event) -> DashResult<()> {
        let conn = &*DB_POOL.get()?;

        match event.payload {
            Payload::Issues(issue_event) => {
                handle_issue(conn, issue_event.issue, &issue_event.repository.full_name)?;
            }

            Payload::PullRequest(pr_event) => {
                handle_pr(conn, pr_event.pull_request, &pr_event.repository.full_name)?;
            }

            Payload::IssueComment(comment_event) => {
                // possible race conditions if we get a comment hook before the issue one (or we
                // missed the issue one), so make sure the issue exists first

                if comment_event.action != "deleted" {
                    // TODO handle deleted comments properly
                    handle_issue(
                        conn,
                        comment_event.issue,
                        &comment_event.repository.full_name,
                    )?;
                    handle_comment(
                        conn,
                        comment_event.comment,
                        &comment_event.repository.full_name,
                    )?;
                }
            }

            Payload::Unsupported => (),
        }

        Ok(())
    }
}

lazy_static! {
    static ref TEMPLATES: Handlebars = {
        let mut hbars = Handlebars::new();
        let root_template = include_str!("templates/index.html");

        let all_fcps_fragment = include_str!("templates/fcp.hbs");
        let all_fcps_template = root_template.replace("{{content}}", all_fcps_fragment);

        let user_fcps_fragment = include_str!("templates/fcp-user.hbs");
        let user_fcps_template = root_template.replace("{{content}}", user_fcps_fragment);

        hbars
            .register_template_string("all", &all_fcps_template)
            .expect("unable to register all-fcps template");
        hbars
            .register_template_string("user", &user_fcps_template)
            .expect("unable to register user fcps template");

        hbars
    };
}
<|endoftext|>


<commit_before>extern crate dbus;
use self::dbus::{Connection, NameFlag, ReleaseNameReply};

use super::error::DBusError;
use super::interface::DBusInterface;
use super::object::DBusObject;
use super::signal::DBusSignal;

use std::collections::btree_map::{BTreeMap, Entry};
use std::error::Error;

pub struct DBusServer<'a> {
    conn: &'a Connection,
    name: String,

    objects: BTreeMap<String, DBusObject<'a>>,
    signals: BTreeMap<DBusSignal, Vec<fn (&Connection, &DBusSignal) -> ()>>
}

impl<'a> DBusServer<'a> {
    pub fn new(conn: &'a Connection, name: &str) -> Result<DBusServer<'a>, dbus::Error> {
        try!(conn.register_name(name, NameFlag::DoNotQueue as u32));

        Ok(DBusServer {
            conn: conn,
            name: name.to_string(),

            objects: BTreeMap::new(),
            signals: BTreeMap::new(),
        })
    }

    pub fn add_object(&mut self, path: &str, ifaces: BTreeMap<String, Box<DBusInterface>>) -> Result<&DBusObject<'a>, Box<Error>> {
        match self.objects.entry(path.to_string()) {
            Entry::Vacant(v)    => Ok(v.insert(try!(DBusObject::new(self.conn, ifaces, path)))),
            Entry::Occupied(_)  => Err(Box::new(DBusError::PathAlreadyRegistered(path.to_string()))),
        }
    }

    pub fn remove_object(&mut self, path: &str) -> Result<(), DBusError> {
        match self.objects.remove(path) {
            Some(_) => Ok(()),
            None    => Err(DBusError::NoSuchPath(path.to_string())),
        }
    }

    pub fn connect(&mut self, signal: DBusSignal, callback: fn (&Connection, &DBusSignal) -> ()) -> () {
        match self.signals.entry(signal) {
            Entry::Vacant(v)    => { v.insert(vec![callback]); },
            Entry::Occupied(o)  => o.into_mut().push(callback),
        };
    }
}

impl<'a> Drop for DBusServer<'a> {
    fn drop(&mut self) {
        let res = self.conn.release_name(&self.name[..]);
        match res {
            Ok(reply) =>
                match reply {
                    ReleaseNameReply::Released    => (),
                    ReleaseNameReply::NonExistent => panic!("internal error: non-existent name {}?!", self.name),
                    ReleaseNameReply::NotOwner    => panic!("internal error: not the owner of {}?!", self.name),
                },
            Err(err) => println!("failed to release {}: {:?}: {:?}", self.name, err.name(), err.message()),
        }
    }
}
<commit_msg>server: add method to run off of the connection<commit_after>extern crate dbus;
use self::dbus::{Connection, ConnectionItem, Message, NameFlag, ReleaseNameReply};

use super::error::DBusError;
use super::interface::DBusInterface;
use super::object::DBusObject;
use super::signal::DBusSignal;

use std::collections::btree_map::{BTreeMap, Entry};
use std::error::Error;

pub struct DBusServer<'a> {
    conn: &'a Connection,
    name: String,

    objects: BTreeMap<String, DBusObject<'a>>,
    signals: BTreeMap<DBusSignal, Vec<fn (&Connection, &DBusSignal) -> ()>>
}

impl<'a> DBusServer<'a> {
    pub fn new(conn: &'a Connection, name: &str) -> Result<DBusServer<'a>, dbus::Error> {
        try!(conn.register_name(name, NameFlag::DoNotQueue as u32));

        Ok(DBusServer {
            conn: conn,
            name: name.to_string(),

            objects: BTreeMap::new(),
            signals: BTreeMap::new(),
        })
    }

    pub fn add_object(&mut self, path: &str, ifaces: BTreeMap<String, Box<DBusInterface>>) -> Result<&DBusObject<'a>, Box<Error>> {
        match self.objects.entry(path.to_string()) {
            Entry::Vacant(v)    => Ok(v.insert(try!(DBusObject::new(self.conn, ifaces, path)))),
            Entry::Occupied(_)  => Err(Box::new(DBusError::PathAlreadyRegistered(path.to_string()))),
        }
    }

    pub fn remove_object(&mut self, path: &str) -> Result<(), DBusError> {
        match self.objects.remove(path) {
            Some(_) => Ok(()),
            None    => Err(DBusError::NoSuchPath(path.to_string())),
        }
    }

    pub fn connect(&mut self, signal: DBusSignal, callback: fn (&Connection, &DBusSignal) -> ()) -> () {
        match self.signals.entry(signal) {
            Entry::Vacant(v)    => { v.insert(vec![callback]); },
            Entry::Occupied(o)  => o.into_mut().push(callback),
        };
    }

    pub fn run(&mut self) -> () {
        self.conn.iter(100).fold((), |_, item| {
            match item {
                ConnectionItem::MethodCall(m)   => self._call_method(m),
                ConnectionItem::Signal(s)       => self._match_signal(s),
                ConnectionItem::Nothing         => (),
            }
        });
    }

    fn _call_method(&mut self, m: Message) -> () {
        // TODO: Implement.
    }

    fn _match_signal(&self, m: Message) -> () {
        // TODO: Implement.
    }
}

impl<'a> Drop for DBusServer<'a> {
    fn drop(&mut self) {
        let res = self.conn.release_name(&self.name[..]);
        match res {
            Ok(reply) =>
                match reply {
                    ReleaseNameReply::Released    => (),
                    ReleaseNameReply::NonExistent => panic!("internal error: non-existent name {}?!", self.name),
                    ReleaseNameReply::NotOwner    => panic!("internal error: not the owner of {}?!", self.name),
                },
            Err(err) => println!("failed to release {}: {:?}: {:?}", self.name, err.name(), err.message()),
        }
    }
}
<|endoftext|>


<commit_before>#![cfg(feature = "syslog-3")]
/*!
Example usage of `fern` with the `syslog` crate.

Be sure to depend on `syslog` and the `syslog` feature in `Cargo.toml`:

```toml
[dependencies]
fern = { version = "0.5", features = ["syslog-3"] }]
syslog = "3.3"
```

To use `syslog`, simply create the log you want, and pass it into `Dispatch::chain`:

```no_run
extern crate fern;
extern crate syslog;

# fn setup_logging() -> Result<(), fern::InitError> {
fern::Dispatch::new()
    .chain(syslog::unix(syslog::Facility::LOG_USER)?)
    .apply()?;
# Ok(())
# }
# fn main() { setup_logging().ok(); }
```

One thing with `syslog` is that you don't generally want to apply any log formatting. The system logger will handle
that for you, however you may want to format messages if you additionally send them to stdout. Fortunately, fern makes
this easy:

```no_run
# extern crate fern;
# extern crate log;
# extern crate syslog;
#
# fn setup_logging() -> Result<(), fern::InitError> {
// top level config
fern::Dispatch::new()
    .chain(
        // console config
        fern::Dispatch::new()
            .level(log::LevelFilter::Debug)
            .format(move |out, message, record| {
                out.finish(format_args!(
                    "[{}] {}",
                    record.level(),
                    message,
                ))
            })
            .chain(std::io::stdout())
    )
    .chain(
        // syslog config
        fern::Dispatch::new()
            .level(log::LevelFilter::Info)
            .chain(syslog::unix(syslog::Facility::LOG_USER)?)
    )
    .apply()?;
# Ok(())
# }
# fn main() { setup_logging().ok(); }
```

With this, all info and above messages will be sent to the syslog with no formatting, and
the messages sent to the console will still look nice as usual.

---

One last pattern you might want to know: creating a log target which must be explicitly mentioned
in order to work.

```no_run
# extern crate fern;
# extern crate log;
# extern crate syslog;
#
# fn setup_logging() -> Result<(), fern::InitError> {
fern::Dispatch::new()
    // by default only accept warning messages from libraries so as not to spam
    .level(log::LevelFilter::Warn)
    // but accept Info if we explicitly mention it
    .level_for("explicit-syslog", log::LevelFilter::Debug)
    .chain(syslog::unix(syslog::Facility::LOG_USER)?)
    .apply()?;
# Ok(())
# }
# fn main() { setup_logging().ok(); }
```

With this configuration, only warning messages will get through by default, and since 'explicit-syslog'
is not the name of the crate, we can only send info / debug messages if we do so explicitly:

```no_run
# #[macro_use]
# extern crate log;
# fn main() {
debug!("this won't get through");
info!("neither will this"); // especially if this is a library you depend on.
warn!("this will!");

info!(target: "explicit-syslog", "this will also show up!");
# }
```
*/
<commit_msg>Remove duplicate 'cfg' predicate on syslog module<commit_after>/*!
Example usage of `fern` with the `syslog` crate.

Be sure to depend on `syslog` and the `syslog` feature in `Cargo.toml`:

```toml
[dependencies]
fern = { version = "0.5", features = ["syslog-3"] }]
syslog = "3.3"
```

To use `syslog`, simply create the log you want, and pass it into `Dispatch::chain`:

```no_run
extern crate fern;
extern crate syslog;

# fn setup_logging() -> Result<(), fern::InitError> {
fern::Dispatch::new()
    .chain(syslog::unix(syslog::Facility::LOG_USER)?)
    .apply()?;
# Ok(())
# }
# fn main() { setup_logging().ok(); }
```

One thing with `syslog` is that you don't generally want to apply any log formatting. The system logger will handle
that for you, however you may want to format messages if you additionally send them to stdout. Fortunately, fern makes
this easy:

```no_run
# extern crate fern;
# extern crate log;
# extern crate syslog;
#
# fn setup_logging() -> Result<(), fern::InitError> {
// top level config
fern::Dispatch::new()
    .chain(
        // console config
        fern::Dispatch::new()
            .level(log::LevelFilter::Debug)
            .format(move |out, message, record| {
                out.finish(format_args!(
                    "[{}] {}",
                    record.level(),
                    message,
                ))
            })
            .chain(std::io::stdout())
    )
    .chain(
        // syslog config
        fern::Dispatch::new()
            .level(log::LevelFilter::Info)
            .chain(syslog::unix(syslog::Facility::LOG_USER)?)
    )
    .apply()?;
# Ok(())
# }
# fn main() { setup_logging().ok(); }
```

With this, all info and above messages will be sent to the syslog with no formatting, and
the messages sent to the console will still look nice as usual.

---

One last pattern you might want to know: creating a log target which must be explicitly mentioned
in order to work.

```no_run
# extern crate fern;
# extern crate log;
# extern crate syslog;
#
# fn setup_logging() -> Result<(), fern::InitError> {
fern::Dispatch::new()
    // by default only accept warning messages from libraries so as not to spam
    .level(log::LevelFilter::Warn)
    // but accept Info if we explicitly mention it
    .level_for("explicit-syslog", log::LevelFilter::Debug)
    .chain(syslog::unix(syslog::Facility::LOG_USER)?)
    .apply()?;
# Ok(())
# }
# fn main() { setup_logging().ok(); }
```

With this configuration, only warning messages will get through by default, and since 'explicit-syslog'
is not the name of the crate, we can only send info / debug messages if we do so explicitly:

```no_run
# #[macro_use]
# extern crate log;
# fn main() {
debug!("this won't get through");
info!("neither will this"); // especially if this is a library you depend on.
warn!("this will!");

info!(target: "explicit-syslog", "this will also show up!");
# }
```
*/
<|endoftext|>


<commit_before>// license info

/*!

*/

use processus::*;
use std::old_io::fs;
use std::fs::{File, read_link, PathExt};
use std::io::Read;
use std::ffi::AsOsStr;
use std::old_io::fs::PathExtensions;
use std::old_path::posix::Path as PosixPath;
use std::str::FromStr;
use std::os;
use std::path::{Path, PathBuf};
use std::io;
use std::old_path;
use std::old_io;

pub struct System {
    processus_list: Vec<Processus>
}

impl System {
    pub fn new() -> System {
        let mut s = System {
            processus_list: Vec::new()
        };

        s.refresh();
        s
    }

    pub fn refresh(&mut self) {
        match fs::readdir(&PosixPath::new("/proc")) {
            Ok(v) => {
                self.processus_list = Vec::new();

                for entry in v.iter() {
                    if entry.is_dir() {
                        match _get_processus_data(entry) {
                            Some(p) => self.processus_list.push(p),
                            None => {}
                        };
                    }
                }
            },
            Err(e) => {
                panic!("readdir error: {}", e);
            }
        }
    }

    pub fn get_processus_list<'a>(&'a self) -> &'a [Processus] {
        self.processus_list.as_slice()
    }

    pub fn get_processus(&self, pid: i32) -> Option<&Processus> {
        for pro in self.processus_list.iter() {
            if pro.pid == pid {
                return Some(pro);
            }
        }
        None
    }
}

fn _get_processus_data(path: &PosixPath) -> Option<Processus> {
    if !path.exists() || !path.is_dir() {
        return None;
    }
    match fs::readdir(path) {
        Ok(v) => {
            let paths : Vec<&str> = path.as_os_str().to_str().unwrap().split("/").collect();
            let last = paths[paths.len() - 1];
            match i32::from_str(last) {
                Ok(nb) => {
                    let mut p = Processus {
                        pid: nb,
                        cmd: String::new(),
                        environ: Vec::new(),
                        exe: String::new(),
                        cwd: String::new(),
                        root: String::new(),
                        memory: 0
                    };

                    for entry in v.iter() {
                        let t = get_last(entry);

                        match t {
                            "cmdline" => {
                                p.cmd = String::from_str(copy_from_file(entry)[0].as_slice());
                            },
                            "environ" => {
                                p.environ = copy_from_file(entry);
                            },
                            "exe" => {
                                let s = read_link(entry.as_str().unwrap());
                                if s.is_ok() {
                                    p.exe = String::from_str(s.unwrap().as_os_str().to_str().unwrap());
                                }
                            },
                            "cwd" => {
                                p.cwd = String::from_str(realpath(Path::new(entry.as_str().unwrap())).unwrap().as_os_str().to_str().unwrap());
                            },
                            "root" => {
                                p.root = String::from_str(realpath(Path::new(entry.as_str().unwrap())).unwrap().as_os_str().to_str().unwrap());
                            },
                            "status" => {
                                let mut file = File::open(entry.as_str().unwrap()).unwrap();
                                let mut data = String::new();

                                file.read_to_string(&mut data);
                                let lines : Vec<&str> = data.split('\n').collect();
                                for line in lines.iter() {
                                    match *line {
                                        l => if l.starts_with("VmSize") {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            p.memory = u32::from_str(parts[parts.len() - 2]).unwrap();
                                            break;
                                        }
                                    }
                                }
                            }
                            l => {}
                        };
                    }
                    Some(p)
                },
                Err(_) => None
            }
        }
        Err(_) => None
    }
}

fn get_last(path: &PosixPath) -> &str {
    let t = path.as_os_str().to_str().unwrap();
    let v : Vec<&str> = t.split('/').collect();

    if v.len() > 0 {
        v[v.len() - 1]
    } else {
        ""
    }
}

fn copy_from_file(entry: &PosixPath) -> Vec<String> {
    match File::open(entry) {
        Ok(mut f) => {
            let mut d = String::new();

            f.read_to_string(&mut d);
            let mut v : Vec<&str> = d.split('\0').collect();
            let mut ret = Vec::new();

            for tmp in v.iter() {
                ret.push(String::from_str(tmp));
            }
            ret
        },
        Err(_) => Vec::new()
    }
}

fn old_realpath(original: &old_path::Path) -> old_io::IoResult<old_path::Path> {
    const MAX_LINKS_FOLLOWED: usize = 256;
    let original = try!(os::getcwd()).join(original);
    // Right now lstat on windows doesn't work quite well
    if cfg!(windows) {
        return Ok(original)
    }
    let result = original.root_path();
    let mut result = result.expect("make_absolute has no root_path");
    let mut followed = 0;
    for part in original.components() {
        result.push(part);
        loop {
            if followed == MAX_LINKS_FOLLOWED {
                return Ok(old_path::Path::new(""));
            }
            match fs::lstat(&result) {
                Err(..) => break,
                Ok(ref stat) if stat.kind != old_io::FileType::Symlink => break,
                Ok(..) => {
                    followed += 1;
                    let path = try!(fs::readlink(&result));
                    result.pop();
                    result.push(path);
                }
            }
        }
    }
    return Ok(result);
}

fn realpath(original: &Path) -> io::Result<PathBuf> {
    let old = old_path::Path::new(original.to_str().unwrap());

    match old_realpath(&old) {
        Ok(p) => Ok(PathBuf::new(p.as_str().unwrap())),
        Err(_) => Ok(PathBuf::new(""))
    }
}<commit_msg>Update system.rs<commit_after>// license info

/*!

*/

use processus::*;
use std::old_io::fs;
use std::fs::{File, read_link, PathExt};
use std::io::Read;
use std::ffi::AsOsStr;
use std::old_io::fs::PathExtensions;
use std::old_path::posix::Path as PosixPath;
use std::str::FromStr;
use std::os;
use std::path::{Path, PathBuf};
use std::io;
use std::old_path;
use std::old_io;

pub struct System {
    processus_list: Vec<Processus>
}

impl System {
    pub fn new() -> System {
        let mut s = System {
            processus_list: Vec::new()
        };

        s.refresh();
        s
    }

    pub fn refresh(&mut self) {
        match fs::readdir(&PosixPath::new("/proc")) {
            Ok(v) => {
                self.processus_list = Vec::new();

                for entry in v.iter() {
                    if entry.is_dir() {
                        match _get_processus_data(entry) {
                            Some(p) => self.processus_list.push(p),
                            None => {}
                        };
                    }
                }
            },
            Err(e) => {
                panic!("readdir error: {}", e);
            }
        }
    }

    pub fn get_processus_list<'a>(&'a self) -> &'a [Processus] {
        self.processus_list.as_slice()
    }

    pub fn get_processus(&self, pid: i32) -> Option<&Processus> {
        for pro in self.processus_list.iter() {
            if pro.pid == pid {
                return Some(pro);
            }
        }
        None
    }
}

fn _get_processus_data(path: &PosixPath) -> Option<Processus> {
    if !path.exists() || !path.is_dir() {
        return None;
    }
    match fs::readdir(path) {
        Ok(v) => {
            let paths : Vec<&str> = path.as_os_str().to_str().unwrap().split("/").collect();
            let last = paths[paths.len() - 1];
            match i32::from_str(last) {
                Ok(nb) => {
                    let mut p = Processus {
                        pid: nb,
                        cmd: String::new(),
                        environ: Vec::new(),
                        exe: String::new(),
                        cwd: String::new(),
                        root: String::new(),
                        memory: 0
                    };

                    for entry in v.iter() {
                        let t = get_last(entry);

                        match t {
                            "cmdline" => {
                                p.cmd = String::from_str(copy_from_file(entry)[0].as_slice());
                            },
                            "environ" => {
                                p.environ = copy_from_file(entry);
                            },
                            "exe" => {
                                let s = read_link(entry.as_str().unwrap());
                                if s.is_ok() {
                                    p.exe = String::from_str(s.unwrap().as_os_str().to_str().unwrap());
                                }
                            },
                            "cwd" => {
                                p.cwd = String::from_str(realpath(Path::new(entry.as_str().unwrap())).unwrap().as_os_str().to_str().unwrap());
                            },
                            "root" => {
                                p.root = String::from_str(realpath(Path::new(entry.as_str().unwrap())).unwrap().as_os_str().to_str().unwrap());
                            },
                            "status" => {
                                let mut file = File::open(entry.as_str().unwrap()).unwrap();
                                let mut data = String::new();

                                file.read_to_string(&mut data);
                                let lines : Vec<&str> = data.split('\n').collect();
                                for line in lines.iter() {
                                    match *line {
                                        l if l.starts_with("VmSize") => {
                                            let parts : Vec<&str> = line.split(' ').collect();

                                            p.memory = u32::from_str(parts[parts.len() - 2]).unwrap();
                                            break;
                                        }
                                        _ => continue,
                                    }
                                }
                            }
                            l => {}
                        };
                    }
                    Some(p)
                },
                Err(_) => None
            }
        }
        Err(_) => None
    }
}

fn get_last(path: &PosixPath) -> &str {
    let t = path.as_os_str().to_str().unwrap();
    let v : Vec<&str> = t.split('/').collect();

    if v.len() > 0 {
        v[v.len() - 1]
    } else {
        ""
    }
}

fn copy_from_file(entry: &PosixPath) -> Vec<String> {
    match File::open(entry) {
        Ok(mut f) => {
            let mut d = String::new();

            f.read_to_string(&mut d);
            let mut v : Vec<&str> = d.split('\0').collect();
            let mut ret = Vec::new();

            for tmp in v.iter() {
                ret.push(String::from_str(tmp));
            }
            ret
        },
        Err(_) => Vec::new()
    }
}

fn old_realpath(original: &old_path::Path) -> old_io::IoResult<old_path::Path> {
    const MAX_LINKS_FOLLOWED: usize = 256;
    let original = try!(os::getcwd()).join(original);
    // Right now lstat on windows doesn't work quite well
    if cfg!(windows) {
        return Ok(original)
    }
    let result = original.root_path();
    let mut result = result.expect("make_absolute has no root_path");
    let mut followed = 0;
    for part in original.components() {
        result.push(part);
        loop {
            if followed == MAX_LINKS_FOLLOWED {
                return Ok(old_path::Path::new(""));
            }
            match fs::lstat(&result) {
                Err(..) => break,
                Ok(ref stat) if stat.kind != old_io::FileType::Symlink => break,
                Ok(..) => {
                    followed += 1;
                    let path = try!(fs::readlink(&result));
                    result.pop();
                    result.push(path);
                }
            }
        }
    }
    return Ok(result);
}

fn realpath(original: &Path) -> io::Result<PathBuf> {
    let old = old_path::Path::new(original.to_str().unwrap());

    match old_realpath(&old) {
        Ok(p) => Ok(PathBuf::new(p.as_str().unwrap())),
        Err(_) => Ok(PathBuf::new(""))
    }
}
<|endoftext|>