<commit_before>use urlencoding::{encode as urlencode};
use urlparse::{Query, parse_qs, urlparse, urlunparse};

fn partial_urlencode(value: &str) -> String {
  // The urlencode from the urlencoding library goes further than I like,
  // and also URL encodes ASCII digits. Reverse that stuff.
  urlencode(value)
    .replace("%30", "0")
    .replace("%31", "1")
    .replace("%32", "2")
    .replace("%33", "3")
    .replace("%34", "4")
    .replace("%35", "5")
    .replace("%36", "6")
    .replace("%37", "7")
    .replace("%38", "8")
    .replace("%39", "9")
}

/// Re-encode a query string for Rust
fn encode_querystring(query: Query) -> Option<String> {
  let mut query_components: Vec<String> = vec![];
  for (key, value) in query {
    for v in value.iter() {
      query_components.push(format!("{}={}", key, partial_urlencode(v)));
    }
  }
  if query_components.len() > 0 {
    Some(query_components.join("&"))
  } else {
    None
  }
}

/// Strip tracking junk and URL suffixes.
pub fn tidy_url(url: &str) -> String {
  let mut parsed_url = urlparse(url);

  // Always get the desktop version of Twitter URLs
  if parsed_url.netloc == "mobile.twitter.com" {
    parsed_url.netloc = String::from("twitter.com");
  }

  // Remove any tracking junk from Amazon URLs so they're not a
  // ridiculous length
  if parsed_url.netloc == "www.amazon.co.uk" {
    parsed_url.query = None;
    let mut new_path = String::from("");
    for component in parsed_url.path.split("ref=") {
      new_path = String::from(component);
      break;
    }
    parsed_url.path = new_path;
  }

  // Strip tracking junk from Medium, Mashable and Buzzfeed
  if (parsed_url.netloc == "medium.com") ||
     (parsed_url.netloc == "www.buzzfeed.com") ||
     (parsed_url.netloc == "mashable.com") {
    parsed_url.fragment = None;
  }

  // Remove '#notes' from Tumblr URLs
  if parsed_url.netloc.ends_with("tumblr.com") {
    parsed_url.fragment = match parsed_url.fragment {
      Some(fragment) => if fragment == "notes" { None } else { Some(fragment) },
      None => None,
    };
  }

  // Remove &feature=youtu.be from YouTube URLs
  if parsed_url.netloc.ends_with("youtube.com") {
    parsed_url.query = match parsed_url.query {
      Some(qs) => {
        let mut query = parse_qs(&qs);
        query.remove("feature");
        encode_querystring(query)
      },
      None => None,
    };
  }

  // Remove any UTM tracking parameters from URLs
  parsed_url.query = match parsed_url.query {
    Some(qs) => {
      let mut query = parse_qs(&qs);
      let utm_keys: Vec<_> = query
        .keys()
        .filter(|key| key.starts_with("utm_"))
        .map(|k| k.clone())
        .collect();
      for key in utm_keys {
        query.remove(&key);
      }
      encode_querystring(query)
    },
    None => None
  };

  // Tidy up the query and anchor links in modules on docs.python.org
  if parsed_url.netloc == "docs.python.org" {

    // If this is a module page, scrap any module- fragment.
    parsed_url.fragment = match parsed_url.fragment {
      Some(fragment) => if fragment.starts_with("module-") { None } else { Some(fragment) },
      None => None,
    };

    // Scrap the highlight
    parsed_url.query = match parsed_url.query {
      Some(qs) => {
        let mut query = parse_qs(&qs);
        query.remove("highlight");
        encode_querystring(query)
      },
      None => None,
    };
  }

  urlunparse(parsed_url)
}

macro_rules! tidy_url_tests {
  ($($name:ident: $value:expr,)*) => {
    $(
      #[test]
      fn $name() {
        let (input, expected) = $value;
        assert_eq!(expected, tidy_url(input));
      }
    )*
  }
}

tidy_url_tests! {
  twitter_mobile: (
    "https://mobile.twitter.com/Breaking911/status/822589852596191235",
    "https://twitter.com/Breaking911/status/822589852596191235"
  ),

  regular_twitter: (
    "https://twitter.com/WholesomeMeme/status/846421658835537921",
    "https://twitter.com/WholesomeMeme/status/846421658835537921"
  ),

  amazon_product: (
    "https://www.amazon.co.uk/dp/B01DFKBL68/ref=gw_aucc_comb_AB_clean_2?pf_rd_r=N1MF2PWADRHS55427ETG&pf_rd_p=c11a2c11-c670-46ff-87a1-c1eef4fad652",
    "https://www.amazon.co.uk/dp/B01DFKBL68/"
  ),

  medium_with_tracking: (
    "https://medium.com/@anildash/forget-why-its-time-to-get-to-work-c49ac5f0da20#.sjyskxdsz",
    "https://medium.com/@anildash/forget-why-its-time-to-get-to-work-c49ac5f0da20"
  ),

  tumblr_with_notes: (
    "http://azurelunatic.tumblr.com/post/155525051123/things-about-hufflepuffs-539#notes",
    "http://azurelunatic.tumblr.com/post/155525051123/things-about-hufflepuffs-539"
  ),

  buzzfeed_with_tracking: (
    "https://www.buzzfeed.com/katienotopoulos/the-end-of-apple-man#.biqmkzz8kn",
    "https://www.buzzfeed.com/katienotopoulos/the-end-of-apple-man"
  ),

  mashable_with_tracking: (
    "http://mashable.com/2016/03/21/apple-liam-recycling-robot/#b9y4lv3m4qqX",
    "http://mashable.com/2016/03/21/apple-liam-recycling-robot/"
  ),

  youtube_plain_video: (
    "https://www.youtube.com/watch?v=zB4I68XVPzQ",
    "https://www.youtube.com/watch?v=zB4I68XVPzQ",
  ),

  youtube_with_timestamp: (
    "https://www.youtube.com/watch?v=D68cUzqcTrg&feature=youtu.be#t=1m",
    "https://www.youtube.com/watch?v=D68cUzqcTrg#t=1m",
  ),

  youtube_with_trailing_feature: (
    "https://www.youtube.com/watch?v=PbJqNa0_Oz0&feature=youtu.be",
    "https://www.youtube.com/watch?v=PbJqNa0_Oz0",
  ),

  youtube_with_leading_feature: (
    "https://www.youtube.com/watch?feature=youtu.be&v=oPo4n9tBPsk",
    "https://www.youtube.com/watch?v=oPo4n9tBPsk",
  ),

  youtube_with_only_feature: (
    "https://www.youtube.com/watch?feature=youtu.be",
    "https://www.youtube.com/watch",
  ),

  youtube_channel: (
    "https://www.youtube.com/user/TheQIElves",
    "https://www.youtube.com/user/TheQIElves"
  ),

  single_utm_tracker: (
    "https://example.com?utm_medium=social",
    "https://example.com",
  ),

  multiple_utm_tracker: (
    "https://example.com?utm_medium=social&utm_source=twitter",
    "https://example.com",
  ),

  multiple_utm_tracker_with_others: (
    "https://example.com?utm_medium=social&foo=bar&utm_source=twitter",
    "https://example.com?foo=bar",
  ),

  url_with_spaces: (
    "https://example.com?foo=bar%20baz",
    "https://example.com?foo=bar%20baz"
  ),

  url_with_numerals: (
    "https://example.com?foo=bar0baz",
    "https://example.com?foo=bar0baz"
  ),

  python_docs_bare: (
    "https://docs.python.org/3.5/library/subprocess.html",
    "https://docs.python.org/3.5/library/subprocess.html"
  ),

  python_docs_with_anchor: (
    "https://docs.python.org/3.5/library/subprocess.html#subprocess.run",
    "https://docs.python.org/3.5/library/subprocess.html#subprocess.run"
  ),

  python_docs_with_highlight: (
    "https://docs.python.org/3.5/library/subprocess.html?highlight=subprocess",
    "https://docs.python.org/3.5/library/subprocess.html"
  ),

  python_docs_with_highlight_anchor: (
    "https://docs.python.org/3.5/library/subprocess.html?highlight=subprocess#subprocess.run",
    "https://docs.python.org/3.5/library/subprocess.html#subprocess.run"
  ),

  python_docs_with_highlight_module_anchor: (
    "https://docs.python.org/3.5/library/subprocess.html?highlight=subprocess#module-subprocess",
    "https://docs.python.org/3.5/library/subprocess.html"
  ),

  stack_overflow_non_question: (
    "http://stackoverflow.com/questions/tagged/html+regex",
    "http://stackoverflow.com/questions/tagged/html+regex"
  ),

  stack_overflow_question: (
    "http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags",
    "http://stackoverflow.com/q/1732348/1558022"
  ),

  stack_overflow_answer: (
    "http://stackoverflow.com/questions/82831/how-do-i-check-whether-a-file-exists-using-python#82852",
    "http://stackoverflow.com/a/82852/1558022"
  ),
}
<commit_msg>Fix referral links on Stack Overflow<commit_after>use urlencoding::{encode as urlencode};
use urlparse::{Query, parse_qs, urlparse, urlunparse};

fn partial_urlencode(value: &str) -> String {
  // The urlencode from the urlencoding library goes further than I like,
  // and also URL encodes ASCII digits. Reverse that stuff.
  urlencode(value)
    .replace("%30", "0")
    .replace("%31", "1")
    .replace("%32", "2")
    .replace("%33", "3")
    .replace("%34", "4")
    .replace("%35", "5")
    .replace("%36", "6")
    .replace("%37", "7")
    .replace("%38", "8")
    .replace("%39", "9")
}

/// Re-encode a query string for Rust
fn encode_querystring(query: Query) -> Option<String> {
  let mut query_components: Vec<String> = vec![];
  for (key, value) in query {
    for v in value.iter() {
      query_components.push(format!("{}={}", key, partial_urlencode(v)));
    }
  }
  if query_components.len() > 0 {
    Some(query_components.join("&"))
  } else {
    None
  }
}

