<commit_before>#![allow(clippy::blacklisted_name)]

#[cfg(feature = "serde-json")]
#[macro_use]
extern crate serde_json;

use askama::Template;
#[cfg(feature = "serde-json")]
use serde_json::Value;

#[derive(Template)]
#[template(path = "filters.html")]
struct TestTemplate {
    strvar: String,
}

#[test]
fn filter_escape() {
    let s = TestTemplate {
        strvar: "// my  is \"unsafe\" & should be 'escaped'".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "// my <html> is "unsafe" & \
         should be 'escaped'"
    );
}

#[derive(Template)]
#[template(path = "format.html", escape = "none")]
struct FormatTemplate<'a> {
    var: &'a str,
}

#[test]
fn filter_format() {
    let t = FormatTemplate { var: "formatted" };
    assert_eq!(t.render().unwrap(), "\"formatted\"");
}

#[derive(Template)]
#[template(source = "{{ var|fmt(\"{:?}\") }}", ext = "html", escape = "none")]
struct FmtTemplate<'a> {
    var: &'a str,
}

#[test]
fn filter_fmt() {
    let t = FmtTemplate { var: "formatted" };
    assert_eq!(t.render().unwrap(), "\"formatted\"");
}

#[derive(Template)]
#[template(
    source = "{{ 1|into_f64 }} {{ 1.9|into_isize }}",
    ext = "txt",
    escape = "none"
)]
struct IntoNumbersTemplate;

#[test]
fn into_numbers_fmt() {
    let t = IntoNumbersTemplate;
    assert_eq!(t.render().unwrap(), "1 1");
}

#[derive(Template)]
#[template(source = "{{ s|myfilter }}", ext = "txt")]
struct MyFilterTemplate<'a> {
    s: &'a str,
}

mod filters {
    pub fn myfilter(s: &str) -> ::askama::Result<String> {
        Ok(s.replace("oo", "aa"))
    }
    // for test_nested_filter_ref
    pub fn mytrim(s: &dyn (::std::fmt::Display)) -> ::askama::Result<String> {
        let s = format!("{}", s);
        Ok(s.trim().to_owned())
    }
}

#[test]
fn test_my_filter() {
    let t = MyFilterTemplate { s: "foo" };
    assert_eq!(t.render().unwrap(), "faa");
}

#[derive(Template)]
#[template(path = "filters_join.html")]
struct JoinTemplate<'a> {
    s: &'a [&'a str],
}

#[test]
fn test_join() {
    let t = JoinTemplate {
        s: &["foo", "bar", "bazz"],
    };
    assert_eq!(t.render().unwrap(), "foo, bar, bazz");
}

#[derive(Template)]
#[template(path = "filters_join.html")]
struct VecJoinTemplate {
    s: Vec<String>,
}

#[test]
fn test_vec_join() {
    let t = VecJoinTemplate {
        s: vec!["foo".into(), "bar".into(), "bazz".into()],
    };
    assert_eq!(t.render().unwrap(), "foo, bar, bazz");
}

#[cfg(feature = "serde-json")]
#[derive(Template)]
#[template(path = "json.html")]
struct JsonTemplate<'a> {
    foo: &'a str,
    bar: &'a Value,
}

#[cfg(feature = "serde-json")]
#[test]
fn test_json() {
    let val = json!({"arr": [ "one", 2, true, null ]});
    let t = JsonTemplate {
        foo: "a",
        bar: &val,
    };
    // Note: the json filter lacks a way to specify initial indentation
    assert_eq!(
        t.render().unwrap(),
        r#"{
  "foo": "a",
  "bar": {
  "arr": [
    "one",
    2,
    true,
    null
  ]
}
}"#
    );
}

#[derive(Template)]
#[template(source = "{{ x|mytrim|safe }}", ext = "html")]
struct NestedFilterTemplate {
    x: String,
}

#[test]
fn test_nested_filter_ref() {
    let t = NestedFilterTemplate {
        x: " floo & bar".to_string(),
    };
    assert_eq!(t.render().unwrap(), "floo & bar");
}

#[derive(Template)]
#[template(
    source = "{% let p = baz.print(foo.as_ref()) %}{{ p|upper }}",
    ext = "html"
)]
struct FilterLetFilterTemplate {
    foo: String,
    baz: Baz,
}

struct Baz {}

impl Baz {
    fn print(&self, s: &str) -> String {
        s.trim().to_owned()
    }
}

#[test]
fn test_filter_let_filter() {
    let t = FilterLetFilterTemplate {
        foo: " bar ".to_owned(),
        baz: Baz {},
    };
    assert_eq!(t.render().unwrap(), "BAR");
}

#[derive(Template)]
#[template(source = "{{ foo|truncate(10) }}{{ foo|truncate(5) }}", ext = "txt")]
struct TruncateFilter {
    foo: String,
}

#[test]
fn test_filter_truncate() {
    let t = TruncateFilter {
        foo: "alpha bar".into(),
    };
    assert_eq!(t.render().unwrap(), "alpha baralpha...");
}
<commit_msg>Added optional escaper tests<commit_after>#![allow(clippy::blacklisted_name)]

#[cfg(feature = "serde-json")]
#[macro_use]
extern crate serde_json;

use askama::Template;
#[cfg(feature = "serde-json")]
use serde_json::Value;

#[derive(Template)]
#[template(path = "filters.html")]
struct TestTemplate {
    strvar: String,
}

#[test]
fn filter_escape() {
    let s = TestTemplate {
        strvar: "// my  is \"unsafe\" & should be 'escaped'".to_string(),
    };
    assert_eq!(
        s.render().unwrap(),
        "// my <html> is "unsafe" & \
         should be 'escaped'"
    );
}

#[derive(Template)]
#[template(
    source = "{{ \"
Foo Bar
\"|escape(\"none\") }}
{{ \"
Foo Bar
\"|escape(\"html\") }}
{{ \"
Foo Bar
\"|escape }}
{{ \"
Foo Bar
\" }}
",
    ext = "txt",
    escape = "none"
)]
struct OptEscaperNoneTemplate;

#[test]
fn filter_opt_escaper_none() {
    let t = OptEscaperNoneTemplate;
    assert_eq!(
        t.render().unwrap(),
        r#"
Foo Bar

<h1 class="title">Foo Bar</h1>

Foo Bar

Foo Bar
"#
    );
}

#[derive(Template)]
#[template(
    source = "{{ \"
Foo Bar
\"|escape(\"none\") }}
{{ \"
Foo Bar
\"|escape(\"html\") }}
{{ \"
Foo Bar
\"|escape }}
{{ \"
Foo Bar
\" }}
",
    ext = "txt",
    escape = "html"
)]
struct OptEscaperHtmlTemplate;

#[test]
fn filter_opt_escaper_html() {
    let t = OptEscaperHtmlTemplate;
    assert_eq!(
        t.render().unwrap(),
        r#"
Foo Bar

<h1 class="title">Foo Bar</h1>
<h1 class="title">Foo Bar</h1>
<h1 class="title">Foo Bar</h1>"#
    );
}

#[derive(Template)]
#[template(path = "format.html", escape = "none")]
struct FormatTemplate<'a> {
    var: &'a str,
}

#[test]
fn filter_format() {
    let t = FormatTemplate { var: "formatted" };
    assert_eq!(t.render().unwrap(), "\"formatted\"");
}

#[derive(Template)]
#[template(source = "{{ var|fmt(\"{:?}\") }}", ext = "html", escape = "none")]
struct FmtTemplate<'a> {
    var: &'a str,
}

#[test]
fn filter_fmt() {
    let t = FmtTemplate { var: "formatted" };
    assert_eq!(t.render().unwrap(), "\"formatted\"");
}

#[derive(Template)]
#[template(
    source = "{{ 1|into_f64 }} {{ 1.9|into_isize }}",
    ext = "txt",
    escape = "none"
)]
struct IntoNumbersTemplate;

#[test]
fn into_numbers_fmt() {
    let t = IntoNumbersTemplate;
    assert_eq!(t.render().unwrap(), "1 1");
}

#[derive(Template)]
#[template(source = "{{ s|myfilter }}", ext = "txt")]
struct MyFilterTemplate<'a> {
    s: &'a str,
}

mod filters {
    pub fn myfilter(s: &str) -> ::askama::Result<String> {
        Ok(s.replace("oo", "aa"))
    }
    // for test_nested_filter_ref
    pub fn mytrim(s: &dyn (::std::fmt::Display)) -> ::askama::Result<String> {
        let s = format!("{}", s);
        Ok(s.trim().to_owned())
    }
}

#[test]
fn test_my_filter() {
    let t = MyFilterTemplate { s: "foo" };
    assert_eq!(t.render().unwrap(), "faa");
}

#[derive(Template)]
#[template(path = "filters_join.html")]
struct JoinTemplate<'a> {
    s: &'a [&'a str],
}

#[test]
fn test_join() {
    let t = JoinTemplate {
        s: &["foo", "bar", "bazz"],
    };
    assert_eq!(t.render().unwrap(), "foo, bar, bazz");
}

#[derive(Template)]
#[template(path = "filters_join.html")]
struct VecJoinTemplate {
    s: Vec<String>,
}

#[test]
fn test_vec_join() {
    let t = VecJoinTemplate {
        s: vec!["foo".into(), "bar".into(), "bazz".into()],
    };
    assert_eq!(t.render().unwrap(), "foo, bar, bazz");
}

#[cfg(feature = "serde-json")]
#[derive(Template)]
#[template(path = "json.html")]
struct JsonTemplate<'a> {
    foo: &'a str,
    bar: &'a Value,
}

#[cfg(feature = "serde-json")]
#[test]
fn test_json() {
    let val = json!({"arr": [ "one", 2, true, null ]});
    let t = JsonTemplate {
        foo: "a",
        bar: &val,
    };
    // Note: the json filter lacks a way to specify initial indentation
    assert_eq!(
        t.render().unwrap(),
        r#"{
  "foo": "a",
  "bar": {
  "arr": [
    "one",
    2,
    true,
    null
  ]
}
}"#
    );
}

#[derive(Template)]
#[template(source = "{{ x|mytrim|safe }}", ext = "html")]
struct NestedFilterTemplate {
    x: String,
}

#[test]
fn test_nested_filter_ref() {
    let t = NestedFilterTemplate {
        x: " floo & bar".to_string(),
    };
    assert_eq!(t.render().unwrap(), "floo & bar");
}

#[derive(Template)]
#[template(
    source = "{% let p = baz.print(foo.as_ref()) %}{{ p|upper }}",
    ext = "html"
)]
struct FilterLetFilterTemplate {
    foo: String,
    baz: Baz,
}

struct Baz {}

impl Baz {
    fn print(&self, s: &str) -> String {
        s.trim().to_owned()
    }
}

#[test]
fn test_filter_let_filter() {
    let t = FilterLetFilterTemplate {
        foo: " bar ".to_owned(),
        baz: Baz {},
    };
    assert_eq!(t.render().unwrap(), "BAR");
}