/// Strip tracking junk and URL suffixes.
pub fn tidy_url(url: &str) -> String {
  let mut parsed_url = urlparse(url);

  // Always get the desktop version of Twitter URLs
  if parsed_url.netloc == "mobile.twitter.com" {
    parsed_url.netloc = String::from("twitter.com");
  }

  // Remove any tracking junk from Amazon URLs so they're not a
  // ridiculous length
  if parsed_url.netloc == "www.amazon.co.uk" {
    parsed_url.query = None;
    let mut new_path = String::from("");
    for component in parsed_url.path.split("ref=") {
      new_path = String::from(component);
      break;
    }
    parsed_url.path = new_path;
  }

  // Strip tracking junk from Medium, Mashable and Buzzfeed
  if (parsed_url.netloc == "medium.com") ||
     (parsed_url.netloc == "www.buzzfeed.com") ||
     (parsed_url.netloc == "mashable.com") {
    parsed_url.fragment = None;
  }

  // Remove '#notes' from Tumblr URLs
  if parsed_url.netloc.ends_with("tumblr.com") {
    parsed_url.fragment = match parsed_url.fragment {
      Some(fragment) => if fragment == "notes" { None } else { Some(fragment) },
      None => None,
    };
  }

  // Remove &feature=youtu.be from YouTube URLs
  if parsed_url.netloc.ends_with("youtube.com") {
    parsed_url.query = match parsed_url.query {
      Some(qs) => {
        let mut query = parse_qs(&qs);
        query.remove("feature");
        encode_querystring(query)
      },
      None => None,
    };
  }

  // Remove any UTM tracking parameters from URLs
  parsed_url.query = match parsed_url.query {
    Some(qs) => {
      let mut query = parse_qs(&qs);
      let utm_keys: Vec<_> = query
        .keys()
        .filter(|key| key.starts_with("utm_"))
        .map(|k| k.clone())
        .collect();
      for key in utm_keys {
        query.remove(&key);
      }
      encode_querystring(query)
    },
    None => None
  };

  // Tidy up the query and anchor links in modules on docs.python.org
  if parsed_url.netloc == "docs.python.org" {

    // If this is a module page, scrap any module- fragment.
    parsed_url.fragment = match parsed_url.fragment {
      Some(fragment) => if fragment.starts_with("module-") { None } else { Some(fragment) },
      None => None,
    };

    // Scrap the highlight
    parsed_url.query = match parsed_url.query {
      Some(qs) => {
        let mut query = parse_qs(&qs);
        query.remove("highlight");
        encode_querystring(query)
      },
      None => None,
    };
  }

  // Convert links to questions on Stack Overflow to insert my sharing
  // link for the Announcer badge.
  if parsed_url.netloc == "stackoverflow.com" {
    if parsed_url.path.starts_with("/questions/") {
      let original_path = parsed_url.path.to_owned();
      let new_path = match original_path.split("/").nth(2) {
        Some(path_component) => {
          // Check it's a number
          match path_component.parse::<i32>() {
            Ok(q_id) => {
              // Check if there's an answer fragment
              match parsed_url.fragment.to_owned() {
                Some(ans_id) => Some(format!("/a/{}/1558022", ans_id)),
                None => Some(format!("/q/{}/1558022", q_id)),
              }
            },
            Err(_) => None,
          }
        }
        None => None,
      };
      match new_path {
        Some(p) => {
          parsed_url.path = p;
          parsed_url.fragment = None;
        },
        None => (),
      };
    }
  }

  urlunparse(parsed_url)
}

macro_rules! tidy_url_tests {
  ($($name:ident: $value:expr,)*) => {
    $(
      #[test]
      fn $name() {
        let (input, expected) = $value;
        assert_eq!(expected, tidy_url(input));
      }
    )*
  }
}

tidy_url_tests! {
  twitter_mobile: (
    "https://mobile.twitter.com/Breaking911/status/822589852596191235",
    "https://twitter.com/Breaking911/status/822589852596191235"
  ),

  regular_twitter: (
    "https://twitter.com/WholesomeMeme/status/846421658835537921",
    "https://twitter.com/WholesomeMeme/status/846421658835537921"
  ),

  amazon_product: (
    "https://www.amazon.co.uk/dp/B01DFKBL68/ref=gw_aucc_comb_AB_clean_2?pf_rd_r=N1MF2PWADRHS55427ETG&pf_rd_p=c11a2c11-c670-46ff-87a1-c1eef4fad652",
    "https://www.amazon.co.uk/dp/B01DFKBL68/"
  ),

  medium_with_tracking: (
    "https://medium.com/@anildash/forget-why-its-time-to-get-to-work-c49ac5f0da20#.sjyskxdsz",
    "https://medium.com/@anildash/forget-why-its-time-to-get-to-work-c49ac5f0da20"
  ),

  tumblr_with_notes: (
    "http://azurelunatic.tumblr.com/post/155525051123/things-about-hufflepuffs-539#notes",
    "http://azurelunatic.tumblr.com/post/155525051123/things-about-hufflepuffs-539"
  ),

  buzzfeed_with_tracking: (
    "https://www.buzzfeed.com/katienotopoulos/the-end-of-apple-man#.biqmkzz8kn",
    "https://www.buzzfeed.com/katienotopoulos/the-end-of-apple-man"
  ),

  mashable_with_tracking: (
    "http://mashable.com/2016/03/21/apple-liam-recycling-robot/#b9y4lv3m4qqX",
    "http://mashable.com/2016/03/21/apple-liam-recycling-robot/"
  ),

  youtube_plain_video: (
    "https://www.youtube.com/watch?v=zB4I68XVPzQ",
    "https://www.youtube.com/watch?v=zB4I68XVPzQ",
  ),

  youtube_with_timestamp: (
    "https://www.youtube.com/watch?v=D68cUzqcTrg&feature=youtu.be#t=1m",
    "https://www.youtube.com/watch?v=D68cUzqcTrg#t=1m",
  ),

  youtube_with_trailing_feature: (
    "https://www.youtube.com/watch?v=PbJqNa0_Oz0&feature=youtu.be",
    "https://www.youtube.com/watch?v=PbJqNa0_Oz0",
  ),

  youtube_with_leading_feature: (
    "https://www.youtube.com/watch?feature=youtu.be&v=oPo4n9tBPsk",
    "https://www.youtube.com/watch?v=oPo4n9tBPsk",
  ),

  youtube_with_only_feature: (
    "https://www.youtube.com/watch?feature=youtu.be",
    "https://www.youtube.com/watch",
  ),

  youtube_channel: (
    "https://www.youtube.com/user/TheQIElves",
    "https://www.youtube.com/user/TheQIElves"
  ),

  single_utm_tracker: (
    "https://example.com?utm_medium=social",
    "https://example.com",
  ),

  multiple_utm_tracker: (
    "https://example.com?utm_medium=social&utm_source=twitter",
    "https://example.com",
  ),

  multiple_utm_tracker_with_others: (
    "https://example.com?utm_medium=social&foo=bar&utm_source=twitter",
    "https://example.com?foo=bar",
  ),

  url_with_spaces: (
    "https://example.com?foo=bar%20baz",
    "https://example.com?foo=bar%20baz"
  ),

  url_with_numerals: (
    "https://example.com?foo=bar0baz",
    "https://example.com?foo=bar0baz"
  ),

  python_docs_bare: (
    "https://docs.python.org/3.5/library/subprocess.html",
    "https://docs.python.org/3.5/library/subprocess.html"
  ),

  python_docs_with_anchor: (
    "https://docs.python.org/3.5/library/subprocess.html#subprocess.run",
    "https://docs.python.org/3.5/library/subprocess.html#subprocess.run"
  ),

  python_docs_with_highlight: (
    "https://docs.python.org/3.5/library/subprocess.html?highlight=subprocess",
    "https://docs.python.org/3.5/library/subprocess.html"
  ),

  python_docs_with_highlight_anchor: (
    "https://docs.python.org/3.5/library/subprocess.html?highlight=subprocess#subprocess.run",
    "https://docs.python.org/3.5/library/subprocess.html#subprocess.run"
  ),

  python_docs_with_highlight_module_anchor: (
    "https://docs.python.org/3.5/library/subprocess.html?highlight=subprocess#module-subprocess",
    "https://docs.python.org/3.5/library/subprocess.html"
  ),

  stack_overflow_non_question: (
    "http://stackoverflow.com/questions/tagged/html+regex",
    "http://stackoverflow.com/questions/tagged/html+regex"
  ),

  stack_overflow_question: (
    "http://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags",
    "http://stackoverflow.com/q/1732348/1558022"
  ),

  stack_overflow_answer: (
    "http://stackoverflow.com/questions/82831/how-do-i-check-whether-a-file-exists-using-python#82852",
    "http://stackoverflow.com/a/82852/1558022"
  ),
}
<|endoftext|>


<commit_before>//! module for topologcal vector space
//!

use std::iter::Sum;
use ndarray::{ArrayBase, Data, Dimension, LinalgScalar};
use num_traits::Float;
use super::vector::*;

pub fn all_close_max<A, Tol, S1, S2, D>(test: &ArrayBase<S1, D>,
                                        truth: &ArrayBase<S2, D>,
                                        atol: Tol)
                                        -> Result<Tol, Tol>
    where A: LinalgScalar + Squared<Output = Tol>,
          Tol: Float + Sum,
          S1: Data<Elem = A>,
          S2: Data<Elem = A>,
          D: Dimension
{
    let tol = (test - truth).norm_max();
    if tol < atol { Ok(tol) } else { Err(tol) }
}

pub fn all_close_l1<A, Tol, S1, S2, D>(test: &ArrayBase<S1, D>, truth: &ArrayBase<S2, D>, rtol: Tol) -> Result<Tol, Tol>
    where A: LinalgScalar + Squared<Output = Tol>,
          Tol: Float + Sum,
          S1: Data<Elem = A>,
          S2: Data<Elem = A>,
          D: Dimension
{
    let tol = (test - truth).norm_l1() / truth.norm_l1();
    if tol < rtol { Ok(tol) } else { Err(tol) }
}

pub fn all_close_l2<A, Tol, S1, S2, D>(test: &ArrayBase<S1, D>, truth: &ArrayBase<S2, D>, rtol: Tol) -> Result<Tol, Tol>
    where A: LinalgScalar + Squared<Output = Tol>,
          Tol: Float + Sum,
          S1: Data<Elem = A>,
          S2: Data<Elem = A>,
          D: Dimension
{
    let tol = (test - truth).norm_l2() / truth.norm_l2();
    if tol < rtol { Ok(tol) } else { Err(tol) }
}
<commit_msg>Add {v,h}stack<commit_after>
use std::iter::Sum;
use ndarray::*;
use num_traits::Float;
use super::vector::*;

/// stack vectors into matrix horizontally
pub fn hstack<A, S>(xs: &[ArrayBase<S, Ix1>]) -> Result<Array<A, Ix2>, ShapeError>
    where A: NdFloat,
          S: Data<Elem = A>
{
    let views: Vec<_> = xs.iter()
        .map(|x| {
            let n = x.len();
            x.view().into_shape((n, 1)).unwrap()
        })
        .collect();
    stack(Axis(1), &views)
}

/// stack vectors into matrix vertically
pub fn vstack<A, S>(xs: &[ArrayBase<S, Ix1>]) -> Result<Array<A, Ix2>, ShapeError>
    where A: NdFloat,
          S: Data<Elem = A>
{
    let views: Vec<_> = xs.iter()
        .map(|x| {
            let n = x.len();
            x.view().into_shape((1, n)).unwrap()
        })
        .collect();
    stack(Axis(0), &views)
}

pub fn all_close_max<A, Tol, S1, S2, D>(test: &ArrayBase<S1, D>,
                                        truth: &ArrayBase<S2, D>,
                                        atol: Tol)
                                        -> Result<Tol, Tol>
    where A: LinalgScalar + Squared<Output = Tol>,
          Tol: Float + Sum,
          S1: Data<Elem = A>,
          S2: Data<Elem = A>,
          D: Dimension
{
    let tol = (test - truth).norm_max();
    if tol < atol { Ok(tol) } else { Err(tol) }
}