#[derive(Template)]
#[template(source = "{{ foo|truncate(10) }}{{ foo|truncate(5) }}", ext = "txt")]
struct TruncateFilter {
    foo: String,
}

#[test]
fn test_filter_truncate() {
    let t = TruncateFilter {
        foo: "alpha bar".into(),
    };
    assert_eq!(t.render().unwrap(), "alpha baralpha...");
}
<|endoftext|>


<commit_before><commit_msg>Bug 1771294 - Skip drawing backdrop-filters with broken transforms r=gfx-reviewers,lsalzman<commit_after><|endoftext|>


<commit_before>//! A general purpose `Batcher` implementation based on radix sort.

use timely::progress::frontier::Antichain;

use ::Diff;

use lattice::Lattice;
use trace::{Batch, Batcher, Builder};

/// Creates batches from unordered tuples.
pub struct MergeBatcher<K: Ord, V: Ord, T: Ord, R: Diff, B: Batch<K, V, T, R>> {
    sorter: MergeSorter<(K, V), T, R>,
    lower: Vec<T>,
    frontier: Antichain<T>,
    phantom: ::std::marker::PhantomData<B>,
}

impl<K, V, T, R, B> Batcher<K, V, T, R, B> for MergeBatcher<K, V, T, R, B>
where
    K: Ord+Clone,
    V: Ord+Clone,
    T: Lattice+Ord+Clone,
    R: Diff,
    B: Batch<K, V, T, R>,
{
    fn new() -> Self {
        MergeBatcher {
            sorter: MergeSorter::new(),
            frontier: Antichain::new(),
            lower: vec![T::minimum()],
            phantom: ::std::marker::PhantomData,
        }
    }

    #[inline(never)]
    fn push_batch(&mut self, batch: &mut Vec<((K,V),T,R)>) {
        self.sorter.push(batch);
    }

    // Sealing a batch means finding those updates with times not greater or equal to any time
    // in `upper`. All updates must have time greater or equal to the previously used `upper`,
    // which we call `lower`, by assumption that after sealing a batcher we receive no more
    // updates with times not greater or equal to `upper`.
    #[inline(never)]
    fn seal(&mut self, upper: &[T]) -> B {

        let mut builder = B::Builder::new();

        let mut merged = Vec::new();
        self.sorter.finish_into(&mut merged);

        let mut kept = Vec::new();
        let mut keep = Vec::with_capacity(1024);

        self.frontier.clear();

        for mut buffer in merged.drain(..) {
            for ((key, val), time, diff) in buffer.drain(..) {
                if upper.iter().any(|t| t.less_equal(&time)) {
                    // keep_count += 1;
                    self.frontier.insert(time.clone());
                    keep.push(((key, val), time, diff));
                    if keep.len() == keep.capacity() {
                        kept.push(keep);
                        keep = self.sorter.empty();
                    }
                }
                else {
                    // seal_count += 1;
                    builder.push((key, val, time, diff));
                }
            }
        }
        if keep.len() > 0 {
            kept.push(keep);
        }
        if kept.len() > 0 {
            self.sorter.push_list(kept);
        }

        let seal = builder.done(&self.lower[..], &upper[..], &self.lower[..]);
        self.lower = upper.to_vec();
        seal
    }

    // the frontier of elements remaining after the most recent call to `self.seal`.
    fn frontier(&mut self) -> &[T] {
        self.frontier.elements()
    }
}

use std::slice::{from_raw_parts};

pub struct VecQueue<T> {
    list: Vec<T>,
    head: usize,
    tail: usize,
}

impl<T> VecQueue<T> {
    #[inline(always)]
    pub fn new() -> Self { VecQueue::from(Vec::new()) }
    #[inline(always)]
    pub fn pop(&mut self) -> T {
        debug_assert!(self.head < self.tail);
        self.head += 1;
        unsafe { ::std::ptr::read(self.list.as_mut_ptr().offset(((self.head as isize) - 1) )) }
    }
    #[inline(always)]
    pub fn peek(&self) -> &T {
        debug_assert!(self.head < self.tail);
        unsafe { self.list.get_unchecked(self.head) }
    }
    #[inline(always)]
    pub fn _peek_tail(&self) -> &T {
        debug_assert!(self.head < self.tail);
        unsafe { self.list.get_unchecked(self.tail-1) }
    }
    #[inline(always)]
    pub fn _slice(&self) -> &[T] {
        debug_assert!(self.head < self.tail);
        unsafe { from_raw_parts(self.list.get_unchecked(self.head), self.tail - self.head) }
    }
    #[inline(always)]
    pub fn from(mut list: Vec<T>) -> Self {
        let tail = list.len();
        unsafe { list.set_len(0); }
        VecQueue {
            list: list,
            head: 0,
            tail: tail,
        }
    }
    // could leak, if self.head != self.tail.
    #[inline(always)]
    pub fn done(self) -> Vec<T> {
        debug_assert!(self.head == self.tail);
        self.list
    }
    #[inline(always)]
    pub fn len(&self) -> usize { self.tail - self.head }
    #[inline(always)]
    pub fn is_empty(&self) -> bool { self.head == self.tail }
}

#[inline(always)]
unsafe fn push_unchecked<T>(vec: &mut Vec<T>, element: T) {
    debug_assert!(vec.len() < vec.capacity());
    let len = vec.len();
    ::std::ptr::write(vec.get_unchecked_mut(len), element);
    vec.set_len(len + 1);
}

pub struct MergeSorter<D: Ord, T: Ord, R: Diff> {
    queue: Vec<Vec<Vec<(D, T, R)>>>,    // each power-of-two length list of allocations.
    stash: Vec<Vec<(D, T, R)>>,
}

impl<D: Ord, T: Ord, R: Diff> MergeSorter<D, T, R> {

    #[inline]
    pub fn new() -> Self { MergeSorter { queue: Vec::new(), stash: Vec::new() } }

    #[inline]
    pub fn empty(&mut self) -> Vec<(D, T, R)> {
        self.stash.pop().unwrap_or_else(|| Vec::with_capacity(1024))
    }

    #[inline(never)]
    pub fn _sort(&mut self, list: &mut Vec<Vec<(D, T, R)>>) {
        for mut batch in list.drain(..) {
            self.push(&mut batch);
        }
        self.finish_into(list);
    }

    #[inline]
    pub fn push(&mut self, batch: &mut Vec<(D, T, R)>) {

        let mut batch = if self.stash.len() > 2 {
            ::std::mem::replace(batch, self.stash.pop().unwrap())
        }
        else {
            ::std::mem::replace(batch, Vec::new())
        };

        if batch.len() > 0 {
            batch.sort_unstable_by(|x,y| (&x.0, &x.1).cmp(&(&y.0, &y.1)));
            for index in 1 .. batch.len() {
                if batch[index].0 == batch[index - 1].0 && batch[index].1 == batch[index - 1].1 {
                    batch[index].2 = batch[index].2 + batch[index - 1].2;
                    batch[index - 1].2 = R::zero();
                }
            }
            batch.retain(|x| !x.2.is_zero());

            self.queue.push(vec![batch]);
            while self.queue.len() > 1 && (self.queue[self.queue.len()-1].len() >= self.queue[self.queue.len()-2].len() / 2) {
                let list1 = self.queue.pop().unwrap();
                let list2 = self.queue.pop().unwrap();
                let merged = self.merge_by(list1, list2);
                self.queue.push(merged);
            }
        }
    }

    // This is awkward, because it isn't a power-of-two length any more, and we don't want
    // to break it down to be so.
    pub fn push_list(&mut self, list: Vec<Vec<(D, T, R)>>) {
        while self.queue.len() > 1 && self.queue[self.queue.len()-1].len() < list.len() {
            let list1 = self.queue.pop().unwrap();
            let list2 = self.queue.pop().unwrap();
            let merged = self.merge_by(list1, list2);
            self.queue.push(merged);
        }
        self.queue.push(list);
    }

    #[inline(never)]
    pub fn finish_into(&mut self, target: &mut Vec<Vec<(D, T, R)>>) {
        while self.queue.len() > 1 {
            let list1 = self.queue.pop().unwrap();
            let list2 = self.queue.pop().unwrap();
            let merged = self.merge_by(list1, list2);
            self.queue.push(merged);
        }

        if let Some(mut last) = self.queue.pop() {
            ::std::mem::swap(&mut last, target);
        }
    }

    // merges two sorted input lists into one sorted output list.
    #[inline(never)]
    fn merge_by(&mut self, list1: Vec<Vec<(D, T, R)>>, list2: Vec<Vec<(D, T, R)>>) -> Vec<Vec<(D, T, R)>> {

        use std::cmp::Ordering;

        // TODO: `list1` and `list2` get dropped; would be better to reuse?
        let mut output = Vec::with_capacity(list1.len() + list2.len());
        let mut result = Vec::with_capacity(1024);

        let mut list1 = VecQueue::from(list1);
        let mut list2 = VecQueue::from(list2);

        let mut head1 = if !list1.is_empty() { VecQueue::from(list1.pop()) } else { VecQueue::new() };
        let mut head2 = if !list2.is_empty() { VecQueue::from(list2.pop()) } else { VecQueue::new() };

        // while we have valid data in each input, merge.
        while !head1.is_empty() && !head2.is_empty() {

            while (result.capacity() - result.len()) > 0 && head1.len() > 0 && head2.len() > 0 {

                let cmp = {
                    let x = head1.peek();
                    let y = head2.peek();
                    (&x.0, &x.1).cmp(&(&y.0, &y.1))
                };
                match cmp {
                    Ordering::Less    => { unsafe { push_unchecked(&mut result, head1.pop()); } }
                    Ordering::Greater => { unsafe { push_unchecked(&mut result, head2.pop()); } }
                    Ordering::Equal   => {
                        let (data1, time1, diff1) = head1.pop();
                        let (_data2, _time2, diff2) = head2.pop();
                        let diff = diff1 + diff2;
                        if !diff.is_zero() {
                            unsafe { push_unchecked(&mut result, (data1, time1, diff)); }
                        }
                    }
                }
            }

            if result.capacity() == result.len() {
                output.push(result);
                result = self.stash.pop().unwrap_or_else(|| Vec::with_capacity(1024));
            }

            if head1.is_empty() {
                let done1 = head1.done();
                if done1.capacity() == 1024 { self.stash.push(done1); }
                head1 = if !list1.is_empty() { VecQueue::from(list1.pop()) } else { VecQueue::new() };
            }
            if head2.is_empty() {
                let done2 = head2.done();
                if done2.capacity() == 1024 { self.stash.push(done2); }
                head2 = if !list2.is_empty() { VecQueue::from(list2.pop()) } else { VecQueue::new() };
            }
        }

        if result.len() > 0 { output.push(result); }
        else if result.capacity() > 0 { self.stash.push(result); }

        if !head1.is_empty() {
            let mut result = self.stash.pop().unwrap_or_else(|| Vec::with_capacity(1024));
            for _ in 0 .. head1.len() { result.push(head1.pop()); }
            output.push(result);
        }
        while !list1.is_empty() {
            output.push(list1.pop());
        }

        if !head2.is_empty() {
            let mut result = self.stash.pop().unwrap_or(Vec::with_capacity(1024));
            for _ in 0 .. head2.len() { result.push(head2.pop()); }
            output.push(result);
        }
        while !list2.is_empty() {
            output.push(list2.pop());
        }

        output
    }
}