pub fn all_close_l1<A, Tol, S1, S2, D>(test: &ArrayBase<S1, D>, truth: &ArrayBase<S2, D>, rtol: Tol) -> Result<Tol, Tol>
    where A: LinalgScalar + Squared<Output = Tol>,
          Tol: Float + Sum,
          S1: Data<Elem = A>,
          S2: Data<Elem = A>,
          D: Dimension
{
    let tol = (test - truth).norm_l1() / truth.norm_l1();
    if tol < rtol { Ok(tol) } else { Err(tol) }
}

pub fn all_close_l2<A, Tol, S1, S2, D>(test: &ArrayBase<S1, D>, truth: &ArrayBase<S2, D>, rtol: Tol) -> Result<Tol, Tol>
    where A: LinalgScalar + Squared<Output = Tol>,
          Tol: Float + Sum,
          S1: Data<Elem = A>,
          S2: Data<Elem = A>,
          D: Dimension
{
    let tol = (test - truth).norm_l2() / truth.norm_l2();
    if tol < rtol { Ok(tol) } else { Err(tol) }
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::ops::Deref;

pub trait OptionBorrowExt<T: ?Sized> {
    fn borrow_internals(&self) -> Option<&T>;
}

impl<T: ?Sized, U> OptionBorrowExt<T> for Option<U> where U: Borrow<T> {
    fn borrow_internals(&self) -> Option<&T> {
        self.as_ref().map(Borrow::borrow)
    }
}

pub trait IteratorClonedPairwiseExt {
    fn cloned_pairwise(self) -> ClonedPairwise<Self>;
}

impl<I, RK, RV, K, V> IteratorClonedPairwiseExt for I
        where I: Iterator<Item=(RK, RV)>,
              RK: Deref<Target=K>, RV: Deref<Target=V>,
              K: Clone, V: Clone {
    fn cloned_pairwise(self) -> ClonedPairwise<I> {
        ClonedPairwise(self)
    }
}

pub struct ClonedPairwise<I>(I);

impl<I, RK, RV, K, V> Iterator for ClonedPairwise<I>
    where I: Iterator<Item=(RK, RV)>,
          RK: Deref<Target=K>,
          RV: Deref<Target=V>,
          K: Clone,
          V: Clone, {

    type Item = (K, V);

    fn next(&mut self) -> Option<(K, V)> {
        self.0.next().map(|(k, v)| (k.clone(), v.clone()))
    }
}

#[cfg(test)]
mod tests {
    use super::{OptionBorrowExt, IteratorClonedPairwiseExt};

    #[test]
    fn test_borrow_value() {
        let v: Option<isize> = Some(10);
        let r: Option<&isize> = v.borrow_internals();
        assert!(r.is_some());
        assert_eq!(*r.unwrap(), 10);

        let v: Option<isize> = None;
        let r: Option<&isize> = v.borrow_internals();
        assert!(r.is_none());
    }

    #[test]
    fn test_borrow_string() {
        let v: Option<String> = Some("abcde".to_string());
        let r: Option<&str> = v.borrow_internals();
        assert!(r.is_some());
        assert_eq!(r.unwrap(), "abcde");

        let v: Option<String> = None;
        let r: Option<&str> = v.borrow_internals();
        assert!(r.is_none());
    }

    #[test]
    fn test_cloned_pairwise() {
        use std::collections::HashMap;

        let mut v1: HashMap<String, Vec<usize>> = HashMap::new();
        v1.insert("a".to_string(), vec![1]);
        v1.insert("b".to_string(), vec![2, 3]);
        v1.insert("c".to_string(), vec![4, 5, 6]);

        let v2: HashMap<String, Vec<usize>> = v1.iter().cloned_pairwise().collect();
        assert_eq!(v1, v2);
    }
}
<commit_msg>    Implementors of IteratorClonedPairwiseExt must implement Sized<commit_after>use std::borrow::Borrow;
use std::ops::Deref;

pub trait OptionBorrowExt<T: ?Sized> {
    fn borrow_internals(&self) -> Option<&T>;
}

impl<T: ?Sized, U> OptionBorrowExt<T> for Option<U> where U: Borrow<T> {
    fn borrow_internals(&self) -> Option<&T> {
        self.as_ref().map(Borrow::borrow)
    }
}

pub trait IteratorClonedPairwiseExt: Sized {
    fn cloned_pairwise(self) -> ClonedPairwise<Self>;
}

impl<I, RK, RV, K, V> IteratorClonedPairwiseExt for I
        where I: Iterator<Item=(RK, RV)>,
              RK: Deref<Target=K>, RV: Deref<Target=V>,
              K: Clone, V: Clone {
    fn cloned_pairwise(self) -> ClonedPairwise<I> {
        ClonedPairwise(self)
    }
}

pub struct ClonedPairwise<I>(I);

impl<I, RK, RV, K, V> Iterator for ClonedPairwise<I>
    where I: Iterator<Item=(RK, RV)>,
          RK: Deref<Target=K>,
          RV: Deref<Target=V>,
          K: Clone,
          V: Clone, {

    type Item = (K, V);

    fn next(&mut self) -> Option<(K, V)> {
        self.0.next().map(|(k, v)| (k.clone(), v.clone()))
    }
}

#[cfg(test)]
mod tests {
    use super::{OptionBorrowExt, IteratorClonedPairwiseExt};

    #[test]
    fn test_borrow_value() {
        let v: Option<isize> = Some(10);
        let r: Option<&isize> = v.borrow_internals();
        assert!(r.is_some());
        assert_eq!(*r.unwrap(), 10);

        let v: Option<isize> = None;
        let r: Option<&isize> = v.borrow_internals();
        assert!(r.is_none());
    }

    #[test]
    fn test_borrow_string() {
        let v: Option<String> = Some("abcde".to_string());
        let r: Option<&str> = v.borrow_internals();
        assert!(r.is_some());
        assert_eq!(r.unwrap(), "abcde");

        let v: Option<String> = None;
        let r: Option<&str> = v.borrow_internals();
        assert!(r.is_none());
    }

    #[test]
    fn test_cloned_pairwise() {
        use std::collections::HashMap;

        let mut v1: HashMap<String, Vec<usize>> = HashMap::new();
        v1.insert("a".to_string(), vec![1]);
        v1.insert("b".to_string(), vec![2, 3]);
        v1.insert("c".to_string(), vec![4, 5, 6]);

        let v2: HashMap<String, Vec<usize>> = v1.iter().cloned_pairwise().collect();
        assert_eq!(v1, v2);
    }
}
<|endoftext|>


<commit_before>//! `bless` updates the reference files in the repo with changed output files
//! from the last test run.

use std::env;
use std::ffi::OsStr;
use std::fs;
use std::lazy::SyncLazy;
use std::path::PathBuf;
use walkdir::WalkDir;

use crate::clippy_project_root;

// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
    Some(v) => v.into(),
    None => env::current_dir().unwrap().join("target"),
});

pub fn bless() {
    let test_dirs = [
        clippy_project_root().join("tests").join("ui"),
        clippy_project_root().join("tests").join("ui-toml"),
        clippy_project_root().join("tests").join("ui-cargo"),
    ];
    for test_dir in &test_dirs {
        WalkDir::new(test_dir)
            .into_iter()
            .filter_map(Result::ok)
            .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
            .for_each(|f| {
                update_reference_file(f.path().with_extension("stdout"));
                update_reference_file(f.path().with_extension("stderr"));
                update_reference_file(f.path().with_extension("fixed"));
            });
    }
}

fn update_reference_file(reference_file_path: PathBuf) {
    let test_output_path = build_dir().join(PathBuf::from(reference_file_path.file_name().unwrap()));
    let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap();

    // If compiletest did not write any changes during the test run,
    // we don't have to update anything
    if !test_output_path.exists() {
        return;
    }

    let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
    let reference_file = fs::read(&reference_file_path).expect("Unable to read reference file");

    if test_output_file != reference_file {
        // If a test run caused an output file to change, update the reference file
        println!("updating {}", &relative_reference_file_path.display());
        fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");

        if reference_file.is_empty() {
            // If we copied over an empty output file, we remove the now empty reference file
            println!("removing {}", &relative_reference_file_path.display());
            fs::remove_file(reference_file_path).expect("Could not remove reference file");
        }
    }
}

fn build_dir() -> PathBuf {
    let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
    let mut path = PathBuf::new();
    path.push(CARGO_TARGET_DIR.clone());
    path.push(profile);
    path.push("test_build_base");
    path
}
<commit_msg>Auto merge of #6471 - phansch:fix-bless, r=flip1995<commit_after>//! `bless` updates the reference files in the repo with changed output files
//! from the last test run.

use std::env;
use std::ffi::OsStr;
use std::fs;
use std::lazy::SyncLazy;
use std::path::PathBuf;
use walkdir::WalkDir;

use crate::clippy_project_root;

// NOTE: this is duplicated with tests/cargo/mod.rs What to do?
pub static CARGO_TARGET_DIR: SyncLazy<PathBuf> = SyncLazy::new(|| match env::var_os("CARGO_TARGET_DIR") {
    Some(v) => v.into(),
    None => env::current_dir().unwrap().join("target"),
});

pub fn bless() {
    let test_dirs = [
        clippy_project_root().join("tests").join("ui"),
        clippy_project_root().join("tests").join("ui-toml"),
        clippy_project_root().join("tests").join("ui-cargo"),
    ];
    for test_dir in &test_dirs {
        WalkDir::new(test_dir)
            .into_iter()
            .filter_map(Result::ok)
            .filter(|f| f.path().extension() == Some(OsStr::new("rs")))
            .for_each(|f| {
                update_reference_file(f.path().with_extension("stdout"));
                update_reference_file(f.path().with_extension("stderr"));
                update_reference_file(f.path().with_extension("fixed"));
            });
    }
}

fn update_reference_file(reference_file_path: PathBuf) {
    let test_output_path = build_dir().join(PathBuf::from(reference_file_path.file_name().unwrap()));
    let relative_reference_file_path = reference_file_path.strip_prefix(clippy_project_root()).unwrap();

    // If compiletest did not write any changes during the test run,
    // we don't have to update anything
    if !test_output_path.exists() {
        return;
    }

    let test_output_file = fs::read(&test_output_path).expect("Unable to read test output file");
    let reference_file = fs::read(&reference_file_path).unwrap_or_default();

    if test_output_file != reference_file {
        // If a test run caused an output file to change, update the reference file
        println!("updating {}", &relative_reference_file_path.display());
        fs::copy(test_output_path, &reference_file_path).expect("Could not update reference file");

        // We need to re-read the file now because it was potentially updated from copying
        let reference_file = fs::read(&reference_file_path).unwrap_or_default();

        if reference_file.is_empty() {
            // If we copied over an empty output file, we remove the now empty reference file
            println!("removing {}", &relative_reference_file_path.display());
            fs::remove_file(reference_file_path).expect("Could not remove reference file");
        }
    }
}

fn build_dir() -> PathBuf {
    let profile = env::var("PROFILE").unwrap_or_else(|_| "debug".to_string());
    let mut path = PathBuf::new();
    path.push(CARGO_TARGET_DIR.clone());
    path.push(profile);
    path.push("test_build_base");
    path
}
<|endoftext|>


<commit_before>//! A read-only Bounding Volume Tree.

use std::collections::BinaryHeap;

use alga::general::Real;
use na;
use partitioning::{BVTVisitor, BVTTVisitor, BVTCostFn};
use bounding_volume::BoundingVolume;
use utils::data::ref_with_cost::RefWithCost;
use utils;
use math::Point;

/// A Boundig Volume Tree.
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub struct BVT<B, BV> {
    tree: Option<BVTNode<B, BV>>
}

/// A node of the bounding volume tree.
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub enum BVTNode<B, BV> {
    // XXX: give a faster access to the BV
    /// An internal node.
    Internal(BV, Box<BVTNode<B, BV>>, Box<BVTNode<B, BV>>),
    /// A leaf.
    Leaf(BV, B)
}

/// Result of a binary partition.
pub enum BinaryPartition<B, BV> {
    /// Result of the partitioning of one element.
    Part(B),
    /// Result of the partitioning of several elements.
    Parts(Vec<(B, BV)>, Vec<(B, BV)>)
}

impl<B, BV> BVT<B, BV> {
    // FIXME: add higher level constructors ?
    /// Builds a bounding volume tree using an user-defined construction function.
    pub fn new_with_partitioner<F: FnMut(usize, Vec<(B, BV)>) -> (BV, BinaryPartition<B, BV>)>
      (leaves:      Vec<(B, BV)>,
       partitioner: &mut F)
          -> BVT<B, BV> {
        if leaves.len() == 0 {
            BVT {
                tree: None
            }
        }
        else {
            BVT {
                tree: Some(Self::_new_with_partitioner(0, leaves, partitioner))
            }
        }
    }

    /// Traverses this tree using an object implementing the `BVTVisitor`trait.
    ///
    /// This will traverse the whole tree and call the visitor `.visit_internal(...)` (resp.
    /// `.visit_leaf(...)`) method on each internal (resp. leaf) node.
    pub fn visit<Vis: BVTVisitor<B, BV>>(&self, visitor: &mut Vis) {
        match self.tree {
            Some(ref t) => t.visit(visitor),
            None        => { }
        }
    }

    /// Visits the bounding volume traversal tree implicitely formed with `other`.
    pub fn visit_bvtt<Vis: BVTTVisitor<B, BV>>(&self, other: &BVT<B, BV>, visitor: &mut Vis) {
        match (&self.tree, &other.tree) {
            (&Some(ref ta), &Some(ref tb)) => ta.visit_bvtt(tb, visitor),
            _ => { }
        }
    }

    // FIXME: really return a ref to B ?
    /// Performs a best-fist-search on the tree.
    ///
    /// Returns the content of the leaf with the smallest associated cost, and a result of
    /// user-defined type.
    pub fn best_first_search<'a, N, BFS>(&'a self, algorithm: &mut BFS) -> Option<(&'a B, BFS::UserData)>
        where N:   Real,
              BFS: BVTCostFn<N, B, BV> {
        match self.tree {
            Some(ref t) => t.best_first_search(algorithm),
            None        => None
        }
    }

    /// Reference to the bounding volume of the tree root.
    pub fn root_bounding_volume<'r>(&'r self) -> Option<&'r BV> {
        match self.tree {
            Some(ref n) => {
                match *n {
                    BVTNode::Internal(ref bv, _, _) => Some(bv),
                    BVTNode::Leaf(ref bv, _) => Some(bv)
                }
            },
            None => None
        }
    }

    /// Computes the depth of this tree.
    pub fn depth(&self) -> usize {
        match self.tree {
            Some(ref n) => n.depth(),
            None        => 0
        }
    }
}

impl<B, BV> BVT<B, BV> {
    /// Creates a balanced `BVT`.
    pub fn new_balanced<P>(leaves: Vec<(B, BV)>) -> BVT<B, BV>
        where P:  Point,
              BV: BoundingVolume<P> + Clone {
        BVT::new_with_partitioner(leaves, &mut Self::median_partitioner)
    }

    /// Construction function for a kdree to be used with `BVT::new_with_partitioner`.
    pub fn median_partitioner_with_centers<P, F: FnMut(&B, &BV) -> P>
            (depth: usize, leaves: Vec<(B, BV)>, center: &mut F) -> (BV, BinaryPartition<B, BV>)
        where P:  Point,
              BV: BoundingVolume<P> + Clone {
        if leaves.len() == 0 {
            panic!("Cannot build a tree without leaves.");
        }
        else if leaves.len() == 1 {
            let (b, bv) = leaves.into_iter().next().unwrap();
            (bv, BinaryPartition::Part(b))
        }
        else {
            let sep_axis = depth % na::dimension::<P::Vector>();

            // compute the median along sep_axis
            let mut median = Vec::new();

            for l in leaves.iter() {
                let c = (*center)(&l.0, &l.1);
                median.push(c[sep_axis]);
            }

            let median = utils::median(&mut median[..]);

            // build the partitions
            let mut right = Vec::new();
            let mut left  = Vec::new();
            let mut bounding_bounding_volume = leaves[0].1.clone();

            let mut insert_left = false;

            for (b, bv) in leaves.into_iter() {
                bounding_bounding_volume.merge(&bv);

                let pos = (*center)(&b, &bv)[sep_axis];

                if pos < median || (pos == median && insert_left) {
                    left.push((b, bv));
                    insert_left = false;
                }
                else {
                    right.push((b, bv));
                    insert_left = true;
                }
            }

            // XXX: hack to avoid degeneracies.
            if left.len() == 0 {
                left.push(right.pop().unwrap());
            }
            else if right.len() == 0 {
                right.push(left.pop().unwrap());
            }

            (bounding_bounding_volume, BinaryPartition::Parts(left, right))
        }
    }

    /// Construction function for a kdree to be used with `BVT::new_with_partitioner`.
    pub fn median_partitioner<P>(depth: usize, leaves: Vec<(B, BV)>) -> (BV, BinaryPartition<B, BV>)
        where P:  Point,
              BV: BoundingVolume<P> + Clone {
        Self::median_partitioner_with_centers(depth, leaves, &mut |_, bv| bv.center())
    }

    fn _new_with_partitioner<F: FnMut(usize, Vec<(B, BV)>) -> (BV, BinaryPartition<B, BV>)>
                             (depth: usize, leaves: Vec<(B, BV)>, partitioner: &mut F) -> BVTNode<B, BV> {
        let (bv, partitions) = partitioner(depth, leaves);

        match partitions {
            BinaryPartition::Part(b)            => BVTNode::Leaf(bv, b),
            BinaryPartition::Parts(left, right) => {
                let left  = Self::_new_with_partitioner(depth + 1, left, partitioner);
                let right = Self::_new_with_partitioner(depth + 1, right, partitioner);
                BVTNode::Internal(bv, Box::new(left), Box::new(right))
            }
        }
    }
}

impl<B, BV> BVTNode<B, BV> {
    /// The bounding volume of this node.
    #[inline]
    pub fn bounding_volume<'a>(&'a self) -> &'a BV {
        match *self {
            BVTNode::Internal(ref bv, _, _) => bv,
            BVTNode::Leaf(ref bv, _)        => bv
        }
    }

    fn visit<Vis: BVTVisitor<B, BV>>(&self, visitor: &mut Vis) {
        match *self {
            BVTNode::Internal(ref bv, ref left, ref right) => {
                if visitor.visit_internal(bv) {
                    left.visit(visitor);
                    right.visit(visitor);
                }
            },
            BVTNode::Leaf(ref bv, ref b) => {
                visitor.visit_leaf(b, bv);
            }
        }
    }

    fn visit_bvtt<Vis: BVTTVisitor<B, BV>>(&self, other: &BVTNode<B, BV>, visitor: &mut Vis) {
        match (self, other) {
            (&BVTNode::Internal(ref bva, ref la, ref ra), &BVTNode::Internal(ref bvb, ref lb, ref rb)) => {
                if visitor.visit_internal_internal(bva, bvb) {
                    la.visit_bvtt(&**lb, visitor);
                    la.visit_bvtt(&**rb, visitor);
                    ra.visit_bvtt(&**lb, visitor);
                    ra.visit_bvtt(&**rb, visitor);
                }
            },
            (&BVTNode::Internal(ref bva, ref la, ref ra), &BVTNode::Leaf(ref bvb, ref bb)) => {
                if visitor.visit_internal_leaf(bva, bb, bvb) {
                    la.visit_bvtt(other, visitor);
                    ra.visit_bvtt(other, visitor);
                }
            },
            (&BVTNode::Leaf(ref bva, ref ba), &BVTNode::Internal(ref bvb, ref lb, ref rb)) => {
                if visitor.visit_leaf_internal(ba, bva, bvb) {
                    self.visit_bvtt(&**lb, visitor);
                    self.visit_bvtt(&**rb, visitor);
                }
            },
            (&BVTNode::Leaf(ref bva, ref ba), &BVTNode::Leaf(ref bvb, ref bb)) => {
                visitor.visit_leaf_leaf(ba, bva, bb, bvb)
            }
        }
    }

    fn best_first_search<'a, N, BFS>(&'a self, algorithm: &mut BFS) -> Option<(&'a B, BFS::UserData)>
        where N:   Real,
              BFS: BVTCostFn<N, B, BV> {
        let mut queue: BinaryHeap<RefWithCost<'a, N, BVTNode<B, BV>>> = BinaryHeap::new();
        let mut best_cost = N::max_value();
        let mut result    = None;

        match algorithm.compute_bv_cost(self.bounding_volume()) {
            Some(cost) => queue.push(RefWithCost::new(self, cost)),
            None       => return None
        }

        loop {
            match queue.pop() {
                Some(node) => {
                    if -node.cost >= best_cost {
                        break; // solution found.
                    }

                    match *node.object {
                        BVTNode::Internal(_, ref left, ref right) => {
                            match algorithm.compute_bv_cost(left.bounding_volume()) {
                                Some(lcost) => {
                                    if lcost < best_cost {
                                        queue.push(RefWithCost::new(&**left, -lcost))
                                    }
                                },
                                None => { }
                            }

                            match algorithm.compute_bv_cost(right.bounding_volume()) {
                                Some(rcost) => {
                                    if rcost < best_cost {
                                        queue.push(RefWithCost::new(&**right, -rcost))
                                    }
                                },
                                None => { }
                            }
                        },
                        BVTNode::Leaf(_, ref b) => {
                            match algorithm.compute_b_cost(b) {
                                Some((candidate_cost, candidate_result)) => {
                                    if candidate_cost < best_cost {
                                        best_cost = candidate_cost;
                                        result    = Some((b, candidate_result));
                                    }
                                }
                                None => { }
                            }
                        }
                    }
                }
                None => break,
            }
        }

        result
    }

    fn depth(&self) -> usize {
        match *self {
            BVTNode::Internal(_, ref left, ref right) => 1 + na::max(left.depth(), right.depth()),
            BVTNode::Leaf(_, _) => 1
        }
    }
}
<commit_msg>Fix spelling<commit_after>//! A read-only Bounding Volume Tree.