/// Reports the number of elements satisfing the predicate.
///
/// This methods *relies strongly* on the assumption that the predicate
/// stays false once it becomes false, a joint property of the predicate
/// and the slice. This allows `advance` to use exponential search to
/// count the number of elements in time logarithmic in the result.
#[inline(always)]
pub fn _advance<T, F: Fn(&T)->bool>(slice: &[T], function: F) -> usize {

	// start with no advance
	let mut index = 0;
	if index < slice.len() && function(&slice[index]) {

		// advance in exponentially growing steps.
		let mut step = 1;
		while index + step < slice.len() && function(&slice[index + step]) {
			index += step;
			step = step << 1;
		}

		// advance in exponentially shrinking steps.
		step = step >> 1;
		while step > 0 {
			if index + step < slice.len() && function(&slice[index + step]) {
				index += step;
			}
			step = step >> 1;
		}

		index += 1;
	}

	index
}
<commit_msg>memory leak in merge_batcher<commit_after>//! A general purpose `Batcher` implementation based on radix sort.

use timely::progress::frontier::Antichain;

use ::Diff;

use lattice::Lattice;
use trace::{Batch, Batcher, Builder};

/// Creates batches from unordered tuples.
pub struct MergeBatcher<K: Ord, V: Ord, T: Ord, R: Diff, B: Batch<K, V, T, R>> {
    sorter: MergeSorter<(K, V), T, R>,
    lower: Vec<T>,
    frontier: Antichain<T>,
    phantom: ::std::marker::PhantomData<B>,
}

impl<K, V, T, R, B> Batcher<K, V, T, R, B> for MergeBatcher<K, V, T, R, B>
where
    K: Ord+Clone,
    V: Ord+Clone,
    T: Lattice+Ord+Clone,
    R: Diff,
    B: Batch<K, V, T, R>,
{
    fn new() -> Self {
        MergeBatcher {
            sorter: MergeSorter::new(),
            frontier: Antichain::new(),
            lower: vec![T::minimum()],
            phantom: ::std::marker::PhantomData,
        }
    }

    #[inline(never)]
    fn push_batch(&mut self, batch: &mut Vec<((K,V),T,R)>) {
        self.sorter.push(batch);
    }

    // Sealing a batch means finding those updates with times not greater or equal to any time
    // in `upper`. All updates must have time greater or equal to the previously used `upper`,
    // which we call `lower`, by assumption that after sealing a batcher we receive no more
    // updates with times not greater or equal to `upper`.
    #[inline(never)]
    fn seal(&mut self, upper: &[T]) -> B {

        let mut builder = B::Builder::new();

        let mut merged = Vec::new();
        self.sorter.finish_into(&mut merged);

        let mut kept = Vec::new();
        let mut keep = Vec::with_capacity(1024);

        self.frontier.clear();

        for mut buffer in merged.drain(..) {
            for ((key, val), time, diff) in buffer.drain(..) {
                if upper.iter().any(|t| t.less_equal(&time)) {
                    // keep_count += 1;
                    self.frontier.insert(time.clone());
                    keep.push(((key, val), time, diff));
                    if keep.len() == keep.capacity() {
                        kept.push(keep);
                        keep = self.sorter.empty();
                    }
                }
                else {
                    // seal_count += 1;
                    builder.push((key, val, time, diff));
                }
            }
        }
        if keep.len() > 0 {
            kept.push(keep);
        }
        if kept.len() > 0 {
            self.sorter.push_list(kept);
        }

        let seal = builder.done(&self.lower[..], &upper[..], &self.lower[..]);
        self.lower = upper.to_vec();
        seal
    }

    // the frontier of elements remaining after the most recent call to `self.seal`.
    fn frontier(&mut self) -> &[T] {
        self.frontier.elements()
    }
}

use std::slice::{from_raw_parts};

pub struct VecQueue<T> {
    list: Vec<T>,
    head: usize,
    tail: usize,
}

impl<T> VecQueue<T> {
    #[inline(always)]
    pub fn new() -> Self { VecQueue::from(Vec::new()) }
    #[inline(always)]
    pub fn pop(&mut self) -> T {
        debug_assert!(self.head < self.tail);
        self.head += 1;
        unsafe { ::std::ptr::read(self.list.as_mut_ptr().offset(((self.head as isize) - 1) )) }
    }
    #[inline(always)]
    pub fn peek(&self) -> &T {
        debug_assert!(self.head < self.tail);
        unsafe { self.list.get_unchecked(self.head) }
    }
    #[inline(always)]
    pub fn _peek_tail(&self) -> &T {
        debug_assert!(self.head < self.tail);
        unsafe { self.list.get_unchecked(self.tail-1) }
    }
    #[inline(always)]
    pub fn _slice(&self) -> &[T] {
        debug_assert!(self.head < self.tail);
        unsafe { from_raw_parts(self.list.get_unchecked(self.head), self.tail - self.head) }
    }
    #[inline(always)]
    pub fn from(mut list: Vec<T>) -> Self {
        let tail = list.len();
        unsafe { list.set_len(0); }
        VecQueue {
            list: list,
            head: 0,
            tail: tail,
        }
    }
    // could leak, if self.head != self.tail.
    #[inline(always)]
    pub fn done(self) -> Vec<T> {
        debug_assert!(self.head == self.tail);
        self.list
    }
    #[inline(always)]
    pub fn len(&self) -> usize { self.tail - self.head }
    #[inline(always)]
    pub fn is_empty(&self) -> bool { self.head == self.tail }
}

#[inline(always)]
unsafe fn push_unchecked<T>(vec: &mut Vec<T>, element: T) {
    debug_assert!(vec.len() < vec.capacity());
    let len = vec.len();
    ::std::ptr::write(vec.get_unchecked_mut(len), element);
    vec.set_len(len + 1);
}

pub struct MergeSorter<D: Ord, T: Ord, R: Diff> {
    queue: Vec<Vec<Vec<(D, T, R)>>>,    // each power-of-two length list of allocations.
    stash: Vec<Vec<(D, T, R)>>,
}

impl<D: Ord, T: Ord, R: Diff> MergeSorter<D, T, R> {

    #[inline]
    pub fn new() -> Self { MergeSorter { queue: Vec::new(), stash: Vec::new() } }

    #[inline]
    pub fn empty(&mut self) -> Vec<(D, T, R)> {
        self.stash.pop().unwrap_or_else(|| Vec::with_capacity(1024))
    }

    #[inline(never)]
    pub fn _sort(&mut self, list: &mut Vec<Vec<(D, T, R)>>) {
        for mut batch in list.drain(..) {
            self.push(&mut batch);
        }
        self.finish_into(list);
    }

    #[inline]
    pub fn push(&mut self, batch: &mut Vec<(D, T, R)>) {

        let mut batch = if self.stash.len() > 2 {
            ::std::mem::replace(batch, self.stash.pop().unwrap())
        }
        else {
            ::std::mem::replace(batch, Vec::new())
        };

        if batch.len() > 0 {
            batch.sort_by(|x,y| (&x.0, &x.1).cmp(&(&y.0, &y.1)));
            for index in 1 .. batch.len() {
                if batch[index].0 == batch[index - 1].0 && batch[index].1 == batch[index - 1].1 {
                    batch[index].2 = batch[index].2 + batch[index - 1].2;
                    batch[index - 1].2 = R::zero();
                }
            }
            batch.retain(|x| !x.2.is_zero());

            self.queue.push(vec![batch]);
            while self.queue.len() > 1 && (self.queue[self.queue.len()-1].len() >= self.queue[self.queue.len()-2].len() / 2) {
                let list1 = self.queue.pop().unwrap();
                let list2 = self.queue.pop().unwrap();
                let merged = self.merge_by(list1, list2);
                self.queue.push(merged);
            }
        }
    }

    // This is awkward, because it isn't a power-of-two length any more, and we don't want
    // to break it down to be so.
    pub fn push_list(&mut self, list: Vec<Vec<(D, T, R)>>) {
        while self.queue.len() > 1 && self.queue[self.queue.len()-1].len() < list.len() {
            let list1 = self.queue.pop().unwrap();
            let list2 = self.queue.pop().unwrap();
            let merged = self.merge_by(list1, list2);
            self.queue.push(merged);
        }
        self.queue.push(list);
    }

    #[inline(never)]
    pub fn finish_into(&mut self, target: &mut Vec<Vec<(D, T, R)>>) {
        while self.queue.len() > 1 {
            let list1 = self.queue.pop().unwrap();
            let list2 = self.queue.pop().unwrap();
            let merged = self.merge_by(list1, list2);
            self.queue.push(merged);
        }

        if let Some(mut last) = self.queue.pop() {
            ::std::mem::swap(&mut last, target);
        }
    }

    // merges two sorted input lists into one sorted output list.
    #[inline(never)]
    fn merge_by(&mut self, list1: Vec<Vec<(D, T, R)>>, list2: Vec<Vec<(D, T, R)>>) -> Vec<Vec<(D, T, R)>> {

        use std::cmp::Ordering;

        // TODO: `list1` and `list2` get dropped; would be better to reuse?
        let mut output = Vec::with_capacity(list1.len() + list2.len());
        let mut result = self.stash.pop().unwrap_or(Vec::with_capacity(1024));

        let mut list1 = VecQueue::from(list1);
        let mut list2 = VecQueue::from(list2);

        let mut head1 = if !list1.is_empty() { VecQueue::from(list1.pop()) } else { VecQueue::new() };
        let mut head2 = if !list2.is_empty() { VecQueue::from(list2.pop()) } else { VecQueue::new() };

        // while we have valid data in each input, merge.
        while !head1.is_empty() && !head2.is_empty() {

            while (result.capacity() - result.len()) > 0 && head1.len() > 0 && head2.len() > 0 {

                let cmp = {
                    let x = head1.peek();
                    let y = head2.peek();
                    (&x.0, &x.1).cmp(&(&y.0, &y.1))
                };
                match cmp {
                    Ordering::Less    => { unsafe { push_unchecked(&mut result, head1.pop()); } }
                    Ordering::Greater => { unsafe { push_unchecked(&mut result, head2.pop()); } }
                    Ordering::Equal   => {
                        let (data1, time1, diff1) = head1.pop();
                        let (_data2, _time2, diff2) = head2.pop();
                        let diff = diff1 + diff2;
                        if !diff.is_zero() {
                            unsafe { push_unchecked(&mut result, (data1, time1, diff)); }
                        }
                    }
                }
            }

            if result.capacity() == result.len() {
                output.push(result);
                result = self.stash.pop().unwrap_or_else(|| Vec::with_capacity(1024));
            }

            if head1.is_empty() {
                let done1 = head1.done();
                if done1.capacity() == 1024 { self.stash.push(done1); }
                head1 = if !list1.is_empty() { VecQueue::from(list1.pop()) } else { VecQueue::new() };
            }
            if head2.is_empty() {
                let done2 = head2.done();
                if done2.capacity() == 1024 { self.stash.push(done2); }
                head2 = if !list2.is_empty() { VecQueue::from(list2.pop()) } else { VecQueue::new() };
            }
        }

        if result.len() > 0 { output.push(result); }
        else if result.capacity() > 0 { self.stash.push(result); }

        if !head1.is_empty() {
            let mut result = self.stash.pop().unwrap_or_else(|| Vec::with_capacity(1024));
            for _ in 0 .. head1.len() { result.push(head1.pop()); }
            output.push(result);
        }
        while !list1.is_empty() {
            output.push(list1.pop());
        }

        if !head2.is_empty() {
            let mut result = self.stash.pop().unwrap_or(Vec::with_capacity(1024));
            for _ in 0 .. head2.len() { result.push(head2.pop()); }
            output.push(result);
        }
        while !list2.is_empty() {
            output.push(list2.pop());
        }

        output
    }
}