use std::collections::BinaryHeap;

use alga::general::Real;
use na;
use partitioning::{BVTVisitor, BVTTVisitor, BVTCostFn};
use bounding_volume::BoundingVolume;
use utils::data::ref_with_cost::RefWithCost;
use utils;
use math::Point;

/// A Bounding Volume Tree.
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub struct BVT<B, BV> {
    tree: Option<BVTNode<B, BV>>
}

/// A node of the bounding volume tree.
#[derive(Clone, RustcEncodable, RustcDecodable)]
pub enum BVTNode<B, BV> {
    // XXX: give a faster access to the BV
    /// An internal node.
    Internal(BV, Box<BVTNode<B, BV>>, Box<BVTNode<B, BV>>),
    /// A leaf.
    Leaf(BV, B)
}

/// Result of a binary partition.
pub enum BinaryPartition<B, BV> {
    /// Result of the partitioning of one element.
    Part(B),
    /// Result of the partitioning of several elements.
    Parts(Vec<(B, BV)>, Vec<(B, BV)>)
}

impl<B, BV> BVT<B, BV> {
    // FIXME: add higher level constructors ?
    /// Builds a bounding volume tree using an user-defined construction function.
    pub fn new_with_partitioner<F: FnMut(usize, Vec<(B, BV)>) -> (BV, BinaryPartition<B, BV>)>
      (leaves:      Vec<(B, BV)>,
       partitioner: &mut F)
          -> BVT<B, BV> {
        if leaves.len() == 0 {
            BVT {
                tree: None
            }
        }
        else {
            BVT {
                tree: Some(Self::_new_with_partitioner(0, leaves, partitioner))
            }
        }
    }

    /// Traverses this tree using an object implementing the `BVTVisitor`trait.
    ///
    /// This will traverse the whole tree and call the visitor `.visit_internal(...)` (resp.
    /// `.visit_leaf(...)`) method on each internal (resp. leaf) node.
    pub fn visit<Vis: BVTVisitor<B, BV>>(&self, visitor: &mut Vis) {
        match self.tree {
            Some(ref t) => t.visit(visitor),
            None        => { }
        }
    }

    /// Visits the bounding volume traversal tree implicitely formed with `other`.
    pub fn visit_bvtt<Vis: BVTTVisitor<B, BV>>(&self, other: &BVT<B, BV>, visitor: &mut Vis) {
        match (&self.tree, &other.tree) {
            (&Some(ref ta), &Some(ref tb)) => ta.visit_bvtt(tb, visitor),
            _ => { }
        }
    }

    // FIXME: really return a ref to B ?
    /// Performs a best-fist-search on the tree.
    ///
    /// Returns the content of the leaf with the smallest associated cost, and a result of
    /// user-defined type.
    pub fn best_first_search<'a, N, BFS>(&'a self, algorithm: &mut BFS) -> Option<(&'a B, BFS::UserData)>
        where N:   Real,
              BFS: BVTCostFn<N, B, BV> {
        match self.tree {
            Some(ref t) => t.best_first_search(algorithm),
            None        => None
        }
    }

    /// Reference to the bounding volume of the tree root.
    pub fn root_bounding_volume<'r>(&'r self) -> Option<&'r BV> {
        match self.tree {
            Some(ref n) => {
                match *n {
                    BVTNode::Internal(ref bv, _, _) => Some(bv),
                    BVTNode::Leaf(ref bv, _) => Some(bv)
                }
            },
            None => None
        }
    }

    /// Computes the depth of this tree.
    pub fn depth(&self) -> usize {
        match self.tree {
            Some(ref n) => n.depth(),
            None        => 0
        }
    }
}

impl<B, BV> BVT<B, BV> {
    /// Creates a balanced `BVT`.
    pub fn new_balanced<P>(leaves: Vec<(B, BV)>) -> BVT<B, BV>
        where P:  Point,
              BV: BoundingVolume<P> + Clone {
        BVT::new_with_partitioner(leaves, &mut Self::median_partitioner)
    }

    /// Construction function for a kdree to be used with `BVT::new_with_partitioner`.
    pub fn median_partitioner_with_centers<P, F: FnMut(&B, &BV) -> P>
            (depth: usize, leaves: Vec<(B, BV)>, center: &mut F) -> (BV, BinaryPartition<B, BV>)
        where P:  Point,
              BV: BoundingVolume<P> + Clone {
        if leaves.len() == 0 {
            panic!("Cannot build a tree without leaves.");
        }
        else if leaves.len() == 1 {
            let (b, bv) = leaves.into_iter().next().unwrap();
            (bv, BinaryPartition::Part(b))
        }
        else {
            let sep_axis = depth % na::dimension::<P::Vector>();

            // compute the median along sep_axis
            let mut median = Vec::new();

            for l in leaves.iter() {
                let c = (*center)(&l.0, &l.1);
                median.push(c[sep_axis]);
            }

            let median = utils::median(&mut median[..]);

            // build the partitions
            let mut right = Vec::new();
            let mut left  = Vec::new();
            let mut bounding_bounding_volume = leaves[0].1.clone();

            let mut insert_left = false;

            for (b, bv) in leaves.into_iter() {
                bounding_bounding_volume.merge(&bv);

                let pos = (*center)(&b, &bv)[sep_axis];

                if pos < median || (pos == median && insert_left) {
                    left.push((b, bv));
                    insert_left = false;
                }
                else {
                    right.push((b, bv));
                    insert_left = true;
                }
            }

            // XXX: hack to avoid degeneracies.
            if left.len() == 0 {
                left.push(right.pop().unwrap());
            }
            else if right.len() == 0 {
                right.push(left.pop().unwrap());
            }

            (bounding_bounding_volume, BinaryPartition::Parts(left, right))
        }
    }

    /// Construction function for a kdree to be used with `BVT::new_with_partitioner`.
    pub fn median_partitioner<P>(depth: usize, leaves: Vec<(B, BV)>) -> (BV, BinaryPartition<B, BV>)
        where P:  Point,
              BV: BoundingVolume<P> + Clone {
        Self::median_partitioner_with_centers(depth, leaves, &mut |_, bv| bv.center())
    }

    fn _new_with_partitioner<F: FnMut(usize, Vec<(B, BV)>) -> (BV, BinaryPartition<B, BV>)>
                             (depth: usize, leaves: Vec<(B, BV)>, partitioner: &mut F) -> BVTNode<B, BV> {
        let (bv, partitions) = partitioner(depth, leaves);

        match partitions {
            BinaryPartition::Part(b)            => BVTNode::Leaf(bv, b),
            BinaryPartition::Parts(left, right) => {
                let left  = Self::_new_with_partitioner(depth + 1, left, partitioner);
                let right = Self::_new_with_partitioner(depth + 1, right, partitioner);
                BVTNode::Internal(bv, Box::new(left), Box::new(right))
            }
        }
    }
}

impl<B, BV> BVTNode<B, BV> {
    /// The bounding volume of this node.
    #[inline]
    pub fn bounding_volume<'a>(&'a self) -> &'a BV {
        match *self {
            BVTNode::Internal(ref bv, _, _) => bv,
            BVTNode::Leaf(ref bv, _)        => bv
        }
    }

    fn visit<Vis: BVTVisitor<B, BV>>(&self, visitor: &mut Vis) {
        match *self {
            BVTNode::Internal(ref bv, ref left, ref right) => {
                if visitor.visit_internal(bv) {
                    left.visit(visitor);
                    right.visit(visitor);
                }
            },
            BVTNode::Leaf(ref bv, ref b) => {
                visitor.visit_leaf(b, bv);
            }
        }
    }

    fn visit_bvtt<Vis: BVTTVisitor<B, BV>>(&self, other: &BVTNode<B, BV>, visitor: &mut Vis) {
        match (self, other) {
            (&BVTNode::Internal(ref bva, ref la, ref ra), &BVTNode::Internal(ref bvb, ref lb, ref rb)) => {
                if visitor.visit_internal_internal(bva, bvb) {
                    la.visit_bvtt(&**lb, visitor);
                    la.visit_bvtt(&**rb, visitor);
                    ra.visit_bvtt(&**lb, visitor);
                    ra.visit_bvtt(&**rb, visitor);
                }
            },
            (&BVTNode::Internal(ref bva, ref la, ref ra), &BVTNode::Leaf(ref bvb, ref bb)) => {
                if visitor.visit_internal_leaf(bva, bb, bvb) {
                    la.visit_bvtt(other, visitor);
                    ra.visit_bvtt(other, visitor);
                }
            },
            (&BVTNode::Leaf(ref bva, ref ba), &BVTNode::Internal(ref bvb, ref lb, ref rb)) => {
                if visitor.visit_leaf_internal(ba, bva, bvb) {
                    self.visit_bvtt(&**lb, visitor);
                    self.visit_bvtt(&**rb, visitor);
                }
            },
            (&BVTNode::Leaf(ref bva, ref ba), &BVTNode::Leaf(ref bvb, ref bb)) => {
                visitor.visit_leaf_leaf(ba, bva, bb, bvb)
            }
        }
    }

    fn best_first_search<'a, N, BFS>(&'a self, algorithm: &mut BFS) -> Option<(&'a B, BFS::UserData)>
        where N:   Real,
              BFS: BVTCostFn<N, B, BV> {
        let mut queue: BinaryHeap<RefWithCost<'a, N, BVTNode<B, BV>>> = BinaryHeap::new();
        let mut best_cost = N::max_value();
        let mut result    = None;

        match algorithm.compute_bv_cost(self.bounding_volume()) {
            Some(cost) => queue.push(RefWithCost::new(self, cost)),
            None       => return None
        }

        loop {
            match queue.pop() {
                Some(node) => {
                    if -node.cost >= best_cost {
                        break; // solution found.
                    }

                    match *node.object {
                        BVTNode::Internal(_, ref left, ref right) => {
                            match algorithm.compute_bv_cost(left.bounding_volume()) {
                                Some(lcost) => {
                                    if lcost < best_cost {
                                        queue.push(RefWithCost::new(&**left, -lcost))
                                    }
                                },
                                None => { }
                            }

                            match algorithm.compute_bv_cost(right.bounding_volume()) {
                                Some(rcost) => {
                                    if rcost < best_cost {
                                        queue.push(RefWithCost::new(&**right, -rcost))
                                    }
                                },
                                None => { }
                            }
                        },
                        BVTNode::Leaf(_, ref b) => {
                            match algorithm.compute_b_cost(b) {
                                Some((candidate_cost, candidate_result)) => {
                                    if candidate_cost < best_cost {
                                        best_cost = candidate_cost;
                                        result    = Some((b, candidate_result));
                                    }
                                }
                                None => { }
                            }
                        }
                    }
                }
                None => break,
            }
        }

        result
    }

    fn depth(&self) -> usize {
        match *self {
            BVTNode::Internal(_, ref left, ref right) => 1 + na::max(left.depth(), right.depth()),
            BVTNode::Leaf(_, _) => 1
        }
    }
}
<|endoftext|>


<commit_before>struct Person<'a> {
    name: &'static str,
    parent: Option<&'a Person<'a>>,
}

impl<'a> Person<'a> {
    fn new(name: &'static str, parent: Option<&'a Person<'a>>) -> Person<'a> {
        Person{name: name, parent: parent}
    }

    fn parents_name(&self) -> Option<&'static str> {
        match self.parent {
            Some(p) => Some(p.name),
            None => None,
        }
    }
}

fn first_five<'a>(value: &'a String) -> &'a str {
    &value[..5]
}

#[test]
fn it_works() {
    let jane = Person::new("Jane", None);
    let tom = Person::new("Tom", Some(&jane));
    let name = "Andrew".to_owned();

    assert_eq!(first_five(&name), "Andre");

    assert_eq!(tom.name, "Tom");
    assert_eq!(jane.name, "Jane");
    assert_eq!(tom.parent.unwrap().name, "Jane");
    assert_eq!(tom.parents_name(), Some("Jane"));
    assert_eq!(jane.parents_name(), None);
}

// - We are making a promise to the compiler that all of these things live for atleast the same scope.
// - We are not asking the compiler to "create a lifetime". We are just telling it to complain if we
//   don't do what we say we are doing.
// - Why? Because this allows Rust to ensure we cannot cause common memory allocation bugs. It does
//   this at compile time. It's a zero-cost abstraction because there is no runtime penalty.
// - It makes code look "uglier". But this is the wrong mindset for writing systems-level programs.
//   The emphasis here is on safety, not expressiveness.
// - Question: Why can't Rust ensure/infer/elide this scenario? Surely any reference to a Parent
//   must *always* live for the same or a bigger scope???
//   - There is an open RFC for this
//   - It may be complex to implement and/or slow down compilation
//   - Eliding in more cases may prevent newcomer Rust programmers from ever encountering
//     lifetimes, which could have a negative impact in the long run. Pedagogy!
<commit_msg>Added example function that cannot be elided<commit_after>struct Person<'a> {
    name: &'static str,
    parent: Option<&'a Person<'a>>,
}

impl<'a> Person<'a> {
    fn new(name: &'static str, parent: Option<&'a Person<'a>>) -> Person<'a> {
        Person{name: name, parent: parent}
    }

    fn parents_name(&self) -> Option<&'static str> {
        match self.parent {
            Some(p) => Some(p.name),
            None => None,
        }
    }
}

// This one can be elided.
fn first_five<'a>(value: &'a String) -> &'a str {
    &value[..5]
}

// Q: Why do I need a lifetime here?
pub fn join_iters<'a, T: Iterator>(vecs: T) -> Vec<u8>
        where T::Item: IntoIterator<Item=&'a u8> {
    vecs.flat_map(|b| b.into_iter())
        .cloned()
        .collect()
}

#[test]
fn it_works() {
    let jane = Person::new("Jane", None);
    let tom = Person::new("Tom", Some(&jane));
    let name = "Andrew".to_owned();

    let ints = vec![vec![78, 43, 1],
                    vec![1, 45, 66, 2],
                    vec![99, 8, 20]];

    assert_eq!(first_five(&name), "Andre");

    assert_eq!(tom.name, "Tom");
    assert_eq!(jane.name, "Jane");
    assert_eq!(tom.parent.unwrap().name, "Jane");
    assert_eq!(tom.parents_name(), Some("Jane"));
    assert_eq!(jane.parents_name(), None);

    assert_eq!(join_iters(ints.iter()), vec![78, 43, 1, 1, 45, 66, 2, 99, 8, 20]);
}

// - We are making a promise to the compiler that all of these things live for atleast the same scope.
// - We are not asking the compiler to "create a lifetime". We are just telling it to complain if we
//   don't do what we say we are doing.
// - Why? Because this allows Rust to ensure we cannot cause common memory allocation bugs. It does
//   this at compile time. It's a zero-cost abstraction because there is no runtime penalty.
// - It makes code look "uglier". But this is the wrong mindset for writing systems-level programs.
//   The emphasis here is on safety, not expressiveness.
// - Question: Why can't Rust ensure/infer/elide this scenario? Surely any reference to a Parent
//   must *always* live for the same or a bigger scope???
//   - There is an open RFC for this
//   - It may be complex to implement and/or slow down compilation
//   - Eliding in more cases may prevent newcomer Rust programmers from ever encountering
//     lifetimes, which could have a negative impact in the long run. Pedagogy!
<|endoftext|>


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

mod host_table;

use std::mem;

use bytes::BytesMut;

use utils;

use net::arrow::proto::codec::Encode;
use net::arrow::proto::msg::MessageBody;
use net::arrow::proto::msg::control::ControlMessageBody;
use net::arrow::proto::msg::control::SimpleServiceTable;

use scanner::ScanResult;

use svc_table::ServiceTable;

use self::host_table::HostTable;

/// SCAN_REPORT message header.
#[repr(packed)]
struct ScanReportMessageHeader {
    request_id: u16,
}

impl<'a> From<&'a ScanReportMessage> for ScanReportMessageHeader {
    fn from(message: &'a ScanReportMessage) -> ScanReportMessageHeader {
        ScanReportMessageHeader {
            request_id: message.request_id,
        }
    }
}

impl Encode for ScanReportMessageHeader {
    fn encode(&self, buf: &mut BytesMut) {
        let be_header = ScanReportMessageHeader {
            request_id: self.request_id.to_be(),
        };

        buf.extend(utils::as_bytes(&be_header))
    }
}

/// SCAN_REPORT message.
pub struct ScanReportMessage {
    request_id:  u16,
    host_table:  HostTable,
    svc_table:   SimpleServiceTable,
}

impl ScanReportMessage {
    /// Create a new SCAN_REPORT message.
    pub fn new<T>(
        request_id: u16,
        scan_result: ScanResult,
        svc_table: &T) -> ScanReportMessage
        where T: ServiceTable {
        let hosts = scan_result.hosts()
            .map(|host| host.clone());

        let host_table = HostTable::from(hosts);

        let mut services = Vec::new();

        for svc in scan_result.services() {
            // TODO: let id = svc_table.get_id(svc);
            let id = 0u16;

            services.push((id, svc.clone()));
        }

        let svc_table = SimpleServiceTable::from(services);

        ScanReportMessage {
            request_id: request_id,
            host_table: host_table,
            svc_table:  svc_table,
        }
    }
}

impl Encode for ScanReportMessage {
    fn encode(&self, buf: &mut BytesMut) {
        ScanReportMessageHeader::from(self)
            .encode(buf);

        self.host_table.encode(buf);
        self.svc_table.encode(buf);
    }
}

impl MessageBody for ScanReportMessage {
    fn len(&self) -> usize {
        mem::size_of::<ScanReportMessageHeader>()
            + self.host_table.len()
            + self.svc_table.len()
    }
}

impl ControlMessageBody for ScanReportMessage {
}
<commit_msg>Get correct service ID for all services in the scan report<commit_after>// Copyright 2017 click2stream, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

mod host_table;

use std::mem;

use bytes::BytesMut;

use utils;

use net::arrow::proto::codec::Encode;
use net::arrow::proto::msg::MessageBody;
use net::arrow::proto::msg::control::ControlMessageBody;
use net::arrow::proto::msg::control::SimpleServiceTable;

use scanner::ScanResult;

use svc_table::ServiceTable;

use self::host_table::HostTable;

/// SCAN_REPORT message header.
#[repr(packed)]
struct ScanReportMessageHeader {
    request_id: u16,
}

impl<'a> From<&'a ScanReportMessage> for ScanReportMessageHeader {
    fn from(message: &'a ScanReportMessage) -> ScanReportMessageHeader {
        ScanReportMessageHeader {
            request_id: message.request_id,
        }
    }
}

impl Encode for ScanReportMessageHeader {
    fn encode(&self, buf: &mut BytesMut) {
        let be_header = ScanReportMessageHeader {
            request_id: self.request_id.to_be(),
        };

        buf.extend(utils::as_bytes(&be_header))
    }
}

/// SCAN_REPORT message.
pub struct ScanReportMessage {
    request_id:  u16,
    host_table:  HostTable,
    svc_table:   SimpleServiceTable,
}

impl ScanReportMessage {
    /// Create a new SCAN_REPORT message.
    pub fn new<T>(
        request_id: u16,
        scan_result: ScanResult,
        svc_table: &T) -> ScanReportMessage
        where T: ServiceTable {
        let hosts = scan_result.hosts()
            .map(|host| host.clone());

        let host_table = HostTable::from(hosts);

        let mut services = Vec::new();

        for svc in scan_result.services() {
            if let Some(id) = svc_table.get_id(&svc.to_service_identifier()) {
                services.push((id, svc.clone()));
            }
        }

        let svc_table = SimpleServiceTable::from(services);

        ScanReportMessage {
            request_id: request_id,
            host_table: host_table,
            svc_table:  svc_table,
        }
    }
}

impl Encode for ScanReportMessage {
    fn encode(&self, buf: &mut BytesMut) {
        ScanReportMessageHeader::from(self)
            .encode(buf);

        self.host_table.encode(buf);
        self.svc_table.encode(buf);
    }
}

impl MessageBody for ScanReportMessage {
    fn len(&self) -> usize {
        mem::size_of::<ScanReportMessageHeader>()
            + self.host_table.len()
            + self.svc_table.len()
    }
}

impl ControlMessageBody for ScanReportMessage {
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::path::PathBuf;

use anyhow::Result;
use chrono::{Duration, Local};
use pueue_daemon_lib::state_helper::save_state;

use pueue_lib::state::{State, PUEUE_DEFAULT_GROUP};
use pueue_lib::task::{Task, TaskResult, TaskStatus};
use rstest::rstest;

use crate::fixtures::*;
use crate::helper::*;

/// A small helper function to reduce a bit of boilerplate.
fn build_task() -> Task {
    Task::new(
        "sleep 60".to_owned(),
        PathBuf::from("/tmp"),
        HashMap::new(),
        PUEUE_DEFAULT_GROUP.to_owned(),
        TaskStatus::Queued,
        Vec::new(),
        None,
    )
}

/// Initialize a daemon which already contains a predefined list of tasks in various states.
async fn daemon_with_test_state() -> Result<PueueDaemon> {
    // Get the base setup for the daemon
    let (settings, tempdir) = daemon_base_setup()?;

    // ------ Inert tasks -------
    // Build and save a state with some pre-build tasks we can use to test our querys.
    // The state is saved and created before the daemon is started.

    let mut state = State::new();

    // Failed task
    let mut failed = build_task();
    failed.id = 0;
    failed.status = TaskStatus::Done(TaskResult::Failed(255));
    failed.start = Some(Local::now() - Duration::days(1) + Duration::minutes(1));
    failed.end = Some(Local::now() - Duration::days(1) + Duration::minutes(5));
    state.tasks.insert(failed.id, failed);

    // Successful task
    let mut successful = build_task();
    successful.id = 1;
    successful.status = TaskStatus::Done(TaskResult::Success);
    successful.start = Some(Local::now() - Duration::days(2) + Duration::minutes(1));
    successful.end = Some(Local::now() - Duration::days(2) + Duration::minutes(5));
    state.tasks.insert(successful.id, successful);

    // Stashed task
    let mut successful = build_task();
    successful.status = TaskStatus::Stashed { enqueue_at: None };
    successful.id = 2;
    state.tasks.insert(successful.id, successful);

    // Scheduled task
    let mut successful = build_task();
    successful.status = TaskStatus::Stashed {
        enqueue_at: Some(Local::now() + Duration::hours(1)),
    };
    successful.id = 3;
    state.tasks.insert(successful.id, successful);

    // Save the state in our temporary directory. This makes it readable by the daemon.
    save_state(&state, &settings)?;

    // ------ Daemon setup -------
    // Start the daemon. It will restore the state we just saved.
    let daemon = daemon_with_settings(settings, tempdir).await?;
    let shared = &daemon.settings.shared;

    // ------ Live tasks -------
    // Now we have to add some tasks that need to be added live.

    // Running task
    assert_success(add_task(shared, "sleep 60", false).await?);
    wait_for_task_condition(shared, 4, |task| task.is_running()).await?;

    // 2 Queued tasks
    assert_success(add_task(shared, "sleep 60", false).await?);
    assert_success(add_task(shared, "sleep 60", false).await?);

    Ok(daemon)
}

/// This is a default `status` call without any paramaters.
/// This only exists to ensure the baseline behavior of our test state.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn default() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__default_status", output.stdout, context)?;