/// Reports the number of elements satisfing the predicate.
///
/// This methods *relies strongly* on the assumption that the predicate
/// stays false once it becomes false, a joint property of the predicate
/// and the slice. This allows `advance` to use exponential search to
/// count the number of elements in time logarithmic in the result.
#[inline(always)]
pub fn _advance<T, F: Fn(&T)->bool>(slice: &[T], function: F) -> usize {

	// start with no advance
	let mut index = 0;
	if index < slice.len() && function(&slice[index]) {

		// advance in exponentially growing steps.
		let mut step = 1;
		while index + step < slice.len() && function(&slice[index + step]) {
			index += step;
			step = step << 1;
		}

		// advance in exponentially shrinking steps.
		step = step >> 1;
		while step > 0 {
			if index + step < slice.len() && function(&slice[index + step]) {
				index += step;
			}
			step = step >> 1;
		}

		index += 1;
	}

	index
}
<|endoftext|>


<commit_before>#[macro_use] extern crate conv;

use conv::TryFrom;

#[derive(Debug, PartialEq)]
enum Get { Up, Down, AllAround }

TryFrom! { (u8)
    enum Get {
        /// The +Z direction
        Up,
        /// The -Z direction
        Down,
        /// Just... everywhere
        AllAround
    }
}

#[test]
fn test_try_from() {
    assert_eq!(Get::try_from(0u8), Ok(Get::Up));
    assert_eq!(Get::try_from(1u8), Ok(Get::Down));
    assert_eq!(Get::try_from(2u8), Ok(Get::AllAround));
    assert_eq!(Get::try_from(3u8), Err(3u8));
}
<commit_msg>Adjusted TryFrom test.<commit_after>#[macro_use] extern crate conv;

use conv::TryFrom;

#[derive(Debug, PartialEq)]
enum Get { Up, Down, AllAround }

TryFrom! { (u8)
    enum Get {
        Up,
        /// And
        Down,
        /** And */
        AllAround
    }
}

#[derive(Debug, PartialEq)]
enum GottaGo { GetAway, Fast = 9000, Faster = 9001 }

TryFrom! { (u16)
    enum GottaGo {
        GetAway,
        Fast = 9000,
        /// This show was stupid.
        Faster = 9001
    }
}

#[test]
fn test_try_from() {
    assert_eq!(Get::try_from(0u8), Ok(Get::Up));
    assert_eq!(Get::try_from(1u8), Ok(Get::Down));
    assert_eq!(Get::try_from(2u8), Ok(Get::AllAround));
    assert_eq!(Get::try_from(3u8), Err(3u8));

    assert_eq!(GottaGo::try_from(0u16), Ok(GottaGo::GetAway));
    assert_eq!(GottaGo::try_from(1u16), Err(1u16));
    assert_eq!(GottaGo::try_from(2u16), Err(2u16));
    assert_eq!(GottaGo::try_from(3u16), Err(3u16));
    assert_eq!(GottaGo::try_from(8999u16), Err(8999u16));
    assert_eq!(GottaGo::try_from(9000u16), Ok(GottaGo::Fast));
    assert_eq!(GottaGo::try_from(9001u16), Ok(GottaGo::Faster));
    assert_eq!(GottaGo::try_from(9002u16), Err(8999u16));
}
<|endoftext|>


<commit_before>/* Cymbalum, Molecular Simulation in Rust - Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 */
#![allow(unused_variables)]

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;

use ::universe::Universe;
use ::potentials::*;

use super::yaml::{YamlLoader, Yaml};
use super::{Error, Result};

trait FromYaml where Self: Sized {
    fn from_yaml(node: &Yaml) -> Result<Self>;
}

// TODO: Add support for * in atoms sections.

/// Read a interactions from a Yaml file at `path`, and add these interactions
/// to the `universe`.
///
/// The following format is accepted:
///
/// ```YAML
/// pairs:  # Non bonded atoms pairs
///   - atoms: [He, He]
///     type: LennardJones
///     sigma: 3.4 A
///     epsilon: 0.45 kJ/mol
///     # computations specification is optional
///     computation:
///       type: table
///       numpoints: 5000
///       max: 20.0 A
///   - atoms: [He, Ar]
///     type: LennardJones
///     sigma: 3.8 A
///     epsilon: 0.67 kJ/mol
///     computation:
///       type: cutoff
///       cutoff: 10 A
///   - atoms: [He, Ar]
///     type: NullPotential
/// bond: # Bonded atoms pairs
///   - atoms: [C, C]
///     type: Harmonic
///     k: 67 kJ/mol/A^2
///     x0: 1.20 A
/// angles: # Molecular angles
///   - atoms: [O, C, C]
///     type: CosineHarmonic
///     k: 67 kJ/mol/deg^2
///     x0: 120 deg
///   - atoms: [C, C, C]
///     type: harmonic
///     k: 300 kJ/mol/deg^2
///     x0: 120 deg
/// dihedrals: # Molecular angles
///   - atoms: [O, C, C, O]
///     type: harmonic
///     k: 42 kJ/mol/deg^2
///     x0: 180 deg
/// ```
///
/// The main items are the `"pairs"`, `"bonds"`, `"angles"` and `"dihedrals"`;
/// which contains the data for pair potentials, bonds, angles and dihedral
/// angles potential. This data is orgnised as a vector of hash maps. Each maps
/// has at least a vector of `"atoms"` defining which particles will get this
/// interaction applied to, and a `"type"` parameter defining the type of the
/// potential. Others keys may be supplied depending on the potential type.
pub fn read_potentials<P: AsRef<Path>>(universe: &mut Universe, path: P) -> Result<()> {
    let mut file = try!(File::open(path));
    let mut buff = String::new();
    try!(file.read_to_string(&mut buff));

    let doc = &try!(YamlLoader::load_from_str(&buff))[0];
    if let Some(config) = doc["pairs"].as_vec() {
        try!(read_pairs(universe, config, true));
    }

    if let Some(config) = doc["bonds"].as_vec() {
        try!(read_pairs(universe, config, false));
    }

    if let Some(config) = doc["angles"].as_vec() {
        try!(read_angles(universe, config));
    }

    if let Some(config) = doc["dihedrals"].as_vec() {
        try!(read_dihedrals(universe, config));
    }

    Ok(())
}

/// Read the "pairs" or the "bonds" section in the file. If `pair_potentials`
/// is `true`, then the interactions are added to the pair interactions. Else,
/// the interaction are added to the bond interactions.
fn read_pairs(universe: &mut Universe, pairs: &Vec<Yaml>, pair_potentials: bool) -> Result<()> {
    for potential in pairs {
        let (a, b) = match potential["atoms"].as_vec() {
            Some(vec) => {
                if vec.len() != 2 {
                    return Err(Error::from(
                        format!("Wrong size for 'atoms' section in pair potentials. Should be 2, is {}", vec.len())
                    ));
                }
                if let (Some(a), Some(b)) = (vec[0].as_str(), vec[1].as_str()) {
                    (a, b)
                } else {
                    return Err(Error::from("The atoms names are not strings in pair potential"));
                }
            },
            None => {
                return Err(Error::from("Missing 'atoms' section in pair potential"));
            }
        };

        let potential = if potential["computation"].as_hash().is_some() {
            try!(read_pair_computation(potential))
        } else {
            try!(read_pair_potential(potential))
        };

        if pair_potentials {
            universe.add_pair_interaction(a, b, potential);
        } else {
            universe.add_bond_interaction(a, b, potential);
        }
    }
    Ok(())
}

fn read_pair_potential(node: &Yaml) -> Result<Box<PairPotential>> {
    match node["type"].as_str() {
        None => {
            Err(Error::from("Missing 'type' section in pair potential"))
        }
        Some(val) => {
            let val: &str = &val.to_lowercase();
            match val {
                "harmonic" => Ok(Box::new(try!(Harmonic::from_yaml(node)))),
                "lennard-jones" | "lennardjones" => Ok(Box::new(try!(LennardJones::from_yaml(node)))),
                "null" | "nullpotential" => Ok(Box::new(try!(NullPotential::from_yaml(node)))),
                val => Err(
                    Error::from(format!("Unknown potential type '{}'", val))
                ),
            }
        }
    }
}

/******************************************************************************/

fn read_angles(universe: &mut Universe, angles: &Vec<Yaml>) -> Result<()> {
    for potential in angles {
        let (a, b, c) = match potential["atoms"].as_vec() {
            Some(vec) => {
                if vec.len() != 3 {
                    return Err(Error::from(
                        format!("Wrong size for 'atoms' section in angle potential. Should be 3, is {}", vec.len())
                    ));
                }
                if let (Some(a), Some(b), Some(c)) = (vec[0].as_str(), vec[1].as_str(), vec[2].as_str()) {
                    (a, b, c)
                } else {
                    return Err(Error::from("The atoms names are not strings in angle potential"));
                }
            },
            None => {
                return Err(Error::from("Missing 'atoms' section in angles potential"));
            }
        };

        let potential = try!(read_angle_potential(potential));
        universe.add_angle_interaction(a, b, c, potential);
    }
    Ok(())
}