    Ok(())
}

/// Select only specific columns for printing
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn column_selection() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status", "columns=id,status,command"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__columns", output.stdout, context)?;

    Ok(())
}

/// Select the first few entries of the list
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn limit_first() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status", "first 4"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__first_4", output.stdout, context)?;

    Ok(())
}

/// Select the first few entries of the list
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn limit_last() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status", "last 4"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__last_4", output.stdout, context)?;

    Ok(())
}

/// Order the test state by task status.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn order_by_status() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status", "order_by status"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__order_by_status", output.stdout, context)?;

    Ok(())
}

/// Filter by start date
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn filter_start() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    // Filter tasks by their start time. This includes only task 0, which was started 1 day ago.
    let time = (Local::now() - Duration::days(1)).format("%F %T");
    let output = run_client_command(shared, &["status", &format!("start>{time}")])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__filter_start", output.stdout, context)?;

    Ok(())
}

/// Filter by end date with the current time as a time and a date.
#[rstest]
#[case("%T")]
#[case("%F")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn filter_end_with_time(#[case] format: &'static str) -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    // Filter tasks by their end time, once by day (today) and time (now).
    // This includes tasks 1 and 2, which were started 1 and 2 days ago.
    let time = Local::now().format(format);
    let output = run_client_command(shared, &["status", &format!("end<{time}")])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__filter_end", output.stdout, context)?;

    Ok(())
}

/// Filter for tasks
#[rstest]
#[case(TaskStatus::Queued, 2)]
#[case(TaskStatus::Running, 1)]
#[case(TaskStatus::Paused, 0)]
#[case(TaskStatus::Done(TaskResult::Success), 1)]
#[case(TaskStatus::Done(TaskResult::Failed(255)), 1)]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn filter_status(#[case] status: TaskStatus, #[case] expected: usize) -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    // Get the correct query keyword for the given status.
    let status_filter = match status {
        TaskStatus::Queued => "queued",
        TaskStatus::Stashed { .. } => "stashed",
        TaskStatus::Running => "running",
        TaskStatus::Paused => "paused",
        TaskStatus::Done(TaskResult::Success) => "success",
        TaskStatus::Done(TaskResult::Failed(_)) => "failed",
        _ => anyhow::bail!("Got unexpected TaskStatus in filter_status"),
    };

    let query = format!("status={status_filter}");
    let tasks = get_json_tasks_from_command(shared, &[query.as_str()])
        .await
        .expect("Failed to get json from task");

    let mut count = 0;
    for task in tasks {
        let id = task.id;
        assert_eq!(
            task.status, status,
            "Expected a different task status on task {id} based on filter {status:?}"
        );

        count += 1;
    }

    assert_eq!(
        count, expected,
        "Got a different amount of tasks than expected for the status filter {status:?}."
    );

    Ok(())
}
<commit_msg>fix: Smaller enqueue_at interval to prevent CI failures at midnight<commit_after>use std::collections::HashMap;
use std::path::PathBuf;

use anyhow::Result;
use chrono::{Duration, Local};
use pueue_daemon_lib::state_helper::save_state;

use pueue_lib::state::{State, PUEUE_DEFAULT_GROUP};
use pueue_lib::task::{Task, TaskResult, TaskStatus};
use rstest::rstest;

use crate::fixtures::*;
use crate::helper::*;

/// A small helper function to reduce a bit of boilerplate.
fn build_task() -> Task {
    Task::new(
        "sleep 60".to_owned(),
        PathBuf::from("/tmp"),
        HashMap::new(),
        PUEUE_DEFAULT_GROUP.to_owned(),
        TaskStatus::Queued,
        Vec::new(),
        None,
    )
}

/// Initialize a daemon which already contains a predefined list of tasks in various states.
async fn daemon_with_test_state() -> Result<PueueDaemon> {
    // Get the base setup for the daemon
    let (settings, tempdir) = daemon_base_setup()?;

    // ------ Inert tasks -------
    // Build and save a state with some pre-build tasks we can use to test our querys.
    // The state is saved and created before the daemon is started.

    let mut state = State::new();

    // Failed task
    let mut failed = build_task();
    failed.id = 0;
    failed.status = TaskStatus::Done(TaskResult::Failed(255));
    failed.start = Some(Local::now() - Duration::days(1) + Duration::minutes(1));
    failed.end = Some(Local::now() - Duration::days(1) + Duration::minutes(5));
    state.tasks.insert(failed.id, failed);

    // Successful task
    let mut successful = build_task();
    successful.id = 1;
    successful.status = TaskStatus::Done(TaskResult::Success);
    successful.start = Some(Local::now() - Duration::days(2) + Duration::minutes(1));
    successful.end = Some(Local::now() - Duration::days(2) + Duration::minutes(5));
    state.tasks.insert(successful.id, successful);

    // Stashed task
    let mut successful = build_task();
    successful.status = TaskStatus::Stashed { enqueue_at: None };
    successful.id = 2;
    state.tasks.insert(successful.id, successful);

    // Scheduled task
    let mut successful = build_task();
    successful.status = TaskStatus::Stashed {
        enqueue_at: Some(Local::now() + Duration::minutes(2)),
    };
    successful.id = 3;
    state.tasks.insert(successful.id, successful);

    // Save the state in our temporary directory. This makes it readable by the daemon.
    save_state(&state, &settings)?;

    // ------ Daemon setup -------
    // Start the daemon. It will restore the state we just saved.
    let daemon = daemon_with_settings(settings, tempdir).await?;
    let shared = &daemon.settings.shared;

    // ------ Live tasks -------
    // Now we have to add some tasks that need to be added live.

    // Running task
    assert_success(add_task(shared, "sleep 60", false).await?);
    wait_for_task_condition(shared, 4, |task| task.is_running()).await?;

    // 2 Queued tasks
    assert_success(add_task(shared, "sleep 60", false).await?);
    assert_success(add_task(shared, "sleep 60", false).await?);

    Ok(daemon)
}

/// This is a default `status` call without any paramaters.
/// This only exists to ensure the baseline behavior of our test state.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn default() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__default_status", output.stdout, context)?;

    Ok(())
}

/// Select only specific columns for printing
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn column_selection() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status", "columns=id,status,command"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__columns", output.stdout, context)?;

    Ok(())
}

/// Select the first few entries of the list
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn limit_first() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status", "first 4"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__first_4", output.stdout, context)?;

    Ok(())
}

/// Select the first few entries of the list
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn limit_last() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status", "last 4"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__last_4", output.stdout, context)?;

    Ok(())
}

/// Order the test state by task status.
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn order_by_status() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    let output = run_client_command(shared, &["status", "order_by status"])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__order_by_status", output.stdout, context)?;

    Ok(())
}

/// Filter by start date
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn filter_start() -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    // Filter tasks by their start time. This includes only task 0, which was started 1 day ago.
    let time = (Local::now() - Duration::days(1)).format("%F %T");
    let output = run_client_command(shared, &["status", &format!("start>{time}")])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__filter_start", output.stdout, context)?;

    Ok(())
}

/// Filter by end date with the current time as a time and a date.
#[rstest]
#[case("%T")]
#[case("%F")]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn filter_end_with_time(#[case] format: &'static str) -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    // Filter tasks by their end time, once by day (today) and time (now).
    // This includes tasks 1 and 2, which were started 1 and 2 days ago.
    let time = Local::now().format(format);
    let output = run_client_command(shared, &["status", &format!("end<{time}")])?;

    let context = get_task_context(&daemon.settings).await?;
    assert_stdout_matches("query__filter_end", output.stdout, context)?;

    Ok(())
}

/// Filter for tasks
#[rstest]
#[case(TaskStatus::Queued, 2)]
#[case(TaskStatus::Running, 1)]
#[case(TaskStatus::Paused, 0)]
#[case(TaskStatus::Done(TaskResult::Success), 1)]
#[case(TaskStatus::Done(TaskResult::Failed(255)), 1)]
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn filter_status(#[case] status: TaskStatus, #[case] expected: usize) -> Result<()> {
    let daemon = daemon_with_test_state().await?;
    let shared = &daemon.settings.shared;

    // Get the correct query keyword for the given status.
    let status_filter = match status {
        TaskStatus::Queued => "queued",
        TaskStatus::Stashed { .. } => "stashed",
        TaskStatus::Running => "running",
        TaskStatus::Paused => "paused",
        TaskStatus::Done(TaskResult::Success) => "success",
        TaskStatus::Done(TaskResult::Failed(_)) => "failed",
        _ => anyhow::bail!("Got unexpected TaskStatus in filter_status"),
    };

    let query = format!("status={status_filter}");
    let tasks = get_json_tasks_from_command(shared, &[query.as_str()])
        .await
        .expect("Failed to get json from task");

    let mut count = 0;
    for task in tasks {
        let id = task.id;
        assert_eq!(
            task.status, status,
            "Expected a different task status on task {id} based on filter {status:?}"
        );

        count += 1;
    }

    assert_eq!(
        count, expected,
        "Got a different amount of tasks than expected for the status filter {status:?}."
    );

    Ok(())
}
<|endoftext|>


<commit_before>extern crate serde_json;
extern crate serde;
#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate slog;
extern crate slog_scope;
extern crate slog_html;
extern crate slog_journald;
extern crate slog_stdlog;
extern crate slog_stream;
extern crate slog_term;

extern crate wlc;
#[macro_use]
extern crate fireplace_lib;

use fireplace_lib::callback::IntoCallback;
use fireplace_lib::handlers::*;
use fireplace_lib::handlers::keyboard::KeyHandler;