fn read_angle_potential(node: &Yaml) -> Result<Box<AnglePotential>> {
    match node["type"].as_str() {
        None => {
            Err(Error::from(
                format!("Missing 'type' section in: {:?}", node)
            ))
        }
        Some(val) => {
            let val: &str = &val.to_lowercase();
            match val {
                "harmonic" => Ok(Box::new(try!(Harmonic::from_yaml(node)))),
                "cosine-harmonic" | "cosineharmonic" => Ok(Box::new(try!(CosineHarmonic::from_yaml(node)))),
                "null" => Ok(Box::new(try!(NullPotential::from_yaml(node)))),
                val => Err(
                    Error::from(format!("Unknown potential type '{}'", val))
                ),
            }
        }
    }
}

/******************************************************************************/

fn read_dihedrals(universe: &mut Universe, dihedrals: &Vec<Yaml>) -> Result<()> {
    for potential in dihedrals {
        let (a, b, c, d) = match potential["atoms"].as_vec() {
            Some(vec) => {
                if vec.len() != 4 {
                    return Err(Error::from(
                        format!("Wrong size for 'atoms' section in dihedral potential. Should be 4, is {}", vec.len())
                    ));
                }
                if let (Some(a), Some(b), Some(c), Some(d)) = (vec[0].as_str(), vec[1].as_str(), vec[2].as_str(), vec[3].as_str()) {
                    (a, b, c, d)
                } else {
                    return Err(Error::from("The atoms names are not strings in dihedral potential"));
                }
            },
            None => {
                return Err(Error::from("Missing 'atoms' section in dihedral potential"));
            }
        };

        let potential = try!(read_dihedral_potential(potential));
        universe.add_dihedral_interaction(a, b, c, d, potential);
    }
    Ok(())
}

fn read_dihedral_potential(node: &Yaml) -> Result<Box<DihedralPotential>> {
    match node["type"].as_str() {
        None => {
            Err(Error::from(
                format!("Missing 'type' section in: {:?}", node)
            ))
        }
        Some(val) => {
            let val: &str = &val.to_lowercase();
            match val {
                "harmonic" => Ok(Box::new(try!(Harmonic::from_yaml(node)))),
                "cosine-harmonic" | "cosineharmonic" => Ok(Box::new(try!(CosineHarmonic::from_yaml(node)))),
                "null" => Ok(Box::new(try!(NullPotential::from_yaml(node)))),
                val => Err(
                    Error::from(format!("Unknown potential type '{}'", val))
                ),
            }
        }
    }
}

/******************************************************************************/

impl FromYaml for NullPotential {
    fn from_yaml(node: &Yaml) -> Result<NullPotential> {
        Ok(NullPotential)
    }
}

impl FromYaml for Harmonic {
    fn from_yaml(node: &Yaml) -> Result<Harmonic> {
        if let (Some(k), Some(x0)) = (node["k"].as_str(), node["x0"].as_str()) {
            let k = try!(::units::from_str(k));
            let x0 = try!(::units::from_str(x0));
            Ok(Harmonic{k: k, x0: x0})
        } else {
            Err(
                Error::from("Missing 'k' or 'x0' in harmonic potential")
            )
        }
    }
}

impl FromYaml for LennardJones {
    fn from_yaml(node: &Yaml) -> Result<LennardJones> {
        if let (Some(sigma), Some(epsilon)) = (node["sigma"].as_str(), node["epsilon"].as_str()) {
            let sigma = try!(::units::from_str(sigma));
            let epsilon = try!(::units::from_str(epsilon));
            Ok(LennardJones{sigma: sigma, epsilon: epsilon})
        } else {
            Err(
                Error::from("Missing 'sigma' or 'espilon' in Lennard-Jones potential")
            )
        }
    }
}

impl FromYaml for CosineHarmonic {
    fn from_yaml(node: &Yaml) -> Result<CosineHarmonic> {
        if let (Some(k), Some(x0)) = (node["k"].as_str(), node["x0"].as_str()) {
            let k = try!(::units::from_str(k));
            let x0 = try!(::units::from_str(x0));
            Ok(CosineHarmonic::new(k, x0))
        } else {
            Err(
                Error::from("Missing 'k' or 'x0' in cosine harmonic potential")
            )
        }
    }
}

/******************************************************************************/

fn read_pair_computation(node: &Yaml) -> Result<Box<PairPotential>> {
    let pot = try!(read_pair_potential(node));
    match node["type"].as_str() {
        None => {
            Err(Error::from(
                format!("Missing 'type' section for potential computation in: {:?}", node)
            ))
        }
        Some(val) => {
            let val: &str = &val.to_lowercase();
            match val {
                "cutoff" => Ok(Box::new(try!(CutoffComputation::from_yaml(node, pot)))),
                //"table" => Ok(Box::new(try!(TableComputation::from_yaml(node, pot)))),
                val => Err(
                    Error::from(format!("Unknown computation type '{}'", val))
                ),
            }
        }
    }
}

trait FromYamlWithPairPotential where Self: Sized {
    fn from_yaml(node: &Yaml, potential: Box<PairPotential>) -> Result<Self>;
}

impl FromYamlWithPairPotential for CutoffComputation {
    fn from_yaml(node: &Yaml, potential: Box<PairPotential>) -> Result<CutoffComputation> {
        if let Some(cutoff) = node["cutoff"].as_str() {
            let cutoff = try!(::units::from_str(cutoff));
            Ok(CutoffComputation::new(potential, cutoff))
        } else {
            Err(
                Error::from("Missing 'cutoff' value in cutoff computation")
            )
        }
    }
}

impl FromYamlWithPairPotential for TableComputation {
    fn from_yaml(node: &Yaml, potential: Box<PairPotential>) -> Result<TableComputation> {
        if let (Some(n), Some(max)) = (node["max"].as_i64(), node["max"].as_str()) {
            let max = try!(::units::from_str(max));
            Ok(TableComputation::new(potential, n as usize, max))
        } else {
            Err(
                Error::from("Missing 'cutoff' value in cutoff computation")
            )
        }
    }
}
<commit_msg>Enable table computation in input<commit_after>/* Cymbalum, Molecular Simulation in Rust - Copyright (C) 2015 Guillaume Fraux
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/
 */
#![allow(unused_variables)]

use std::io::prelude::*;
use std::fs::File;
use std::path::Path;

use ::universe::Universe;
use ::potentials::*;

use super::yaml::{YamlLoader, Yaml};
use super::{Error, Result};

trait FromYaml where Self: Sized {
    fn from_yaml(node: &Yaml) -> Result<Self>;
}

// TODO: Add support for * in atoms sections.

/// Read a interactions from a Yaml file at `path`, and add these interactions
/// to the `universe`.
///
/// The following format is accepted:
///
/// ```YAML
/// pairs:  # Non bonded atoms pairs
///   - atoms: [He, He]
///     type: LennardJones
///     sigma: 3.4 A
///     epsilon: 0.45 kJ/mol
///     # computations specification is optional
///     computation:
///       type: table
///       numpoints: 5000
///       max: 20.0 A
///   - atoms: [He, Ar]
///     type: LennardJones
///     sigma: 3.8 A
///     epsilon: 0.67 kJ/mol
///     computation:
///       type: cutoff
///       cutoff: 10 A
///   - atoms: [He, Ar]
///     type: NullPotential
/// bond: # Bonded atoms pairs
///   - atoms: [C, C]
///     type: Harmonic
///     k: 67 kJ/mol/A^2
///     x0: 1.20 A
/// angles: # Molecular angles
///   - atoms: [O, C, C]
///     type: CosineHarmonic
///     k: 67 kJ/mol/deg^2
///     x0: 120 deg
///   - atoms: [C, C, C]
///     type: harmonic
///     k: 300 kJ/mol/deg^2
///     x0: 120 deg
/// dihedrals: # Molecular angles
///   - atoms: [O, C, C, O]
///     type: harmonic
///     k: 42 kJ/mol/deg^2
///     x0: 180 deg
/// ```
///
/// The main items are the `"pairs"`, `"bonds"`, `"angles"` and `"dihedrals"`;
/// which contains the data for pair potentials, bonds, angles and dihedral
/// angles potential. This data is orgnised as a vector of hash maps. Each maps
/// has at least a vector of `"atoms"` defining which particles will get this
/// interaction applied to, and a `"type"` parameter defining the type of the
/// potential. Others keys may be supplied depending on the potential type.
pub fn read_potentials<P: AsRef<Path>>(universe: &mut Universe, path: P) -> Result<()> {
    let mut file = try!(File::open(path));
    let mut buff = String::new();
    try!(file.read_to_string(&mut buff));

    let doc = &try!(YamlLoader::load_from_str(&buff))[0];
    if let Some(config) = doc["pairs"].as_vec() {
        try!(read_pairs(universe, config, true));
    }

    if let Some(config) = doc["bonds"].as_vec() {
        try!(read_pairs(universe, config, false));
    }

    if let Some(config) = doc["angles"].as_vec() {
        try!(read_angles(universe, config));
    }

    if let Some(config) = doc["dihedrals"].as_vec() {
        try!(read_dihedrals(universe, config));
    }

    Ok(())
}

/// Read the "pairs" or the "bonds" section in the file. If `pair_potentials`
/// is `true`, then the interactions are added to the pair interactions. Else,
/// the interaction are added to the bond interactions.
fn read_pairs(universe: &mut Universe, pairs: &Vec<Yaml>, pair_potentials: bool) -> Result<()> {
    for potential in pairs {
        let (a, b) = match potential["atoms"].as_vec() {
            Some(vec) => {
                if vec.len() != 2 {
                    return Err(Error::from(
                        format!("Wrong size for 'atoms' section in pair potentials. Should be 2, is {}", vec.len())
                    ));
                }
                if let (Some(a), Some(b)) = (vec[0].as_str(), vec[1].as_str()) {
                    (a, b)
                } else {
                    return Err(Error::from("The atoms names are not strings in pair potential"));
                }
            },
            None => {
                return Err(Error::from("Missing 'atoms' section in pair potential"));
            }
        };

        let potential = if potential["computation"].as_hash().is_some() {
            try!(read_pair_computation(potential))
        } else {
            try!(read_pair_potential(potential))
        };

        if pair_potentials {
            universe.add_pair_interaction(a, b, potential);
        } else {
            universe.add_bond_interaction(a, b, potential);
        }
    }
    Ok(())
}

fn read_pair_potential(node: &Yaml) -> Result<Box<PairPotential>> {
    match node["type"].as_str() {
        None => {
            Err(Error::from("Missing 'type' section in pair potential"))
        }
        Some(val) => {
            let val: &str = &val.to_lowercase();
            match val {
                "harmonic" => Ok(Box::new(try!(Harmonic::from_yaml(node)))),
                "lennard-jones" | "lennardjones" => Ok(Box::new(try!(LennardJones::from_yaml(node)))),
                "null" | "nullpotential" => Ok(Box::new(try!(NullPotential::from_yaml(node)))),
                val => Err(
                    Error::from(format!("Unknown potential type '{}'", val))
                ),
            }
        }
    }
}

/******************************************************************************/

fn read_angles(universe: &mut Universe, angles: &Vec<Yaml>) -> Result<()> {
    for potential in angles {
        let (a, b, c) = match potential["atoms"].as_vec() {
            Some(vec) => {
                if vec.len() != 3 {
                    return Err(Error::from(
                        format!("Wrong size for 'atoms' section in angle potential. Should be 3, is {}", vec.len())
                    ));
                }
                if let (Some(a), Some(b), Some(c)) = (vec[0].as_str(), vec[1].as_str(), vec[2].as_str()) {
                    (a, b, c)
                } else {
                    return Err(Error::from("The atoms names are not strings in angle potential"));
                }
            },
            None => {
                return Err(Error::from("Missing 'atoms' section in angles potential"));
            }
        };

        let potential = try!(read_angle_potential(potential));
        universe.add_angle_interaction(a, b, c, potential);
    }
    Ok(())
}

fn read_angle_potential(node: &Yaml) -> Result<Box<AnglePotential>> {
    match node["type"].as_str() {
        None => {
            Err(Error::from(
                format!("Missing 'type' section in: {:?}", node)
            ))
        }
        Some(val) => {
            let val: &str = &val.to_lowercase();
            match val {
                "harmonic" => Ok(Box::new(try!(Harmonic::from_yaml(node)))),
                "cosine-harmonic" | "cosineharmonic" => Ok(Box::new(try!(CosineHarmonic::from_yaml(node)))),
                "null" => Ok(Box::new(try!(NullPotential::from_yaml(node)))),
                val => Err(
                    Error::from(format!("Unknown potential type '{}'", val))
                ),
            }
        }
    }
}

/******************************************************************************/

fn read_dihedrals(universe: &mut Universe, dihedrals: &Vec<Yaml>) -> Result<()> {
    for potential in dihedrals {
        let (a, b, c, d) = match potential["atoms"].as_vec() {
            Some(vec) => {
                if vec.len() != 4 {
                    return Err(Error::from(
                        format!("Wrong size for 'atoms' section in dihedral potential. Should be 4, is {}", vec.len())
                    ));
                }
                if let (Some(a), Some(b), Some(c), Some(d)) = (vec[0].as_str(), vec[1].as_str(), vec[2].as_str(), vec[3].as_str()) {
                    (a, b, c, d)
                } else {
                    return Err(Error::from("The atoms names are not strings in dihedral potential"));
                }
            },
            None => {
                return Err(Error::from("Missing 'atoms' section in dihedral potential"));
            }
        };

        let potential = try!(read_dihedral_potential(potential));
        universe.add_dihedral_interaction(a, b, c, d, potential);
    }
    Ok(())
}

fn read_dihedral_potential(node: &Yaml) -> Result<Box<DihedralPotential>> {
    match node["type"].as_str() {
        None => {
            Err(Error::from(
                format!("Missing 'type' section in: {:?}", node)
            ))
        }
        Some(val) => {
            let val: &str = &val.to_lowercase();
            match val {
                "harmonic" => Ok(Box::new(try!(Harmonic::from_yaml(node)))),
                "cosine-harmonic" | "cosineharmonic" => Ok(Box::new(try!(CosineHarmonic::from_yaml(node)))),
                "null" => Ok(Box::new(try!(NullPotential::from_yaml(node)))),
                val => Err(
                    Error::from(format!("Unknown potential type '{}'", val))
                ),
            }
        }
    }
}

/******************************************************************************/

impl FromYaml for NullPotential {
    fn from_yaml(node: &Yaml) -> Result<NullPotential> {
        Ok(NullPotential)
    }
}

impl FromYaml for Harmonic {
    fn from_yaml(node: &Yaml) -> Result<Harmonic> {
        if let (Some(k), Some(x0)) = (node["k"].as_str(), node["x0"].as_str()) {
            let k = try!(::units::from_str(k));
            let x0 = try!(::units::from_str(x0));
            Ok(Harmonic{k: k, x0: x0})
        } else {
            Err(
                Error::from("Missing 'k' or 'x0' in harmonic potential")
            )
        }
    }
}

impl FromYaml for LennardJones {
    fn from_yaml(node: &Yaml) -> Result<LennardJones> {
        if let (Some(sigma), Some(epsilon)) = (node["sigma"].as_str(), node["epsilon"].as_str()) {
            let sigma = try!(::units::from_str(sigma));
            let epsilon = try!(::units::from_str(epsilon));
            Ok(LennardJones{sigma: sigma, epsilon: epsilon})
        } else {
            Err(
                Error::from("Missing 'sigma' or 'espilon' in Lennard-Jones potential")
            )
        }
    }
}

impl FromYaml for CosineHarmonic {
    fn from_yaml(node: &Yaml) -> Result<CosineHarmonic> {
        if let (Some(k), Some(x0)) = (node["k"].as_str(), node["x0"].as_str()) {
            let k = try!(::units::from_str(k));
            let x0 = try!(::units::from_str(x0));
            Ok(CosineHarmonic::new(k, x0))
        } else {
            Err(
                Error::from("Missing 'k' or 'x0' in cosine harmonic potential")
            )
        }
    }
}

/******************************************************************************/

fn read_pair_computation(node: &Yaml) -> Result<Box<PairPotential>> {
    let pot = try!(read_pair_potential(node));
    match node["type"].as_str() {
        None => {
            Err(Error::from(
                format!("Missing 'type' section for potential computation in: {:?}", node)
            ))
        }
        Some(val) => {
            let val: &str = &val.to_lowercase();
            match val {
                "cutoff" => Ok(Box::new(try!(CutoffComputation::from_yaml(node, pot)))),
                "table" => Ok(Box::new(try!(TableComputation::from_yaml(node, pot)))),
                val => Err(
                    Error::from(format!("Unknown computation type '{}'", val))
                ),
            }
        }
    }
}

trait FromYamlWithPairPotential where Self: Sized {
    fn from_yaml(node: &Yaml, potential: Box<PairPotential>) -> Result<Self>;
}

impl FromYamlWithPairPotential for CutoffComputation {
    fn from_yaml(node: &Yaml, potential: Box<PairPotential>) -> Result<CutoffComputation> {
        if let Some(cutoff) = node["cutoff"].as_str() {
            let cutoff = try!(::units::from_str(cutoff));
            Ok(CutoffComputation::new(potential, cutoff))
        } else {
            Err(
                Error::from("Missing 'cutoff' value in cutoff computation")
            )
        }
    }
}

impl FromYamlWithPairPotential for TableComputation {
    fn from_yaml(node: &Yaml, potential: Box<PairPotential>) -> Result<TableComputation> {
        if let (Some(n), Some(max)) = (node["n"].as_i64(), node["max"].as_str()) {
            let max = try!(::units::from_str(max));
            Ok(TableComputation::new(potential, n as usize, max))
        } else {
            Err(
                Error::from("Missing 'max' or 'n' value in cutoff computation")
            )
        }
    }
}
<|endoftext|>


<commit_before>//! Implementation of the [ByteBuf] type

use super::RmpWrite;
#[cfg(not(feature = "std"))]
use core::fmt::{self, Display, Formatter};
use alloc::vec::Vec;

/// An error returned from writing to `&mut [u8]` (a byte buffer of fixed capacity) on no_std
///
/// In feature="std", capacity overflow in `<&mut [u8] as std::io::Write>::write_exact()`
/// currently returns [`ErrorKind::WriteZero`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.WriteZero).
///
/// Since no_std doesn't have std::io::Error we use this instead ;)
///
/// This is specific to `#[cfg(not(feature = "std"))]` so it is `#[doc(hidden)]`
#[derive(Debug)]
#[cfg(not(feature = "std"))]
#[doc(hidden)]
pub struct FixedBufCapacityOverflow {
    _priv: ()
}

/// An error returned from writing to `&mut [u8]`
///
/// Aliased for compatibility with `no_std` mode.
#[cfg(feature = "std")]
#[doc(hidden)]
pub type FixedBufCapacityOverflow = std::io::Error;

#[cfg(not(feature = "std"))]
impl Display for FixedBufCapacityOverflow {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        // This is intentionally vauge because std::io::Error is
        // Doesn't make sense for no_std to have bettetr errors than std
        f.write_str("Capacity overflow for fixed-size byte buffer")
    }
}
#[cfg(not(feature = "std"))]
impl crate::encode::RmpWriteErr for FixedBufCapacityOverflow {}

/// Fallback implementation for fixed-capacity buffers
///
/// Only needed for no-std because we don't have
/// the blanket impl for `std::io::Write`
#[cfg(not(feature = "std"))]
impl<'a> RmpWrite for &'a mut [u8] {
    type Error = FixedBufCapacityOverflow;

    #[inline]
    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
        let to_write = buf.len();
        let remaining = self.len();
        if to_write <= remaining {
            self[..to_write].copy_from_slice(buf);
            unsafe {
                //Cant use split_at or re-borrowing due to lifetime errors :(
                *self = core::slice::from_raw_parts_mut(
                    self.as_mut_ptr().add(to_write),
                    remaining - to_write,
                )
            }
            Ok(())
        } else {
            Err(FixedBufCapacityOverflow {
                _priv: ()
            })
        }
    }
}

/// A wrapper around `Vec<u8>` to serialize more efficiently.
///
/// This has a specialized implementation of `RmpWrite`.
///
/// This has the additional benefit of working on `#[no_std]`
///
/// See also [serde_bytes::ByteBuf](https://docs.rs/serde_bytes/0.11/serde_bytes/struct.ByteBuf.html)
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct ByteBuf {
    bytes: Vec<u8>,
}
impl ByteBuf {
    /// Construct a new empty buffer
    #[inline]
    pub fn new() -> Self {
        ByteBuf { bytes: Vec::new() }
    }
    /// Construct a new buffer with the specified capacity
    ///
    /// See [Vec::with_capacity] for details
    #[inline]
    pub fn with_capacity(capacity: usize) -> Self {
        ByteBuf { bytes: Vec::with_capacity(capacity) }
    }
    /// Unwrap the underlying buffer of this vector
    #[inline]
    pub fn into_vec(self) -> Vec<u8> {
        self.bytes
    }
    /// Wrap the specified vector as a [ByteBuf]
    #[inline]
    pub fn from_vec(bytes: Vec<u8>) -> Self {
        ByteBuf { bytes }
    }
    /// Get a reference to this type as a [Vec]
    #[inline]
    pub fn as_vec(&self) -> &Vec<u8> {
        &self.bytes
    }
    /// Get a mutable reference to this type as a [Vec]
    #[inline]
    pub fn as_mut_vec(&mut self) -> &mut Vec<u8> {
        &mut self.bytes
    }
    /// Get a reference to this type as a slice of bytes (`&[u8]`)
    #[inline]
    pub fn as_slice(&self) -> &[u8] {
        &self.bytes
    }
}
impl AsRef<[u8]> for ByteBuf {
    fn as_ref(&self) -> &[u8] {
        &self.bytes
    }
}
impl AsRef<Vec<u8>> for ByteBuf {
    #[inline]
    fn as_ref(&self) -> &Vec<u8> {
        &self.bytes
    }
}
impl AsMut<Vec<u8>> for ByteBuf {
    #[inline]
    fn as_mut(&mut self) -> &mut Vec<u8> {
        &mut self.bytes
    }
}
impl From<ByteBuf> for Vec<u8> {
    #[inline]
    fn from(buf: ByteBuf) -> Self {
        buf.bytes
    }
}
impl From<Vec<u8>> for ByteBuf {
    #[inline]
    fn from(bytes: Vec<u8>) -> Self {
        ByteBuf { bytes }
    }
}