use std::env;
use std::fs::{self, OpenOptions};
use std::io::Read;
use std::path::PathBuf;
use std::process::Command;

use wlc::{Callback, View};

mod logger;
mod config;
pub use self::config::Config;

fn try_config_locations(paths: &[PathBuf]) -> Config {
    for path in paths {
        if path.exists() {
            return serde_json::from_reader(OpenOptions::new()
                    .read(true)
                    .open(path)
                    .unwrap())
                .expect("Malformed config file");
        }
    }
    serde_json::from_str("{}").unwrap()
}

fn main() {

    // Parse configuration
    let mut locations = Vec::new();
    if let Ok(xdg_dir) = env::var("XDG_CONFIG_DIR") {
        locations.push(PathBuf::from(&xdg_dir).join("fireplace").join(".fireplace.json"));
        locations.push(PathBuf::from(&xdg_dir).join("fireplace").join("fireplace.json"));
        locations.push(PathBuf::from(&xdg_dir).join(".fireplace.json"));
        locations.push(PathBuf::from(&xdg_dir).join("fireplace.json"));
    }
    if let Some(home_dir) = env::home_dir() {
        locations.push(PathBuf::from(&home_dir).join(".config").join("fireplace").join(".fireplace.json"));
        locations.push(PathBuf::from(&home_dir).join(".config").join("fireplace").join("fireplace.json"));
        locations.push(PathBuf::from(&home_dir).join(".config").join(".fireplace.json"));
        locations.push(PathBuf::from(&home_dir).join(".config").join("fireplace.json"));
        locations.push(PathBuf::from(&home_dir).join(".fireplace.json"));
    }
    locations.push(PathBuf::from("/etc/fireplace/fireplace.json"));
    locations.push(PathBuf::from("/etc/fireplace.json"));
    let mut config = try_config_locations(&locations);

    // Initialize logger
    logger::init(config.logging);

    // Initialize the key combinations

    let mut keyboard_handler = KeyboardHandler::new();

    for (command, pattern) in config.keys.drain() {
        keyboard_handler.register(pattern, {
            struct GlobalKeyHandler {
                command: String,
            }
            impl KeyHandler for GlobalKeyHandler {
                fn handle_key(&mut self, _time: u32, _view: Option<&View>) {
                    match &*self.command {
                        #[cfg(feature = "conrod_ui")]
                        "terminate" => wlc::terminate(),
                        x => {
                            warn!(slog_scope::logger(),
                                  "Unknown command {}. Ignoring KeyBinding",
                                  x)
                        }
                    };
                }
            }

            GlobalKeyHandler { command: command }
        });
    }

    for (command, pattern) in config.view.keys.drain() {
        keyboard_handler.register(pattern, {
            struct ViewKeyHandler {
                command: String,
            }
            impl KeyHandler for ViewKeyHandler {
                fn handle_key(&mut self, _time: u32, view: Option<&View>) {
                    if let Some(view) = view {
                        match &*self.command {
                            "close" => view.close(),
                            x => {
                                warn!(slog_scope::logger(),
                                      "Unknown command {}. Ignoring KeyBinding",
                                      x)
                            }
                        };
                    }
                }
            }

            ViewKeyHandler { command: command }
        });
    }

    for (command, pattern) in config.exec.keys.drain() {
        keyboard_handler.register(pattern, {
            struct ExecKeyHandler {
                command: String,
            }
            impl KeyHandler for ExecKeyHandler {
                fn handle_key(&mut self, _time: u32, _view: Option<&View>) {
                    use std::process;

                    match Command::new(env::var("SHELL").unwrap_or_else(|_| String::from("sh")))
                        .arg("-c")
                        .arg(self.command.clone())
                        .stdin(process::Stdio::null())
                        .stdout(process::Stdio::null())
                        .stderr(process::Stdio::null())
                        .spawn() {
                        Ok(_) => {}
                        Err(x) => {
                            warn!(slog_scope::logger(),
                                  "Command {} failed with {}",
                                  self.command,
                                  x)
                        }
                    };
                }
            }

            ExecKeyHandler { command: command }
        });
    }

    // load the other handlers are start the compositor

    let mut handlers: Vec<Box<Callback>> = Vec::new();

    handlers.push(Box::new(StoreHandler::new().into_callback()));
    handlers.push(Box::new(OutputConfigHandler::new(config.outputs)));
    handlers.push(Box::new(geometry::GeometryHandler::new().into_callback()));
    handlers.push(Box::new(geometry::GapsHandler::new().into_callback()));

    #[cfg(feature = "conrod_ui")]
    {
        handlers.push(Box::new(render::conrod::ConrodHandler::new().into_callback()));

        handlers.push(Box::new(render::conrod::provider::background::BackgroundHandler::new()
            .into_callback()));
        handlers.push(Box::new(render::conrod::provider::statusbar::StatusbarHandler::new().into_callback()));

        handlers.push(Box::new(render::ScreenshotHandler::new(config.screenshot)));
    }

    handlers.push(Box::new(workspaces::WorkspaceHandler::new(config.workspace).into_callback()));
    handlers.push(Box::new(FocusHandler::new(config.focus).into_callback()));
    handlers.push(Box::new(PointerHandler::new()));
    handlers.push(Box::new(keyboard_handler.into_callback()));

    wlc::init(handlers.into_callback()).unwrap();
}
<commit_msg>Fix warning in json flavor<commit_after>extern crate serde_json;
extern crate serde;
#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate slog;
extern crate slog_scope;
extern crate slog_html;
extern crate slog_journald;
extern crate slog_stdlog;
extern crate slog_stream;
extern crate slog_term;

extern crate wlc;
#[macro_use]
extern crate fireplace_lib;

use fireplace_lib::callback::IntoCallback;
use fireplace_lib::handlers::*;
use fireplace_lib::handlers::keyboard::KeyHandler;

use std::env;
use std::fs::OpenOptions;
use std::path::PathBuf;
use std::process::Command;

use wlc::{Callback, View};

mod logger;
mod config;
pub use self::config::Config;

fn try_config_locations(paths: &[PathBuf]) -> Config {
    for path in paths {
        if path.exists() {
            return serde_json::from_reader(OpenOptions::new()
                    .read(true)
                    .open(path)
                    .unwrap())
                .expect("Malformed config file");
        }
    }
    serde_json::from_str("{}").unwrap()
}

fn main() {

    // Parse configuration
    let mut locations = Vec::new();
    if let Ok(xdg_dir) = env::var("XDG_CONFIG_DIR") {
        locations.push(PathBuf::from(&xdg_dir).join("fireplace").join(".fireplace.json"));
        locations.push(PathBuf::from(&xdg_dir).join("fireplace").join("fireplace.json"));
        locations.push(PathBuf::from(&xdg_dir).join(".fireplace.json"));
        locations.push(PathBuf::from(&xdg_dir).join("fireplace.json"));
    }
    if let Some(home_dir) = env::home_dir() {
        locations.push(PathBuf::from(&home_dir).join(".config").join("fireplace").join(".fireplace.json"));
        locations.push(PathBuf::from(&home_dir).join(".config").join("fireplace").join("fireplace.json"));
        locations.push(PathBuf::from(&home_dir).join(".config").join(".fireplace.json"));
        locations.push(PathBuf::from(&home_dir).join(".config").join("fireplace.json"));
        locations.push(PathBuf::from(&home_dir).join(".fireplace.json"));
    }
    locations.push(PathBuf::from("/etc/fireplace/fireplace.json"));
    locations.push(PathBuf::from("/etc/fireplace.json"));
    let mut config = try_config_locations(&locations);

    // Initialize logger
    logger::init(config.logging);

    // Initialize the key combinations

    let mut keyboard_handler = KeyboardHandler::new();

    for (command, pattern) in config.keys.drain() {
        keyboard_handler.register(pattern, {
            struct GlobalKeyHandler {
                command: String,
            }
            impl KeyHandler for GlobalKeyHandler {
                fn handle_key(&mut self, _time: u32, _view: Option<&View>) {
                    match &*self.command {
                        #[cfg(feature = "conrod_ui")]
                        "terminate" => wlc::terminate(),
                        x => {
                            warn!(slog_scope::logger(),
                                  "Unknown command {}. Ignoring KeyBinding",
                                  x)
                        }
                    };
                }
            }

            GlobalKeyHandler { command: command }
        });
    }

    for (command, pattern) in config.view.keys.drain() {
        keyboard_handler.register(pattern, {
            struct ViewKeyHandler {
                command: String,
            }
            impl KeyHandler for ViewKeyHandler {
                fn handle_key(&mut self, _time: u32, view: Option<&View>) {
                    if let Some(view) = view {
                        match &*self.command {
                            "close" => view.close(),
                            x => {
                                warn!(slog_scope::logger(),
                                      "Unknown command {}. Ignoring KeyBinding",
                                      x)
                            }
                        };
                    }
                }
            }

            ViewKeyHandler { command: command }
        });
    }

    for (command, pattern) in config.exec.keys.drain() {
        keyboard_handler.register(pattern, {
            struct ExecKeyHandler {
                command: String,
            }
            impl KeyHandler for ExecKeyHandler {
                fn handle_key(&mut self, _time: u32, _view: Option<&View>) {
                    use std::process;

                    match Command::new(env::var("SHELL").unwrap_or_else(|_| String::from("sh")))
                        .arg("-c")
                        .arg(self.command.clone())
                        .stdin(process::Stdio::null())
                        .stdout(process::Stdio::null())
                        .stderr(process::Stdio::null())
                        .spawn() {
                        Ok(_) => {}
                        Err(x) => {
                            warn!(slog_scope::logger(),
                                  "Command {} failed with {}",
                                  self.command,
                                  x)
                        }
                    };
                }
            }

            ExecKeyHandler { command: command }
        });
    }

    // load the other handlers are start the compositor

    let mut handlers: Vec<Box<Callback>> = Vec::new();

    handlers.push(Box::new(StoreHandler::new().into_callback()));
    handlers.push(Box::new(OutputConfigHandler::new(config.outputs)));
    handlers.push(Box::new(geometry::GeometryHandler::new().into_callback()));
    handlers.push(Box::new(geometry::GapsHandler::new().into_callback()));

    #[cfg(feature = "conrod_ui")]
    {
        handlers.push(Box::new(render::conrod::ConrodHandler::new().into_callback()));

        handlers.push(Box::new(render::conrod::provider::background::BackgroundHandler::new()
            .into_callback()));
        handlers.push(Box::new(render::conrod::provider::statusbar::StatusbarHandler::new().into_callback()));

        handlers.push(Box::new(render::ScreenshotHandler::new(config.screenshot)));
    }

    handlers.push(Box::new(workspaces::WorkspaceHandler::new(config.workspace).into_callback()));
    handlers.push(Box::new(FocusHandler::new(config.focus).into_callback()));
    handlers.push(Box::new(PointerHandler::new()));
    handlers.push(Box::new(keyboard_handler.into_callback()));

    wlc::init(handlers.into_callback()).unwrap();
}
<|endoftext|>