impl RmpWrite for ByteBuf {
    type Error = core::convert::Infallible;

    #[inline]
    fn write_u8(&mut self, val: u8) -> Result<(), Self::Error> {
        self.bytes.push(val);
        Ok(())
    }

    #[inline]
    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
        self.bytes.extend_from_slice(buf);
        Ok(())
    }
}
#[cfg(not(feature = "std"))]
impl<'a> RmpWrite for Vec<u8> {
    type Error = core::convert::Infallible;

    #[inline]
    fn write_u8(&mut self, val: u8) -> Result<(), Self::Error> {
        self.push(val);
        Ok(())
    }

    #[inline]
    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
        self.extend_from_slice(buf);
        Ok(())
    }
}<commit_msg>Clarify the purpose of ByteBuf.<commit_after>//! Implementation of the [ByteBuf] type

use super::RmpWrite;
#[cfg(not(feature = "std"))]
use core::fmt::{self, Display, Formatter};
use alloc::vec::Vec;

/// An error returned from writing to `&mut [u8]` (a byte buffer of fixed capacity) on no_std
///
/// In feature="std", capacity overflow in `<&mut [u8] as std::io::Write>::write_exact()`
/// currently returns [`ErrorKind::WriteZero`](https://doc.rust-lang.org/std/io/enum.ErrorKind.html#variant.WriteZero).
///
/// Since no_std doesn't have std::io::Error we use this instead ;)
///
/// This is specific to `#[cfg(not(feature = "std"))]` so it is `#[doc(hidden)]`
#[derive(Debug)]
#[cfg(not(feature = "std"))]
#[doc(hidden)]
pub struct FixedBufCapacityOverflow {
    _priv: ()
}

/// An error returned from writing to `&mut [u8]`
///
/// Aliased for compatibility with `no_std` mode.
#[cfg(feature = "std")]
#[doc(hidden)]
pub type FixedBufCapacityOverflow = std::io::Error;

#[cfg(not(feature = "std"))]
impl Display for FixedBufCapacityOverflow {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        // This is intentionally vauge because std::io::Error is
        // Doesn't make sense for no_std to have bettetr errors than std
        f.write_str("Capacity overflow for fixed-size byte buffer")
    }
}
#[cfg(not(feature = "std"))]
impl crate::encode::RmpWriteErr for FixedBufCapacityOverflow {}

/// Fallback implementation for fixed-capacity buffers
///
/// Only needed for no-std because we don't have
/// the blanket impl for `std::io::Write`
#[cfg(not(feature = "std"))]
impl<'a> RmpWrite for &'a mut [u8] {
    type Error = FixedBufCapacityOverflow;

    #[inline]
    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
        let to_write = buf.len();
        let remaining = self.len();
        if to_write <= remaining {
            self[..to_write].copy_from_slice(buf);
            unsafe {
                //Cant use split_at or re-borrowing due to lifetime errors :(
                *self = core::slice::from_raw_parts_mut(
                    self.as_mut_ptr().add(to_write),
                    remaining - to_write,
                )
            }
            Ok(())
        } else {
            Err(FixedBufCapacityOverflow {
                _priv: ()
            })
        }
    }
}

/// A wrapper around `Vec<u8>` to serialize more efficiently.
///
/// This has a specialized implementation of `RmpWrite`
/// It gives `std::convert::Infailable` for errors.
/// This is because writing to `Vec<T>` can only fail due to allocation.
///
/// This has the additional benefit of working on `#[no_std]`
///
/// See also [serde_bytes::ByteBuf](https://docs.rs/serde_bytes/0.11/serde_bytes/struct.ByteBuf.html)
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct ByteBuf {
    bytes: Vec<u8>,
}
impl ByteBuf {
    /// Construct a new empty buffer
    #[inline]
    pub fn new() -> Self {
        ByteBuf { bytes: Vec::new() }
    }
    /// Construct a new buffer with the specified capacity
    ///
    /// See [Vec::with_capacity] for details
    #[inline]
    pub fn with_capacity(capacity: usize) -> Self {
        ByteBuf { bytes: Vec::with_capacity(capacity) }
    }
    /// Unwrap the underlying buffer of this vector
    #[inline]
    pub fn into_vec(self) -> Vec<u8> {
        self.bytes
    }
    /// Wrap the specified vector as a [ByteBuf]
    #[inline]
    pub fn from_vec(bytes: Vec<u8>) -> Self {
        ByteBuf { bytes }
    }
    /// Get a reference to this type as a [Vec]
    #[inline]
    pub fn as_vec(&self) -> &Vec<u8> {
        &self.bytes
    }
    /// Get a mutable reference to this type as a [Vec]
    #[inline]
    pub fn as_mut_vec(&mut self) -> &mut Vec<u8> {
        &mut self.bytes
    }
    /// Get a reference to this type as a slice of bytes (`&[u8]`)
    #[inline]
    pub fn as_slice(&self) -> &[u8] {
        &self.bytes
    }
}
impl AsRef<[u8]> for ByteBuf {
    fn as_ref(&self) -> &[u8] {
        &self.bytes
    }
}
impl AsRef<Vec<u8>> for ByteBuf {
    #[inline]
    fn as_ref(&self) -> &Vec<u8> {
        &self.bytes
    }
}
impl AsMut<Vec<u8>> for ByteBuf {
    #[inline]
    fn as_mut(&mut self) -> &mut Vec<u8> {
        &mut self.bytes
    }
}
impl From<ByteBuf> for Vec<u8> {
    #[inline]
    fn from(buf: ByteBuf) -> Self {
        buf.bytes
    }
}
impl From<Vec<u8>> for ByteBuf {
    #[inline]
    fn from(bytes: Vec<u8>) -> Self {
        ByteBuf { bytes }
    }
}

impl RmpWrite for ByteBuf {
    type Error = core::convert::Infallible;

    #[inline]
    fn write_u8(&mut self, val: u8) -> Result<(), Self::Error> {
        self.bytes.push(val);
        Ok(())
    }

    #[inline]
    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
        self.bytes.extend_from_slice(buf);
        Ok(())
    }
}
#[cfg(not(feature = "std"))]
impl<'a> RmpWrite for Vec<u8> {
    type Error = core::convert::Infallible;

    #[inline]
    fn write_u8(&mut self, val: u8) -> Result<(), Self::Error> {
        self.push(val);
        Ok(())
    }

    #[inline]
    fn write_bytes(&mut self, buf: &[u8]) -> Result<(), Self::Error> {
        self.extend_from_slice(buf);
        Ok(())
    }
}<|endoftext|>


<commit_before>use std::collections::{BinaryHeap, HashMap};
use std::env::home_dir;
use std::fs::{create_dir_all, OpenOptions, read_dir, remove_dir_all, remove_file, rename};
use std::io::Write;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};

use semver::Version;

use error::{ErrorKind, Result};
use process::{exec_command, run_command};

const DEFAULT_HOME_DIR: &str = ".monger";
const DEFAULT_BIN_DIR: &str = "mongodb-versions";
const DEFAULT_DB_DIR: &str = "db";

#[derive(Debug)]
pub struct Fs {
    home_dir: PathBuf,
    bin_dir: PathBuf,
    db_dir: PathBuf,
}

impl Fs {
    pub fn new() -> FsBuilder {
        Default::default()
    }

    #[inline]
    fn get_file<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.home_dir.join(filename)
    }

    #[inline]
    fn get_bin_file_rel<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.bin_dir.join(filename)
    }

    #[inline]
    fn get_bin_file_abs<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.get_file(self.get_bin_file_rel(filename))
    }

    #[inline]
    fn get_bin_dir(&self) -> PathBuf {
        self.get_file(self.bin_dir.as_path())
    }

    #[inline]
    fn get_db_file_rel<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.db_dir.join(filename)
    }

    #[inline]
    fn get_version_dir(&self, version: &str) -> PathBuf {
        self.get_bin_file_abs(version)
    }

    #[inline]
    fn get_version_bin_dir<P: AsRef<Path>>(&self, version: P) -> PathBuf {
        self.get_bin_file_abs(version.as_ref().join("bin"))
    }

    fn create(&self) -> Result<()> {
        create_dir_all(self.get_file(self.bin_dir.as_path()).as_path())?;
        create_dir_all(self.get_file(self.db_dir.as_path()).as_path())?;

        Ok(())
    }

    fn decompress_download<P: AsRef<Path>>(
        &self,
        filename: P,
        dirname: P,
        version: P,
    ) -> Result<()> {
        run_command(
            "tar",
            vec!["xf".as_ref(), filename.as_ref().as_os_str()],
            self.get_bin_dir(),
        )?;

        let old_name = self.get_bin_file_abs(dirname);
        let new_name = self.get_bin_file_abs(version);
        rename(old_name, new_name)?;

        Ok(())
    }

    fn delete_file<P: AsRef<Path>>(&self, filename: P) -> Result<()> {
        let path = self.home_dir.join(filename);

        if !path.exists() {
            return Ok(());
        }

        remove_file(path)?;

        Ok(())
    }

    fn delete_directory<P: AsRef<Path>>(&self, dirname: P) -> Result<bool> {
        let path = self.home_dir.join(dirname);

        if !path.exists() {
            return Ok(false);
        }

        remove_dir_all(path)?;

        Ok(true)
    }

    fn write_file<P: AsRef<Path>>(&self, filename: P, bytes: &[u8]) -> Result<()> {
        self.create()?;

        let filepath = self.home_dir.join(filename);
        let mut file = OpenOptions::new().write(true).create(true).open(filepath)?;
        file.write_all(bytes)?;

        Ok(())
    }

    #[inline]
    pub fn version_exists(&self, version: &str) -> bool {
        self.get_version_dir(version).is_dir()
    }

    pub fn create_or_get_db_dir(&self, version: &str) -> Result<PathBuf> {
        let db_dir = self.get_file(self.get_db_file_rel(version));
        create_dir_all(db_dir.as_path())?;
        Ok(db_dir)
    }

    pub fn delete_mongodb_version(&self, version: &str) -> Result<bool> {
        self.delete_directory(self.get_version_dir(version))
    }

    pub fn write_mongodb_download(
        &self,
        filename: &str,
        dirname: &str,
        bytes: &[u8],
        version: &str,
    ) -> Result<()> {
        let bin_file = self.get_bin_file_rel(filename);

        println!("writing {}...", bin_file.display());
        self.write_file(&bin_file, bytes)?;

        println!("decompressing...");
        self.decompress_download(filename, dirname, version)?;

        println!("cleaning up...");
        self.delete_file(&bin_file)?;

        Ok(())
    }

    pub fn list_versions(&self) -> Result<Vec<OsString>> {
        self.create()?;

        let mut versions = Vec::new();

        for e in read_dir(self.get_bin_dir())? {
            let entry = e?;

            if entry.file_type()?.is_dir() {
                versions.push(entry.file_name());
            }
        }

        Ok(versions)
    }

    pub fn prune(&self) -> Result<()> {
        let mut versions: HashMap<(u64, u64), _> = HashMap::new();

        for e in read_dir(self.get_bin_dir())? {
            let entry = e?;

            // Skip unless the entry is a directory.
            if !entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {
                continue;
            }

            let version = match Version::parse(entry.file_name().to_string_lossy().as_ref()) {
                Ok(v) => v,
                Err(_) => continue,
            };

            versions
                .entry((version.major, version.minor))
                .or_insert_with(BinaryHeap::new)
                .push(version);
        }

        for (_, vs) in versions {
            self.prune_versions(vs)?;
        }

        Ok(())
    }

    fn prune_versions(&self, mut versions: BinaryHeap<Version>) -> Result<()> {
        while let Some(version) = versions.pop() {
            if version.build.is_empty() && version.pre.is_empty() {
                break;
            }
        }

        for version in versions {
            self.delete_mongodb_version(&format!("{}", version))?;
        }

        Ok(())
    }

    pub fn exec<I, S>(&self, binary_name: &str, args: I, version: &str) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        exec_command(
            &format!("./{}", binary_name),
            args,
            Some(self.get_version_bin_dir(version)),
        )
    }
}

#[derive(Debug, Default)]
pub struct FsBuilder {
    home_dir: Option<String>,
    bin_dir: Option<String>,
    db_dir: Option<String>,
}

impl FsBuilder {
    #[allow(dead_code)]
    pub fn with_home_dir(&mut self, home_dir: &str) -> &mut Self {
        self.home_dir = Some(home_dir.into());
        self
    }

    #[allow(dead_code)]
    pub fn with_bin_dir(&mut self, bin_dir: &str) -> &mut Self {
        self.bin_dir = Some(bin_dir.into());
        self
    }

    #[allow(dead_code)]
    pub fn with_db_dir(&mut self, db_dir: &str) -> &mut Self {
        self.db_dir = Some(db_dir.into());
        self
    }

    pub fn build(self) -> Result<Fs> {
        match home_dir() {
            Some(mut home_dir) => {
                home_dir.push(self.home_dir.unwrap_or_else(|| DEFAULT_HOME_DIR.into()));

                let bin_dir = Path::new(&self.bin_dir.unwrap_or_else(|| DEFAULT_BIN_DIR.into()))
                    .to_path_buf();

                let db_dir = Path::new(&self.db_dir.unwrap_or_else(|| DEFAULT_DB_DIR.into()))
                    .to_path_buf();

                Ok(Fs {
                    home_dir,
                    bin_dir,
                    db_dir,
                })
            }
            None => bail!(ErrorKind::UnknownHomeDirectory),
        }
    }
}
<commit_msg>add log messages for which versions were pruned<commit_after>use std::collections::{BinaryHeap, HashMap};
use std::env::home_dir;
use std::fs::{create_dir_all, OpenOptions, read_dir, remove_dir_all, remove_file, rename};
use std::io::Write;
use std::ffi::{OsStr, OsString};
use std::path::{Path, PathBuf};

use semver::Version;

use error::{ErrorKind, Result};
use process::{exec_command, run_command};

const DEFAULT_HOME_DIR: &str = ".monger";
const DEFAULT_BIN_DIR: &str = "mongodb-versions";
const DEFAULT_DB_DIR: &str = "db";

#[derive(Debug)]
pub struct Fs {
    home_dir: PathBuf,
    bin_dir: PathBuf,
    db_dir: PathBuf,
}

impl Fs {
    pub fn new() -> FsBuilder {
        Default::default()
    }

    #[inline]
    fn get_file<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.home_dir.join(filename)
    }

    #[inline]
    fn get_bin_file_rel<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.bin_dir.join(filename)
    }

    #[inline]
    fn get_bin_file_abs<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.get_file(self.get_bin_file_rel(filename))
    }

    #[inline]
    fn get_bin_dir(&self) -> PathBuf {
        self.get_file(self.bin_dir.as_path())
    }

    #[inline]
    fn get_db_file_rel<P: AsRef<Path>>(&self, filename: P) -> PathBuf {
        self.db_dir.join(filename)
    }

    #[inline]
    fn get_version_dir(&self, version: &str) -> PathBuf {
        self.get_bin_file_abs(version)
    }

    #[inline]
    fn get_version_bin_dir<P: AsRef<Path>>(&self, version: P) -> PathBuf {
        self.get_bin_file_abs(version.as_ref().join("bin"))
    }

    fn create(&self) -> Result<()> {
        create_dir_all(self.get_file(self.bin_dir.as_path()).as_path())?;
        create_dir_all(self.get_file(self.db_dir.as_path()).as_path())?;

        Ok(())
    }

    fn decompress_download<P: AsRef<Path>>(
        &self,
        filename: P,
        dirname: P,
        version: P,
    ) -> Result<()> {
        run_command(
            "tar",
            vec!["xf".as_ref(), filename.as_ref().as_os_str()],
            self.get_bin_dir(),
        )?;

        let old_name = self.get_bin_file_abs(dirname);
        let new_name = self.get_bin_file_abs(version);
        rename(old_name, new_name)?;

        Ok(())
    }

    fn delete_file<P: AsRef<Path>>(&self, filename: P) -> Result<()> {
        let path = self.home_dir.join(filename);

        if !path.exists() {
            return Ok(());
        }

        remove_file(path)?;

        Ok(())
    }

    fn delete_directory<P: AsRef<Path>>(&self, dirname: P) -> Result<bool> {
        let path = self.home_dir.join(dirname);

        if !path.exists() {
            return Ok(false);
        }

        remove_dir_all(path)?;

        Ok(true)
    }

    fn write_file<P: AsRef<Path>>(&self, filename: P, bytes: &[u8]) -> Result<()> {
        self.create()?;

        let filepath = self.home_dir.join(filename);
        let mut file = OpenOptions::new().write(true).create(true).open(filepath)?;
        file.write_all(bytes)?;

        Ok(())
    }

    #[inline]
    pub fn version_exists(&self, version: &str) -> bool {
        self.get_version_dir(version).is_dir()
    }

    pub fn create_or_get_db_dir(&self, version: &str) -> Result<PathBuf> {
        let db_dir = self.get_file(self.get_db_file_rel(version));
        create_dir_all(db_dir.as_path())?;
        Ok(db_dir)
    }

    pub fn delete_mongodb_version(&self, version: &str) -> Result<bool> {
        self.delete_directory(self.get_version_dir(version))
    }

    pub fn write_mongodb_download(
        &self,
        filename: &str,
        dirname: &str,
        bytes: &[u8],
        version: &str,
    ) -> Result<()> {
        let bin_file = self.get_bin_file_rel(filename);

        println!("writing {}...", bin_file.display());
        self.write_file(&bin_file, bytes)?;

        println!("decompressing...");
        self.decompress_download(filename, dirname, version)?;

        println!("cleaning up...");
        self.delete_file(&bin_file)?;

        Ok(())
    }

    pub fn list_versions(&self) -> Result<Vec<OsString>> {
        self.create()?;

        let mut versions = Vec::new();

        for e in read_dir(self.get_bin_dir())? {
            let entry = e?;

            if entry.file_type()?.is_dir() {
                versions.push(entry.file_name());
            }
        }

        Ok(versions)
    }

    pub fn prune(&self) -> Result<()> {
        let mut versions: HashMap<(u64, u64), _> = HashMap::new();

        for e in read_dir(self.get_bin_dir())? {
            let entry = e?;

            // Skip unless the entry is a directory.
            if !entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {
                continue;
            }

            let version = match Version::parse(entry.file_name().to_string_lossy().as_ref()) {
                Ok(v) => v,
                Err(_) => continue,
            };

            versions
                .entry((version.major, version.minor))
                .or_insert_with(BinaryHeap::new)
                .push(version);
        }

        for (_, vs) in versions {
            self.prune_versions(vs)?;
        }

        Ok(())
    }

    fn prune_versions(&self, mut versions: BinaryHeap<Version>) -> Result<()> {
        let latest_stable = loop {
            match versions.pop() {
                Some(version) => {
                    if version.build.is_empty() && version.pre.is_empty() {
                        break version;
                    }
                }
                None => return Ok(())
            }
        };

        for version in versions {
            self.delete_mongodb_version(&format!("{}", version))?;
            println!("Deleted {} (because {} is installed)", version, latest_stable);
        }

        Ok(())
    }

    pub fn exec<I, S>(&self, binary_name: &str, args: I, version: &str) -> Result<()>
    where
        I: IntoIterator<Item = S>,
        S: AsRef<OsStr>,
    {
        exec_command(
            &format!("./{}", binary_name),
            args,
            Some(self.get_version_bin_dir(version)),
        )
    }
}

#[derive(Debug, Default)]
pub struct FsBuilder {
    home_dir: Option<String>,
    bin_dir: Option<String>,
    db_dir: Option<String>,
}

impl FsBuilder {
    #[allow(dead_code)]
    pub fn with_home_dir(&mut self, home_dir: &str) -> &mut Self {
        self.home_dir = Some(home_dir.into());
        self
    }

    #[allow(dead_code)]
    pub fn with_bin_dir(&mut self, bin_dir: &str) -> &mut Self {
        self.bin_dir = Some(bin_dir.into());
        self
    }

    #[allow(dead_code)]
    pub fn with_db_dir(&mut self, db_dir: &str) -> &mut Self {
        self.db_dir = Some(db_dir.into());
        self
    }

    pub fn build(self) -> Result<Fs> {
        match home_dir() {
            Some(mut home_dir) => {
                home_dir.push(self.home_dir.unwrap_or_else(|| DEFAULT_HOME_DIR.into()));

                let bin_dir = Path::new(&self.bin_dir.unwrap_or_else(|| DEFAULT_BIN_DIR.into()))
                    .to_path_buf();

                let db_dir = Path::new(&self.db_dir.unwrap_or_else(|| DEFAULT_DB_DIR.into()))
                    .to_path_buf();

                Ok(Fs {
                    home_dir,
                    bin_dir,
                    db_dir,
                })
            }
            None => bail!(ErrorKind::UnknownHomeDirectory),
        }
    }
}
<|endoftext|>