<commit_before>
use std::env;
// use test_tools::dependencies::*;

#[ test ]
#[ rustversion::stable ]
// #[ cfg( not( feature = "nightly" ) ) ]
fn trybuild_tests()
{
  println!( "current_dir : {:?}", env::current_dir().unwrap() );
}

// stable have different information about error
// that's why these tests are active only for nightly
#[ test ]
// #[ cfg( feature = "nightly" ) ]
#[ rustversion::nightly ]
fn trybuild_tests()
{
  println!( "current_dir : {:?}", env::current_dir().unwrap() );
  let t = trybuild::TestCases::new();
  t.compile_fail( "../../../rust/test/former/all/bad_attr.rs" );
  t.compile_fail( "../../../rust/test/former/all/vector_without_parameter.rs" );
  t.compile_fail( "../../../rust/test/former/all/hashmap_without_parameter.rs" );
}

/* zzz : use mod_at */

mod basic_runtime
{
  #[cfg( not( feature = "in_wtools" ) )]
  use meta_tools::*;

  #[cfg( feature = "in_wtools" )]
  use wtools::*;
  #[cfg( not( feature = "in_wtools" ) )]
  mod former
  {
    pub use former_runtime as runtime;
  }

  include!( "./all/basic_runtime_common.rs" );
}

/* zzz : introduce file all.rs */

mod basic { include!( "./all/basic.rs" ); }
mod conflict { include!( "./all/conflict.rs" ); }
mod string_slice_runtime { include!( "./all/string_slice_runtime.rs" ); }
mod string_slice { include!( "./all/string_slice.rs" ); }

mod default_user_type { include!( "./all/default_user_type.rs" ); }
mod default_primitive { include!( "./all/default_primitive.rs" ); }
mod default_container { include!( "./all/default_container.rs" ); }
mod after { include!( "./all/perform.rs" ); }
<commit_msg>Fix test `front_test` of module `former`, enable dependency `trybuild`<commit_after>
use std::env;
use test_tools::dependencies::trybuild;

#[ test ]
#[ rustversion::stable ]
// #[ cfg( not( feature = "nightly" ) ) ]
fn trybuild_tests()
{
  println!( "current_dir : {:?}", env::current_dir().unwrap() );
}

// stable have different information about error
// that's why these tests are active only for nightly
#[ test ]
// #[ cfg( feature = "nightly" ) ]
#[ rustversion::nightly ]
fn trybuild_tests()
{
  println!( "current_dir : {:?}", env::current_dir().unwrap() );
  let t = trybuild::TestCases::new();
  t.compile_fail( "../../../rust/test/former/all/bad_attr.rs" );
  t.compile_fail( "../../../rust/test/former/all/vector_without_parameter.rs" );
  t.compile_fail( "../../../rust/test/former/all/hashmap_without_parameter.rs" );
}

/* zzz : use mod_at */

mod basic_runtime
{
  #[cfg( not( feature = "in_wtools" ) )]
  use meta_tools::*;

  #[cfg( feature = "in_wtools" )]
  use wtools::*;
  #[cfg( not( feature = "in_wtools" ) )]
  mod former
  {
    pub use former_runtime as runtime;
  }

  include!( "./all/basic_runtime_common.rs" );
}

/* zzz : introduce file all.rs */

mod basic { include!( "./all/basic.rs" ); }
mod conflict { include!( "./all/conflict.rs" ); }
mod string_slice_runtime { include!( "./all/string_slice_runtime.rs" ); }
mod string_slice { include!( "./all/string_slice.rs" ); }

mod default_user_type { include!( "./all/default_user_type.rs" ); }
mod default_primitive { include!( "./all/default_primitive.rs" ); }
mod default_container { include!( "./all/default_container.rs" ); }
mod after { include!( "./all/perform.rs" ); }
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Complex numbers.

use core::num::{Zero,One,ToStrRadix};
use core::prelude::*;

// FIXME #1284: handle complex NaN & infinity etc. This
// probably doesn't map to C's _Complex correctly.

// XXX: Need generic sqrt to implement .norm(). Need generic sin/cos
// for .to/from_polar().

/// A complex number in Cartesian form.
#[deriving(Eq,Clone)]
pub struct Cmplx<T> {
    re: T,
    im: T
}

pub type Complex = Cmplx<float>;
pub type Complex32 = Cmplx<f32>;
pub type Complex64 = Cmplx<f64>;

impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Cmplx<T> {
    /// Create a new Cmplx
    #[inline]
    pub fn new(re: T, im: T) -> Cmplx<T> {
        Cmplx { re: re, im: im }
    }

    /**
    Returns the square of the norm (since `T` doesn't necessarily
    have a sqrt function), i.e. `re^2 + im^2`.
    */
    #[inline]
    pub fn norm_sqr(&self) -> T {
        self.re * self.re + self.im * self.im
    }

    /// Returns the complex conjugate. i.e. `re - i im`
    #[inline]
    pub fn conj(&self) -> Cmplx<T> {
        Cmplx::new(self.re, -self.im)
    }

    /// Multiplies `self` by the scalar `t`.
    #[inline]
    pub fn scale(&self, t: T) -> Cmplx<T> {
        Cmplx::new(self.re * t, self.im * t)
    }

    /// Divides `self` by the scalar `t`.
    #[inline]
    pub fn unscale(&self, t: T) -> Cmplx<T> {
        Cmplx::new(self.re / t, self.im / t)
    }

    /// Returns `1/self`
    #[inline]
    pub fn inv(&self) -> Cmplx<T> {
        let norm_sqr = self.norm_sqr();
        Cmplx::new(self.re / norm_sqr,
                    -self.im / norm_sqr)

    }
}

/* arithmetic */
// (a + i b) + (c + i d) == (a + c) + i (b + d)
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Add<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn add(&self, other: &Cmplx<T>) -> Cmplx<T> {
        Cmplx::new(self.re + other.re, self.im + other.im)
    }
}
// (a + i b) - (c + i d) == (a - c) + i (b - d)
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Sub<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn sub(&self, other: &Cmplx<T>) -> Cmplx<T> {
        Cmplx::new(self.re - other.re, self.im - other.im)
    }
}
// (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c)
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Mul<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn mul(&self, other: &Cmplx<T>) -> Cmplx<T> {
        Cmplx::new(self.re*other.re - self.im*other.im,
                     self.re*other.im + self.im*other.re)
    }
}

// (a + i b) / (c + i d) == [(a + i b) * (c - i d)] / (c*c + d*d)
//   == [(a*c + b*d) / (c*c + d*d)] + i [(b*c - a*d) / (c*c + d*d)]
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Div<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn div(&self, other: &Cmplx<T>) -> Cmplx<T> {
        let norm_sqr = other.norm_sqr();
        Cmplx::new((self.re*other.re + self.im*other.im) / norm_sqr,
                     (self.im*other.re - self.re*other.im) / norm_sqr)
    }
}

impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Neg<Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn neg(&self) -> Cmplx<T> {
        Cmplx::new(-self.re, -self.im)
    }
}

/* constants */
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T> + Zero>
    Zero for Cmplx<T> {
    #[inline]
    fn zero() -> Cmplx<T> {
        Cmplx::new(Zero::zero(), Zero::zero())
    }
}

impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T> + Zero + One>
    One for Cmplx<T> {
    #[inline]
    fn one() -> Cmplx<T> {
        Cmplx::new(One::one(), Zero::zero())
    }
}

/* string conversions */
impl<T: ToStr + Zero + Ord + Neg<T>> ToStr for Cmplx<T> {
    fn to_str(&self) -> ~str {
        if self.im < Zero::zero() {
            fmt!("%s-%si", self.re.to_str(), (-self.im).to_str())
        } else {
            fmt!("%s+%si", self.re.to_str(), self.im.to_str())
        }
    }
}

impl<T: ToStrRadix + Zero + Ord + Neg<T>> ToStrRadix for Cmplx<T> {
    fn to_str_radix(&self, radix: uint) -> ~str {
        if self.im < Zero::zero() {
            fmt!("%s-%si", self.re.to_str_radix(radix), (-self.im).to_str_radix(radix))
        } else {
            fmt!("%s+%si", self.re.to_str_radix(radix), self.im.to_str_radix(radix))
        }
    }
}

#[cfg(test)]
mod test {
    use core::prelude::*;
    use super::*;
    use core::num::{Zero,One};

    pub static _0_0i : Complex = Cmplx { re: 0f, im: 0f };
    pub static _1_0i : Complex = Cmplx { re: 1f, im: 0f };
    pub static _1_1i : Complex = Cmplx { re: 1f, im: 1f };
    pub static _0_1i : Complex = Cmplx { re: 0f, im: 1f };
    pub static _neg1_1i : Complex = Cmplx { re: -1f, im: 1f };
    pub static _05_05i : Complex = Cmplx { re: 0.5f, im: 0.5f };
    pub static all_consts : [Complex, .. 5] = [_0_0i, _1_0i, _1_1i, _neg1_1i, _05_05i];

    #[test]
    fn test_consts() {
        // check our constants are what Cmplx::new creates
        fn test(c : Complex, r : float, i: float) {
            assert_eq!(c, Cmplx::new(r,i));
        }
        test(_0_0i, 0f, 0f);
        test(_1_0i, 1f, 0f);
        test(_1_1i, 1f, 1f);
        test(_neg1_1i, -1f, 1f);
        test(_05_05i, 0.5f, 0.5f);

        assert_eq!(_0_0i, Zero::zero());
        assert_eq!(_1_0i, One::one());
    }

    #[test]
    fn test_norm_sqr() {
        fn test(c: Complex, ns: float) {
            assert_eq!(c.norm_sqr(), ns);
        }
        test(_0_0i, 0f);
        test(_1_0i, 1f);
        test(_1_1i, 2f);
        test(_neg1_1i, 2f);
        test(_05_05i, 0.5f);
    }

    #[test]
    fn test_scale_unscale() {
        assert_eq!(_05_05i.scale(2f), _1_1i);
        assert_eq!(_1_1i.unscale(2f), _05_05i);
        for all_consts.each |&c| {
            assert_eq!(c.scale(2f).unscale(2f), c);
        }
    }

    #[test]
    fn test_conj() {
        for all_consts.each |&c| {
            assert_eq!(c.conj(), Cmplx::new(c.re, -c.im));
            assert_eq!(c.conj().conj(), c);
        }
    }

    #[test]
    fn test_inv() {
        assert_eq!(_1_1i.inv(), _05_05i.conj());
        assert_eq!(_1_0i.inv(), _1_0i.inv());
    }

    #[test]
    #[should_fail]
    #[ignore]
    fn test_inv_zero() {
        // XXX: should this really fail, or just NaN?
        _0_0i.inv();
    }

    mod arith {
        use super::*;
        use super::super::*;
        use core::num::Zero;

        #[test]
        fn test_add() {
            assert_eq!(_05_05i + _05_05i, _1_1i);
            assert_eq!(_0_1i + _1_0i, _1_1i);
            assert_eq!(_1_0i + _neg1_1i, _0_1i);

            for all_consts.each |&c| {
                assert_eq!(_0_0i + c, c);
                assert_eq!(c + _0_0i, c);
            }
        }

        #[test]
        fn test_sub() {
            assert_eq!(_05_05i - _05_05i, _0_0i);
            assert_eq!(_0_1i - _1_0i, _neg1_1i);
            assert_eq!(_0_1i - _neg1_1i, _1_0i);

            for all_consts.each |&c| {
                assert_eq!(c - _0_0i, c);
                assert_eq!(c - c, _0_0i);
            }
        }

        #[test]
        fn test_mul() {
            assert_eq!(_05_05i * _05_05i, _0_1i.unscale(2f));
            assert_eq!(_1_1i * _0_1i, _neg1_1i);

            // i^2 & i^4
            assert_eq!(_0_1i * _0_1i, -_1_0i);
            assert_eq!(_0_1i * _0_1i * _0_1i * _0_1i, _1_0i);

            for all_consts.each |&c| {
                assert_eq!(c * _1_0i, c);
                assert_eq!(_1_0i * c, c);
            }
        }
        #[test]
        fn test_div() {
            assert_eq!(_neg1_1i / _0_1i, _1_1i);
            for all_consts.each |&c| {
                if c != Zero::zero() {
                    assert_eq!(c / c, _1_0i);
                }
            }
        }
        #[test]
        fn test_neg() {
            assert_eq!(-_1_0i + _0_1i, _neg1_1i);
            assert_eq!((-_0_1i) * _0_1i, _1_0i);
            for all_consts.each |&c| {
                assert_eq!(-(-c), c);
            }
        }
    }

    #[test]
    fn test_to_str() {
        fn test(c : Complex, s: ~str) {
            assert_eq!(c.to_str(), s);
        }
        test(_0_0i, ~"0+0i");
        test(_1_0i, ~"1+0i");
        test(_0_1i, ~"0+1i");
        test(_1_1i, ~"1+1i");
        test(_neg1_1i, ~"-1+1i");
        test(-_neg1_1i, ~"1-1i");
        test(_05_05i, ~"0.5+0.5i");
    }
}
<commit_msg>libstd: make complex.rs XXX's into issues and FIXME's<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Complex numbers.

use core::num::{Zero,One,ToStrRadix};
use core::prelude::*;

// FIXME #1284: handle complex NaN & infinity etc. This
// probably doesn't map to C's _Complex correctly.

// FIXME #5734:: Need generic sin/cos for .to/from_polar().
// FIXME #5735: Need generic sqrt to implement .norm().

/// A complex number in Cartesian form.
#[deriving(Eq,Clone)]
pub struct Cmplx<T> {
    re: T,
    im: T
}

pub type Complex = Cmplx<float>;
pub type Complex32 = Cmplx<f32>;
pub type Complex64 = Cmplx<f64>;

impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Cmplx<T> {
    /// Create a new Cmplx
    #[inline]
    pub fn new(re: T, im: T) -> Cmplx<T> {
        Cmplx { re: re, im: im }
    }

    /**
    Returns the square of the norm (since `T` doesn't necessarily
    have a sqrt function), i.e. `re^2 + im^2`.
    */
    #[inline]
    pub fn norm_sqr(&self) -> T {
        self.re * self.re + self.im * self.im
    }

    /// Returns the complex conjugate. i.e. `re - i im`
    #[inline]
    pub fn conj(&self) -> Cmplx<T> {
        Cmplx::new(self.re, -self.im)
    }

    /// Multiplies `self` by the scalar `t`.
    #[inline]
    pub fn scale(&self, t: T) -> Cmplx<T> {
        Cmplx::new(self.re * t, self.im * t)
    }

    /// Divides `self` by the scalar `t`.
    #[inline]
    pub fn unscale(&self, t: T) -> Cmplx<T> {
        Cmplx::new(self.re / t, self.im / t)
    }

    /// Returns `1/self`
    #[inline]
    pub fn inv(&self) -> Cmplx<T> {
        let norm_sqr = self.norm_sqr();
        Cmplx::new(self.re / norm_sqr,
                    -self.im / norm_sqr)
    }
}

/* arithmetic */
// (a + i b) + (c + i d) == (a + c) + i (b + d)
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Add<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn add(&self, other: &Cmplx<T>) -> Cmplx<T> {
        Cmplx::new(self.re + other.re, self.im + other.im)
    }
}
// (a + i b) - (c + i d) == (a - c) + i (b - d)
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Sub<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn sub(&self, other: &Cmplx<T>) -> Cmplx<T> {
        Cmplx::new(self.re - other.re, self.im - other.im)
    }
}
// (a + i b) * (c + i d) == (a*c - b*d) + i (a*d + b*c)
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Mul<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn mul(&self, other: &Cmplx<T>) -> Cmplx<T> {
        Cmplx::new(self.re*other.re - self.im*other.im,
                     self.re*other.im + self.im*other.re)
    }
}

// (a + i b) / (c + i d) == [(a + i b) * (c - i d)] / (c*c + d*d)
//   == [(a*c + b*d) / (c*c + d*d)] + i [(b*c - a*d) / (c*c + d*d)]
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Div<Cmplx<T>, Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn div(&self, other: &Cmplx<T>) -> Cmplx<T> {
        let norm_sqr = other.norm_sqr();
        Cmplx::new((self.re*other.re + self.im*other.im) / norm_sqr,
                     (self.im*other.re - self.re*other.im) / norm_sqr)
    }
}

impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T>>
    Neg<Cmplx<T>> for Cmplx<T> {
    #[inline]
    fn neg(&self) -> Cmplx<T> {
        Cmplx::new(-self.re, -self.im)
    }
}

/* constants */
impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T> + Zero>
    Zero for Cmplx<T> {
    #[inline]
    fn zero() -> Cmplx<T> {
        Cmplx::new(Zero::zero(), Zero::zero())
    }
}

impl<T: Copy + Add<T,T> + Sub<T,T> + Mul<T,T> + Div<T,T> + Neg<T> + Zero + One>
    One for Cmplx<T> {
    #[inline]
    fn one() -> Cmplx<T> {
        Cmplx::new(One::one(), Zero::zero())
    }
}

/* string conversions */
impl<T: ToStr + Zero + Ord + Neg<T>> ToStr for Cmplx<T> {
    fn to_str(&self) -> ~str {
        if self.im < Zero::zero() {
            fmt!("%s-%si", self.re.to_str(), (-self.im).to_str())
        } else {
            fmt!("%s+%si", self.re.to_str(), self.im.to_str())
        }
    }
}

impl<T: ToStrRadix + Zero + Ord + Neg<T>> ToStrRadix for Cmplx<T> {
    fn to_str_radix(&self, radix: uint) -> ~str {
        if self.im < Zero::zero() {
            fmt!("%s-%si", self.re.to_str_radix(radix), (-self.im).to_str_radix(radix))
        } else {
            fmt!("%s+%si", self.re.to_str_radix(radix), self.im.to_str_radix(radix))
        }
    }
}

#[cfg(test)]
mod test {
    use core::prelude::*;
    use super::*;
    use core::num::{Zero,One};

    pub static _0_0i : Complex = Cmplx { re: 0f, im: 0f };
    pub static _1_0i : Complex = Cmplx { re: 1f, im: 0f };
    pub static _1_1i : Complex = Cmplx { re: 1f, im: 1f };
    pub static _0_1i : Complex = Cmplx { re: 0f, im: 1f };
    pub static _neg1_1i : Complex = Cmplx { re: -1f, im: 1f };
    pub static _05_05i : Complex = Cmplx { re: 0.5f, im: 0.5f };
    pub static all_consts : [Complex, .. 5] = [_0_0i, _1_0i, _1_1i, _neg1_1i, _05_05i];

    #[test]
    fn test_consts() {
        // check our constants are what Cmplx::new creates
        fn test(c : Complex, r : float, i: float) {
            assert_eq!(c, Cmplx::new(r,i));
        }
        test(_0_0i, 0f, 0f);
        test(_1_0i, 1f, 0f);
        test(_1_1i, 1f, 1f);
        test(_neg1_1i, -1f, 1f);
        test(_05_05i, 0.5f, 0.5f);

        assert_eq!(_0_0i, Zero::zero());
        assert_eq!(_1_0i, One::one());
    }

    #[test]
    fn test_norm_sqr() {
        fn test(c: Complex, ns: float) {
            assert_eq!(c.norm_sqr(), ns);
        }
        test(_0_0i, 0f);
        test(_1_0i, 1f);
        test(_1_1i, 2f);
        test(_neg1_1i, 2f);
        test(_05_05i, 0.5f);
    }

    #[test]
    fn test_scale_unscale() {
        assert_eq!(_05_05i.scale(2f), _1_1i);
        assert_eq!(_1_1i.unscale(2f), _05_05i);
        for all_consts.each |&c| {
            assert_eq!(c.scale(2f).unscale(2f), c);
        }
    }

    #[test]
    fn test_conj() {
        for all_consts.each |&c| {
            assert_eq!(c.conj(), Cmplx::new(c.re, -c.im));
            assert_eq!(c.conj().conj(), c);
        }
    }

    #[test]
    fn test_inv() {
        assert_eq!(_1_1i.inv(), _05_05i.conj());
        assert_eq!(_1_0i.inv(), _1_0i.inv());
    }

    #[test]
    #[should_fail]
    #[ignore]
    fn test_inv_zero() {
        // FIXME #5736: should this really fail, or just NaN?
        _0_0i.inv();
    }

    mod arith {
        use super::*;
        use super::super::*;
        use core::num::Zero;

        #[test]
        fn test_add() {
            assert_eq!(_05_05i + _05_05i, _1_1i);
            assert_eq!(_0_1i + _1_0i, _1_1i);
            assert_eq!(_1_0i + _neg1_1i, _0_1i);

            for all_consts.each |&c| {
                assert_eq!(_0_0i + c, c);
                assert_eq!(c + _0_0i, c);
            }
        }

        #[test]
        fn test_sub() {
            assert_eq!(_05_05i - _05_05i, _0_0i);
            assert_eq!(_0_1i - _1_0i, _neg1_1i);
            assert_eq!(_0_1i - _neg1_1i, _1_0i);

            for all_consts.each |&c| {
                assert_eq!(c - _0_0i, c);
                assert_eq!(c - c, _0_0i);
            }
        }

        #[test]
        fn test_mul() {
            assert_eq!(_05_05i * _05_05i, _0_1i.unscale(2f));
            assert_eq!(_1_1i * _0_1i, _neg1_1i);

            // i^2 & i^4
            assert_eq!(_0_1i * _0_1i, -_1_0i);
            assert_eq!(_0_1i * _0_1i * _0_1i * _0_1i, _1_0i);

            for all_consts.each |&c| {
                assert_eq!(c * _1_0i, c);
                assert_eq!(_1_0i * c, c);
            }
        }
        #[test]
        fn test_div() {
            assert_eq!(_neg1_1i / _0_1i, _1_1i);
            for all_consts.each |&c| {
                if c != Zero::zero() {
                    assert_eq!(c / c, _1_0i);
                }
            }
        }
        #[test]
        fn test_neg() {
            assert_eq!(-_1_0i + _0_1i, _neg1_1i);
            assert_eq!((-_0_1i) * _0_1i, _1_0i);
            for all_consts.each |&c| {
                assert_eq!(-(-c), c);
            }
        }
    }

    #[test]
    fn test_to_str() {
        fn test(c : Complex, s: ~str) {
            assert_eq!(c.to_str(), s);
        }
        test(_0_0i, ~"0+0i");
        test(_1_0i, ~"1+0i");
        test(_0_1i, ~"0+1i");
        test(_1_1i, ~"1+1i");
        test(_neg1_1i, ~"-1+1i");
        test(-_neg1_1i, ~"1-1i");
        test(_05_05i, ~"0.5+0.5i");
    }
}
<|endoftext|>


<commit_before>// Useful references:
// https://www.reddit.com/r/gamedev/comments/2vlypg/i_made_a_html5_particle_engine/
// https://www.reddit.com/r/gamedev/comments/135w5u/version_five_of_my_2d_particle_system_is_complete/
// https://www.reddit.com/r/gamedev/comments/13ksu3/article_on_particle_systems_and_an_online_cocos2d/
// Unity3D's particle system
// Cocos2d's plist file format
// Oh, love2d's particle system parameters, derp.

extern crate nalgebra as na;

use ggez::{GameResult, Context};
use ggez::graphics;

type Point2 = na::Point2<f64>;
type Vector2 = na::Vector2<f64>;

struct Particle {
    pos: Point2,
    vel: Vector2,
}

/// A trait that defines a way to do some sort of
/// lerp or easing function on a type.
trait Interpable {
    fn interp(&self, t: f64) -> Self;
}

/// A structure that represents a transition between
/// set properties, with multiple potential defined points.
/// So for instance you could use Transition<Color> and define
/// a transition of colors from red to orange to grey to do smoke.
/// You could also use Transition<f64> to just represent a size
/// curve.
/// So really this is a general-purpose easing type thing...
/// It assumes that all time values range from 0 to 1, currently.
/// Though we could fix that just by having or finding some kind of
/// scaling factor... hmmmm.  Nah, that should be external to the
/// transition.
struct Transition<T: Interpable> {
    breakpoints: Vec<(f64, T)>,
}

impl<T: Interpable> Transition<T> {
    /// Add a new breakpoint to the transition
    /// at time 0 < t < 1
    fn add(&mut self, t: f64, val: T) {}
}

// Properties particles should have:
// Age, position, velocity

// Properties particle systems should have:
// color, inc. opacity
// texture (perhaps sprite?), multiplied by color
// size
// gravity
// fade rate/color transitions
// max lifespan
// speed
// xvel, yvel
// shape???
// Gravity???
// Glow???
// x/y bounds (delete particles that go further than this)
// floor and ceiling?  (particles bounce off of these)
//
// Per love2d, which appears to cover all the basics and more:
// area spread (uniform, normal)
// buffer size (number of particles)
// color (of image)
// colors (of non-image particle)
// direction
// emission rate
// emitter lifetime
// image
// insert mode (where particles are inserted; top, bottom, random)
// lifetime
// linear acceleration (general case of gravity)
// linear damping
// particle lifetime (min, max)
// position of emitter
// quads (series of images to use as sprites)
// radial acceeleration
// rotation
// size variations/sizes
// set speed
// spin, spin variation
// spread
// tangential acceleration
//
// Honestly having general purpose "create" and "update" traits
// would abstract out a lot of this, and then we just define
// the basics.

impl Particle {
    fn new(pos: Point2, vel: Vector2) -> Self {
        Particle {
            pos: pos,
            vel: vel,
        }
    }
}

pub struct ParticleSystem {
    particles: Vec<Particle>,
}

impl ParticleSystem {
    pub fn new() -> Self {
        ParticleSystem { particles: Vec::new() }
    }

    pub fn emit(&mut self) {
        let pos = Point2::new(0.0, 0.0);
        let vec = Vector2::new(10.0, 10.0);
        let newparticle = Particle::new(pos, vec);
        self.particles.push(newparticle);
    }

    pub fn update(&mut self, dt: f64) {
        for mut p in self.particles.iter_mut() {
            p.pos += p.vel * dt;
        }
    }
}

impl graphics::Drawable for ParticleSystem {
    fn draw_ex(&self,
               context: &mut Context,
               src: Option<graphics::Rect>,
               dst: Option<graphics::Rect>,
               angle: f64,
               center: Option<graphics::Point>,
               flip_horizontal: bool,
               flip_vertical: bool)
               -> GameResult<()> {
        for p in &self.particles {
            let rect = graphics::Rect::new(p.pos.x as i32, p.pos.y as i32, 5, 5);
            graphics::rectangle(context, graphics::DrawMode::Fill, rect);
        }
        Ok(())
    }
}
<commit_msg>Thoughts on how particles work.<commit_after>// Useful references:
// https://www.reddit.com/r/gamedev/comments/2vlypg/i_made_a_html5_particle_engine/
// https://www.reddit.com/r/gamedev/comments/135w5u/version_five_of_my_2d_particle_system_is_complete/
// https://www.reddit.com/r/gamedev/comments/13ksu3/article_on_particle_systems_and_an_online_cocos2d/
// Unity3D's particle system
// Cocos2d's plist file format
// Oh, love2d's particle system parameters, derp.

use std::marker::Sized;

extern crate nalgebra as na;

use ggez::{GameResult, Context};
use ggez::graphics;

type Point2 = na::Point2<f64>;
type Vector2 = na::Vector2<f64>;

// Properties particles should have:
// Age, position, velocity

// Properties particle systems should have:
// color, inc. opacity
// texture (perhaps sprite?), multiplied by color
// size
// gravity
// fade rate/color transitions
// max lifespan
// speed
// xvel, yvel
// shape???
// Gravity???
// Glow???
// x/y bounds (delete particles that go further than this)
// floor and ceiling?  (particles bounce off of these)
//
// Per love2d, which appears to cover all the basics and more:
// area spread (uniform, normal)
// buffer size (number of particles)
// color (of image)
// colors (of non-image particle)
// direction
// emission rate
// emitter lifetime
// image
// insert mode (where particles are inserted; top, bottom, random)
// lifetime
// linear acceleration (general case of gravity)
// linear damping
// particle lifetime (min, max)
// position of emitter
// quads (series of images to use as sprites)
// radial acceeleration
// rotation
// size variations/sizes
// set speed
// spin, spin variation
// spread
// tangential acceleration
//
// Honestly having general purpose "create" and "update" traits
// would abstract out a lot of this, and then we just define
// the basics.

// Aha.  We have a 2x2 matrix of cases here: A particle can have a property
// that's specific to each particle and calculated from some particle-specific
// state, like position.  It can have a property that's the same for  each particle
// but calculated the same for each particle, like color in a simple flame effect.
// It can have a property that's not specific to each particle and calculated the
// same for each particle, like gravity, or that's not specific to each particle and
// calculated
//
// So our axes are: State per particle vs state per particle system,
// and constant over time vs varying over time.

struct Particle {
    // These are both varying over time and per-particle.
    pos: Point2,
    vel: Vector2,

}

impl Particle {
    fn new(pos: Point2, vel: Vector2) -> Self {
        Particle {
            pos: pos,
            vel: vel,
        }
    }
}

/// A trait that defines a way to do some sort of
/// lerp or easing function on a type.
trait Interpable where Self: Sized {
    /// Interpolate the value.  t should always be a number
    /// between 0.0 and 1.0, normalized for whatever actual
    /// value is the "end" of the interpolation.
    fn interp(&self, t: f64) -> Self;

    /// A little shortcut that does the normalization for you.
    fn normalize_interp(&self, t: f64, max_t: f64) -> Self {
        let norm_t = t / max_t;
        self.interp(norm_t)
    }
}

impl Interpable for f64 {
    fn interp(&self, t: f64) -> Self {
        *self * t
    }
}

/// A structure that represents a transition between
/// set properties, with multiple potential defined points.
/// So for instance you could use Transition<Color> and define
/// a transition of colors from red to orange to grey to do smoke.
/// You could also use Transition<f64> to just represent a size
/// curve.
/// So really this is a general-purpose easing type thing...
/// It assumes that all time values range from 0 to 1, currently.
/// Though we could fix that just by having or finding some kind of
/// scaling factor... hmmmm.  Nah, that should be external to the
/// transition.
struct Transition<T: Interpable> {
    breakpoints: Vec<(f64, T)>,
}

impl<T: Interpable> Transition<T> {
    /// Add a new breakpoint to the transition
    /// at time 0 < t < 1
    fn add(&mut self, t: f64, val: T) {}
}

pub struct ParticleSystem {
    particles: Vec<Particle>,
}

impl ParticleSystem {
    pub fn new() -> Self {
        ParticleSystem { particles: Vec::new() }
    }

    pub fn emit(&mut self) {
        let pos = Point2::new(0.0, 0.0);
        let vec = Vector2::new(10.0, 10.0);
        let newparticle = Particle::new(pos, vec);
        self.particles.push(newparticle);
    }

    pub fn update(&mut self, dt: f64) {
        for mut p in self.particles.iter_mut() {
            p.pos += p.vel * dt;
        }
    }
}

impl graphics::Drawable for ParticleSystem {
    fn draw_ex(&self,
               context: &mut Context,
               src: Option<graphics::Rect>,
               dst: Option<graphics::Rect>,
               angle: f64,
               center: Option<graphics::Point>,
               flip_horizontal: bool,
               flip_vertical: bool)
               -> GameResult<()> {
        for p in &self.particles {
            let rect = graphics::Rect::new(p.pos.x as i32, p.pos.y as i32, 5, 5);
            graphics::rectangle(context, graphics::DrawMode::Fill, rect);
        }
        Ok(())
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

This modules provides bindings to the local event loop's TTY interface, using it
to have synchronous, but non-blocking versions of stdio. These handles can be
inspected for information about terminal dimensions or related information
about the stream or terminal that it is attached to.

# Example

```rust
use std::rt::io;

let mut out = io::stdout();
out.write(bytes!("Hello, world!"));
```

*/

use fmt;
use libc;
use option::{Option, Some, None};
use result::{Ok, Err};
use rt::io::buffered::{LineBufferedWriter, BufferedWriter};
use rt::rtio::{IoFactory, RtioTTY, RtioFileStream, with_local_io,
               CloseAsynchronously};
use super::{Reader, Writer, io_error, IoError, OtherIoError};

// And so begins the tale of acquiring a uv handle to a stdio stream on all
// platforms in all situations. Our story begins by splitting the world into two
// categories, windows and unix. Then one day the creators of unix said let
// there be redirection! And henceforth there was redirection away from the
// console for standard I/O streams.
//
// After this day, the world split into four factions:
//
// 1. Unix with stdout on a terminal.
// 2. Unix with stdout redirected.
// 3. Windows with stdout on a terminal.
// 4. Windows with stdout redirected.
//
// Many years passed, and then one day the nation of libuv decided to unify this
// world. After months of toiling, uv created three ideas: TTY, Pipe, File.
// These three ideas propagated throughout the lands and the four great factions
// decided to settle among them.
//
// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
// doing so, they even enhanced themselves further then their Pipe/File
// brethren, becoming the dominant powers.
//
// The group of 4, however, decided to work independently. They abandoned the
// common TTY belief throughout, and even abandoned the fledgling Pipe belief.
// The members of the 4th faction decided to only align themselves with File.
//
// tl;dr; TTY works on everything but when windows stdout is redirected, in that
//        case pipe also doesn't work, but magically file does!
enum StdSource {
    TTY(~RtioTTY),
    File(~RtioFileStream),
}

#[fixed_stack_segment] #[inline(never)]
fn src<T>(fd: libc::c_int, readable: bool, f: &fn(StdSource) -> T) -> T {
    do with_local_io |io| {
        let fd = unsafe { libc::dup(fd) };
        match io.tty_open(fd, readable) {
            Ok(tty) => Some(f(TTY(tty))),
            Err(_) => {
                // It's not really that desirable if these handles are closed
                // synchronously, and because they're squirreled away in a task
                // structure the destructors will be run when the task is
                // attempted to get destroyed. This means that if we run a
                // synchronous destructor we'll attempt to do some scheduling
                // operations which will just result in sadness.
                Some(f(File(io.fs_from_raw_fd(fd, CloseAsynchronously))))
            }
        }
    }.unwrap()
}

/// Creates a new non-blocking handle to the stdin of the current process.
///
/// See `stdout()` for notes about this function.
#[fixed_stack_segment] #[inline(never)]
pub fn stdin() -> StdReader {
    do src(libc::STDIN_FILENO, true) |src| { StdReader { inner: src } }
}

/// Creates a new non-blocking handle to the stdout of the current process.
///
/// Note that this is a fairly expensive operation in that at least one memory
/// allocation is performed. Additionally, this must be called from a runtime
/// task context because the stream returned will be a non-blocking object using
/// the local scheduler to perform the I/O.
pub fn stdout() -> StdWriter {
    do src(libc::STDOUT_FILENO, false) |src| { StdWriter { inner: src } }
}

/// Creates a new non-blocking handle to the stderr of the current process.
///
/// See `stdout()` for notes about this function.
pub fn stderr() -> StdWriter {
    do src(libc::STDERR_FILENO, false) |src| { StdWriter { inner: src } }
}

// Helper to access the local task's stdout handle
//
// Note that this is not a safe function to expose because you can create an
// aliased pointer very easily:
//
//  do with_task_stdout |io1| {
//      do with_task_stdout |io2| {
//          // io1 aliases io2
//      }
//  }
fn with_task_stdout(f: &fn(&mut Writer)) {
    use rt::local::Local;
    use rt::task::Task;

    unsafe {
        // Logging may require scheduling operations, so we can't remove the
        // task from TLS right now, hence the unsafe borrow. Sad.
        let task: *mut Task = Local::unsafe_borrow();

        match (*task).stdout_handle {
            Some(ref mut handle) => f(*handle),
            None => {
                let handle = stdout();
                let mut handle = if handle.isatty() {
                    ~LineBufferedWriter::new(handle) as ~Writer
                } else {
                    // The default capacity is very large, 64k, but this is just
                    // a stdout stream, and possibly per task, so let's not make
                    // this too expensive.
                    ~BufferedWriter::with_capacity(4096, handle) as ~Writer
                };
                f(handle);
                (*task).stdout_handle = Some(handle);
            }
        }
    }
}

/// Flushes the local task's stdout handle.
///
/// By default, this stream is a buffering stream, flushing may be necessary to
/// ensure output is on the terminal screen. The buffering used is
/// line-buffering when stdout is attached to a terminal, and a fixed sized
/// buffer if it is not attached to a terminal.
///
/// Note that logging macros do not use this stream. Using the logging macros
/// will emit output to stderr, and while they are line buffered the log
/// messages are always terminated in a newline (no need to flush).
pub fn flush() {
    do with_task_stdout |io| {
        io.flush();
    }
}

/// Prints a string to the stdout of the current process. No newline is emitted
/// after the string is printed.
pub fn print(s: &str) {
    do with_task_stdout |io| {
        io.write(s.as_bytes());
    }
}

/// Prints a string as a line. to the stdout of the current process. A literal
/// `\n` character is printed to the console after the string.
pub fn println(s: &str) {
    do with_task_stdout |io| {
        io.write(s.as_bytes());
        io.write(['\n' as u8]);
    }
}

/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
/// with the `format_args!` macro.
pub fn print_args(fmt: &fmt::Arguments) {
    do with_task_stdout |io| {
        fmt::write(io, fmt);
    }
}

/// Similar to `println`, but takes a `fmt::Arguments` structure to be
/// compatible with the `format_args!` macro.
pub fn println_args(fmt: &fmt::Arguments) {
    do with_task_stdout |io| {
        fmt::writeln(io, fmt);
    }
}

/// Representation of a reader of a standard input stream
pub struct StdReader {
    priv inner: StdSource
}

impl Reader for StdReader {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        let ret = match self.inner {
            TTY(ref mut tty) => tty.read(buf),
            File(ref mut file) => file.read(buf).map(|i| i as uint),
        };
        match ret {
            Ok(amt) => Some(amt as uint),
            Err(e) => {
                io_error::cond.raise(e);
                None
            }
        }
    }

    fn eof(&mut self) -> bool { false }
}

/// Representation of a writer to a standard output stream
pub struct StdWriter {
    priv inner: StdSource
}

impl StdWriter {
    /// Gets the size of this output window, if possible. This is typically used
    /// when the writer is attached to something like a terminal, this is used
    /// to fetch the dimensions of the terminal.
    ///
    /// If successful, returns Some((width, height)).
    ///
    /// # Failure
    ///
    /// This function will raise on the `io_error` condition if an error
    /// happens.
    pub fn winsize(&mut self) -> Option<(int, int)> {
        match self.inner {
            TTY(ref mut tty) => {
                match tty.get_winsize() {
                    Ok(p) => Some(p),
                    Err(e) => {
                        io_error::cond.raise(e);
                        None
                    }
                }
            }
            File(*) => {
                io_error::cond.raise(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                });
                None
            }
        }
    }

    /// Controls whether this output stream is a "raw stream" or simply a normal
    /// stream.
    ///
    /// # Failure
    ///
    /// This function will raise on the `io_error` condition if an error
    /// happens.
    pub fn set_raw(&mut self, raw: bool) {
        match self.inner {
            TTY(ref mut tty) => {
                match tty.set_raw(raw) {
                    Ok(()) => {},
                    Err(e) => io_error::cond.raise(e),
                }
            }
            File(*) => {
                io_error::cond.raise(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                });
            }
        }
    }

    /// Returns whether this tream is attached to a TTY instance or not.
    ///
    /// This is similar to libc's isatty() function
    pub fn isatty(&self) -> bool {
        match self.inner {
            TTY(ref tty) => tty.isatty(),
            File(*) => false,
        }
    }
}

impl Writer for StdWriter {
    fn write(&mut self, buf: &[u8]) {
        let ret = match self.inner {
            TTY(ref mut tty) => tty.write(buf),
            File(ref mut file) => file.write(buf),
        };
        match ret {
            Ok(()) => {}
            Err(e) => io_error::cond.raise(e)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn smoke() {
        // Just make sure we can acquire handles
        stdin();
        stdout();
        stderr();
    }
}
<commit_msg>Stop extra buffering when stdout isn't a tty<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

/*!

This modules provides bindings to the local event loop's TTY interface, using it
to have synchronous, but non-blocking versions of stdio. These handles can be
inspected for information about terminal dimensions or related information
about the stream or terminal that it is attached to.

# Example

```rust
use std::rt::io;

let mut out = io::stdout();
out.write(bytes!("Hello, world!"));
```

*/

use fmt;
use libc;
use option::{Option, Some, None};
use result::{Ok, Err};
use rt::io::buffered::LineBufferedWriter;
use rt::rtio::{IoFactory, RtioTTY, RtioFileStream, with_local_io,
               CloseAsynchronously};
use super::{Reader, Writer, io_error, IoError, OtherIoError};

// And so begins the tale of acquiring a uv handle to a stdio stream on all
// platforms in all situations. Our story begins by splitting the world into two
// categories, windows and unix. Then one day the creators of unix said let
// there be redirection! And henceforth there was redirection away from the
// console for standard I/O streams.
//
// After this day, the world split into four factions:
//
// 1. Unix with stdout on a terminal.
// 2. Unix with stdout redirected.
// 3. Windows with stdout on a terminal.
// 4. Windows with stdout redirected.
//
// Many years passed, and then one day the nation of libuv decided to unify this
// world. After months of toiling, uv created three ideas: TTY, Pipe, File.
// These three ideas propagated throughout the lands and the four great factions
// decided to settle among them.
//
// The groups of 1, 2, and 3 all worked very hard towards the idea of TTY. Upon
// doing so, they even enhanced themselves further then their Pipe/File
// brethren, becoming the dominant powers.
//
// The group of 4, however, decided to work independently. They abandoned the
// common TTY belief throughout, and even abandoned the fledgling Pipe belief.
// The members of the 4th faction decided to only align themselves with File.
//
// tl;dr; TTY works on everything but when windows stdout is redirected, in that
//        case pipe also doesn't work, but magically file does!
enum StdSource {
    TTY(~RtioTTY),
    File(~RtioFileStream),
}

#[fixed_stack_segment] #[inline(never)]
fn src<T>(fd: libc::c_int, readable: bool, f: &fn(StdSource) -> T) -> T {
    do with_local_io |io| {
        let fd = unsafe { libc::dup(fd) };
        match io.tty_open(fd, readable) {
            Ok(tty) => Some(f(TTY(tty))),
            Err(_) => {
                // It's not really that desirable if these handles are closed
                // synchronously, and because they're squirreled away in a task
                // structure the destructors will be run when the task is
                // attempted to get destroyed. This means that if we run a
                // synchronous destructor we'll attempt to do some scheduling
                // operations which will just result in sadness.
                Some(f(File(io.fs_from_raw_fd(fd, CloseAsynchronously))))
            }
        }
    }.unwrap()
}

/// Creates a new non-blocking handle to the stdin of the current process.
///
/// See `stdout()` for notes about this function.
#[fixed_stack_segment] #[inline(never)]
pub fn stdin() -> StdReader {
    do src(libc::STDIN_FILENO, true) |src| { StdReader { inner: src } }
}

/// Creates a new non-blocking handle to the stdout of the current process.
///
/// Note that this is a fairly expensive operation in that at least one memory
/// allocation is performed. Additionally, this must be called from a runtime
/// task context because the stream returned will be a non-blocking object using
/// the local scheduler to perform the I/O.
pub fn stdout() -> StdWriter {
    do src(libc::STDOUT_FILENO, false) |src| { StdWriter { inner: src } }
}

/// Creates a new non-blocking handle to the stderr of the current process.
///
/// See `stdout()` for notes about this function.
pub fn stderr() -> StdWriter {
    do src(libc::STDERR_FILENO, false) |src| { StdWriter { inner: src } }
}

// Helper to access the local task's stdout handle
//
// Note that this is not a safe function to expose because you can create an
// aliased pointer very easily:
//
//  do with_task_stdout |io1| {
//      do with_task_stdout |io2| {
//          // io1 aliases io2
//      }
//  }
fn with_task_stdout(f: &fn(&mut Writer)) {
    use rt::local::Local;
    use rt::task::Task;

    unsafe {
        // Logging may require scheduling operations, so we can't remove the
        // task from TLS right now, hence the unsafe borrow. Sad.
        let task: *mut Task = Local::unsafe_borrow();

        match (*task).stdout_handle {
            Some(ref mut handle) => f(*handle),
            None => {
                let handle = stdout();
                let mut handle = ~LineBufferedWriter::new(handle) as ~Writer;
                f(handle);
                (*task).stdout_handle = Some(handle);
            }
        }
    }
}

/// Flushes the local task's stdout handle.
///
/// By default, this stream is a line-buffering stream, so flushing may be
/// necessary to ensure that all output is printed to the screen (if there are
/// no newlines printed).
///
/// Note that logging macros do not use this stream. Using the logging macros
/// will emit output to stderr, and while they are line buffered the log
/// messages are always terminated in a newline (no need to flush).
pub fn flush() {
    do with_task_stdout |io| {
        io.flush();
    }
}

/// Prints a string to the stdout of the current process. No newline is emitted
/// after the string is printed.
pub fn print(s: &str) {
    do with_task_stdout |io| {
        io.write(s.as_bytes());
    }
}

/// Prints a string as a line. to the stdout of the current process. A literal
/// `\n` character is printed to the console after the string.
pub fn println(s: &str) {
    do with_task_stdout |io| {
        io.write(s.as_bytes());
        io.write(['\n' as u8]);
    }
}

/// Similar to `print`, but takes a `fmt::Arguments` structure to be compatible
/// with the `format_args!` macro.
pub fn print_args(fmt: &fmt::Arguments) {
    do with_task_stdout |io| {
        fmt::write(io, fmt);
    }
}

/// Similar to `println`, but takes a `fmt::Arguments` structure to be
/// compatible with the `format_args!` macro.
pub fn println_args(fmt: &fmt::Arguments) {
    do with_task_stdout |io| {
        fmt::writeln(io, fmt);
    }
}

/// Representation of a reader of a standard input stream
pub struct StdReader {
    priv inner: StdSource
}

impl Reader for StdReader {
    fn read(&mut self, buf: &mut [u8]) -> Option<uint> {
        let ret = match self.inner {
            TTY(ref mut tty) => tty.read(buf),
            File(ref mut file) => file.read(buf).map(|i| i as uint),
        };
        match ret {
            Ok(amt) => Some(amt as uint),
            Err(e) => {
                io_error::cond.raise(e);
                None
            }
        }
    }

    fn eof(&mut self) -> bool { false }
}

/// Representation of a writer to a standard output stream
pub struct StdWriter {
    priv inner: StdSource
}

impl StdWriter {
    /// Gets the size of this output window, if possible. This is typically used
    /// when the writer is attached to something like a terminal, this is used
    /// to fetch the dimensions of the terminal.
    ///
    /// If successful, returns Some((width, height)).
    ///
    /// # Failure
    ///
    /// This function will raise on the `io_error` condition if an error
    /// happens.
    pub fn winsize(&mut self) -> Option<(int, int)> {
        match self.inner {
            TTY(ref mut tty) => {
                match tty.get_winsize() {
                    Ok(p) => Some(p),
                    Err(e) => {
                        io_error::cond.raise(e);
                        None
                    }
                }
            }
            File(*) => {
                io_error::cond.raise(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                });
                None
            }
        }
    }

    /// Controls whether this output stream is a "raw stream" or simply a normal
    /// stream.
    ///
    /// # Failure
    ///
    /// This function will raise on the `io_error` condition if an error
    /// happens.
    pub fn set_raw(&mut self, raw: bool) {
        match self.inner {
            TTY(ref mut tty) => {
                match tty.set_raw(raw) {
                    Ok(()) => {},
                    Err(e) => io_error::cond.raise(e),
                }
            }
            File(*) => {
                io_error::cond.raise(IoError {
                    kind: OtherIoError,
                    desc: "stream is not a tty",
                    detail: None,
                });
            }
        }
    }

    /// Returns whether this tream is attached to a TTY instance or not.
    ///
    /// This is similar to libc's isatty() function
    pub fn isatty(&self) -> bool {
        match self.inner {
            TTY(ref tty) => tty.isatty(),
            File(*) => false,
        }
    }
}

impl Writer for StdWriter {
    fn write(&mut self, buf: &[u8]) {
        let ret = match self.inner {
            TTY(ref mut tty) => tty.write(buf),
            File(ref mut file) => file.write(buf),
        };
        match ret {
            Ok(()) => {}
            Err(e) => io_error::cond.raise(e)
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn smoke() {
        // Just make sure we can acquire handles
        stdin();
        stdout();
        stderr();
    }
}
<|endoftext|>


<commit_before>use std::borrow::Borrow;
use std::sync::Arc;

use regex::Regex;
use threadpool::{self, ThreadPool};

use config::Config;
use errors::*;
use git::Git;
use git_clone_manager::GitCloneManager;
use github;
use github::api::{GithubSessionFactory, Session};
use messenger;
use slack::{SlackAttachmentBuilder, SlackRequest};
use worker::{self, WorkSender};

fn clone_and_merge_pull_request(
    github_app: &GithubSessionFactory,
    clone_mgr: &GitCloneManager,
    owner: &str,
    repo: &str,
    pull_request: &github::PullRequest,
    target_branch: &str,
) -> Result<github::PullRequest> {

    let session = github_app.new_session(owner, repo)?;
    let held_clone_dir = clone_mgr.clone(owner, repo)?;
    let clone_dir = held_clone_dir.dir();
    let git = Git::new(session.github_host(), session.github_token(), clone_dir);

    merge_pull_request(&git, &session, owner, repo, pull_request, target_branch)
}

pub fn merge_pull_request(
    git: &Git,
    session: &Session,
    owner: &str,
    repo: &str,
    pull_request: &github::PullRequest,
    target_branch: &str,
) -> Result<github::PullRequest> {
    if !pull_request.is_merged() {
        return Err(format!("Pull Request #{} is not yet merged.", pull_request.number).into());
    }

    let merge_commit_sha;
    if let Some(ref sha) = pull_request.merge_commit_sha {
        merge_commit_sha = sha;
    } else {
        return Err(format!("Pull Request #{} has no merge commit.", pull_request.number).into());
    }

    // strip everything before last slash
    let regex = Regex::new(r".*/").unwrap();
    let pr_branch_name =
        format!("{}-{}", regex.replace(&pull_request.head.ref_name, ""), regex.replace(&target_branch, ""));

    // make sure there isn't already such a branch
    let current_remotes = git.run(&["ls-remote", "--heads"])?;
    if current_remotes.contains(&format!("refs/heads/{}", pr_branch_name)) {
        return Err(format!("PR branch already exists on origin: '{}'", pr_branch_name).into());
    }

    let (title, body, whitespace_mode) = cherry_pick(
        &git,
        &merge_commit_sha,
        &pr_branch_name,
        pull_request.number,
        &target_branch,
        &pull_request.base.ref_name,
    )?;

    git.run(&["push", "origin", &format!("HEAD:{}", pr_branch_name)])?;

    let new_pr = session.create_pull_request(owner, repo, &title, &body, &pr_branch_name, &target_branch)?;

    let mut assignees: Vec<String> = pull_request.assignees.iter().map(|a| a.login().to_string()).collect();
    assignees.retain(|r| r != pull_request.user.login());
    if !assignees.is_empty() {
        session.assign_pull_request(owner, repo, new_pr.number, assignees)?;
    }

    let mut reviewers: Vec<String> = pull_request.all_reviewers().into_iter().map(|a| a.login().to_string()).collect();
    reviewers.retain(|r| r != pull_request.user.login());
    if !reviewers.is_empty() {
        session.request_review(owner, repo, new_pr.number, reviewers)?;
    }

    if whitespace_mode.len() > 0 {
        let msg = format!("Cherry-pick required option `{}`. Please verify correctness.", whitespace_mode);
        if let Err(e) = session.comment_pull_request(owner, repo, new_pr.number, &msg) {
            error!("Error making whitespace comment on pull request: {}", e);
        }
    }

    Ok(new_pr)
}

pub fn cherry_pick(
    git: &Git,
    commit_hash: &str,
    pr_branch_name: &str,
    pr_number: u32,
    target_branch: &str,
    orig_base_branch: &str,
) -> Result<(String, String, String)> {
    git.checkout_branch(pr_branch_name, &format!("origin/{}", target_branch))?;

    let (user, email) = git.get_commit_author(commit_hash)?;
    let email = format!("user.email={}", email);
    let user = format!("user.user={}", user);
    let user_opts = ["-c", &email, "-c", &user];

    // cherry-pick!

    let mut whitespace_mode = "";
    if let Err(e) = do_cherry_pick(git, &commit_hash, &[], &user_opts) {
        info!("Could not cherry-pick normally. Ignoring changed whitespace. {}", e);

        whitespace_mode = "ignore-space-change";
        if let Err(e) = do_cherry_pick(git, &commit_hash, &["-X", whitespace_mode], &user_opts) {
            info!("Could not cherry-pick with `-X {}`. Ignoring all whitespace. {}", whitespace_mode, e);

            whitespace_mode = "ignore-all-space";
            if let Err(e) = do_cherry_pick(git, &commit_hash, &["-X", whitespace_mode], &user_opts) {
                info!("Could not cherry-pick with `-X {}`: {}", whitespace_mode, e);
                return Err(e);
            }
        }
    }

    let desc = git.get_commit_desc(commit_hash)?;
    let (title, body) = make_merge_desc(desc, commit_hash, pr_number, target_branch, orig_base_branch);

    // change commit message
    let mut amend_args = vec![];
    amend_args.extend(user_opts.iter());
    amend_args.extend(["commit", "--amend", "-F", "-"].iter());
    git.run_with_stdin(&amend_args, &format!("{}\n\n{}", &title, &body))?;

    Ok((title, body, whitespace_mode.into()))
}

fn do_cherry_pick(git: &Git, commit_hash: &str, opts: &[&str], user_opts: &[&str]) -> Result<String> {
    git.run(&vec!["reset", "--hard"])?;

    let mut args = vec!["-c", "merge.renameLimit=999999"];
    args.extend(user_opts.iter());
    args.extend(vec!["cherry-pick", "--allow-empty"].iter());

    for i in 0..opts.len() {
        args.push(opts[i]);
    }
    args.push(commit_hash);

    git.run(&args)
}

fn make_merge_desc(
    orig_desc: (String, String),
    commit_hash: &str,
    pr_number: u32,
    target_branch: &str,
    orig_base_branch: &str,
) -> (String, String) {
    // grab original title and strip out the PR number at the end
    let pr_regex = Regex::new(r"(\s*\(#\d+\))+$").unwrap();
    let orig_title = pr_regex.replace(&orig_desc.0, "");
    // strip out 'release' from the prefix to keep titles shorter
    let release_branch_regex = Regex::new(r"^release/").unwrap();
    let title = format!("{}->{}: {}", orig_base_branch, release_branch_regex.replace(target_branch, ""), orig_title);
    let mut body = orig_desc.1;

    if body.len() != 0 {
        body += "\n\n";
    }
    body += format!("(cherry-picked from {}, PR #{})", commit_hash, pr_number).as_str();

    (title, body)
}

#[derive(Debug)]
pub struct PRMergeRequest {
    pub repo: github::Repo,
    pub pull_request: github::PullRequest,
    pub target_branch: String,
}

struct Runner {
    config: Arc<Config>,
    github_app: Arc<GithubSessionFactory>,
    clone_mgr: Arc<GitCloneManager>,
    slack: WorkSender<SlackRequest>,
    thread_pool: ThreadPool,
}

pub fn req(repo: &github::Repo, pull_request: &github::PullRequest, target_branch: &str) -> PRMergeRequest {
    PRMergeRequest {
        repo: repo.clone(),
        pull_request: pull_request.clone(),
        target_branch: target_branch.to_string(),
    }
}

pub fn new_worker(
    max_concurrency: usize,
    config: Arc<Config>,
    github_app: Arc<GithubSessionFactory>,
    clone_mgr: Arc<GitCloneManager>,
    slack: WorkSender<SlackRequest>,
) -> worker::Worker<PRMergeRequest> {
    worker::Worker::new(
        "pr-merge",
        Runner {
            config: config,
            github_app: github_app,
            clone_mgr: clone_mgr.clone(),
            slack: slack,
            thread_pool: threadpool::Builder::new()
                .num_threads(max_concurrency)
                .thread_name("pr-merge".to_string())
                .build(),
        },
    )
}

impl worker::Runner<PRMergeRequest> for Runner {
    fn handle(&self, req: PRMergeRequest) {
        let github_app = self.github_app.clone();
        let clone_mgr = self.clone_mgr.clone();
        let config = self.config.clone();

        let slack = self.slack.clone();

        // launch another thread to do the merge
        self.thread_pool.execute(move || {
            let merge_result = clone_and_merge_pull_request(
                github_app.borrow(),
                &clone_mgr,
                &req.repo.owner.login(),
                &req.repo.name,
                &req.pull_request,
                &req.target_branch,
            );
            if let Err(e) = merge_result {

                let attach = SlackAttachmentBuilder::new(&format!("{}", e))
                    .title(
                        format!("Source PR: #{}: \"{}\"", req.pull_request.number, req.pull_request.title)
                            .as_str(),
                    )
                    .title_link(req.pull_request.html_url.clone())
                    .color("danger")
                    .build();

                let messenger = messenger::new(config, slack);
                messenger.send_to_owner(
                    &format!(
                        "Error creating merge PR from {} to {}",
                        req.pull_request.head.ref_name,
                        req.target_branch
                    ),
                    &vec![attach],
                    &req.pull_request.user,
                    &req.repo,
                );
            }
        });
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_make_merge_desc() {
        let desc = make_merge_desc(
            (String::from("Yay, I made a change (#99)"), String::from("here is more data about it")),
            "abcdef",
            99,
            "release/target_branch",
            "source_branch",
        );

        assert_eq!(desc.0, "source_branch->target_branch: Yay, I made a change");
        assert_eq!(desc.1, "here is more data about it\n\n(cherry-picked from abcdef, PR #99)");
    }

    #[test]
    fn test_make_merge_desc_no_body() {
        let desc = make_merge_desc(
            (String::from("Yay, I made a change (#99)"), String::from("")),
            "abcdef",
            99,
            "release/target_branch",
            "source_branch",
        );

        assert_eq!(desc.0, "source_branch->target_branch: Yay, I made a change");
        assert_eq!(desc.1, "(cherry-picked from abcdef, PR #99)");
    }

    #[test]
    fn test_make_merge_desc_no_release_branch() {
        let desc = make_merge_desc(
            (String::from("Yay, I made a change (#99)"), String::from("")),
            "abcdef",
            99,
            "other_branch",
            "source_branch",
        );

        assert_eq!(desc.0, "source_branch->other_branch: Yay, I made a change");
        assert_eq!(desc.1, "(cherry-picked from abcdef, PR #99)");
    }
}
<commit_msg>Fix typo: user.name, not user.user (#196)<commit_after>use std::borrow::Borrow;
use std::sync::Arc;

use regex::Regex;
use threadpool::{self, ThreadPool};

use config::Config;
use errors::*;
use git::Git;
use git_clone_manager::GitCloneManager;
use github;
use github::api::{GithubSessionFactory, Session};
use messenger;
use slack::{SlackAttachmentBuilder, SlackRequest};
use worker::{self, WorkSender};

fn clone_and_merge_pull_request(
    github_app: &GithubSessionFactory,
    clone_mgr: &GitCloneManager,
    owner: &str,
    repo: &str,
    pull_request: &github::PullRequest,
    target_branch: &str,
) -> Result<github::PullRequest> {

    let session = github_app.new_session(owner, repo)?;
    let held_clone_dir = clone_mgr.clone(owner, repo)?;
    let clone_dir = held_clone_dir.dir();
    let git = Git::new(session.github_host(), session.github_token(), clone_dir);

    merge_pull_request(&git, &session, owner, repo, pull_request, target_branch)
}

pub fn merge_pull_request(
    git: &Git,
    session: &Session,
    owner: &str,
    repo: &str,
    pull_request: &github::PullRequest,
    target_branch: &str,
) -> Result<github::PullRequest> {
    if !pull_request.is_merged() {
        return Err(format!("Pull Request #{} is not yet merged.", pull_request.number).into());
    }

    let merge_commit_sha;
    if let Some(ref sha) = pull_request.merge_commit_sha {
        merge_commit_sha = sha;
    } else {
        return Err(format!("Pull Request #{} has no merge commit.", pull_request.number).into());
    }

    // strip everything before last slash
    let regex = Regex::new(r".*/").unwrap();
    let pr_branch_name =
        format!("{}-{}", regex.replace(&pull_request.head.ref_name, ""), regex.replace(&target_branch, ""));

    // make sure there isn't already such a branch
    let current_remotes = git.run(&["ls-remote", "--heads"])?;
    if current_remotes.contains(&format!("refs/heads/{}", pr_branch_name)) {
        return Err(format!("PR branch already exists on origin: '{}'", pr_branch_name).into());
    }

    let (title, body, whitespace_mode) = cherry_pick(
        &git,
        &merge_commit_sha,
        &pr_branch_name,
        pull_request.number,
        &target_branch,
        &pull_request.base.ref_name,
    )?;

    git.run(&["push", "origin", &format!("HEAD:{}", pr_branch_name)])?;

    let new_pr = session.create_pull_request(owner, repo, &title, &body, &pr_branch_name, &target_branch)?;

    let mut assignees: Vec<String> = pull_request.assignees.iter().map(|a| a.login().to_string()).collect();
    assignees.retain(|r| r != pull_request.user.login());
    if !assignees.is_empty() {
        session.assign_pull_request(owner, repo, new_pr.number, assignees)?;
    }

    let mut reviewers: Vec<String> = pull_request.all_reviewers().into_iter().map(|a| a.login().to_string()).collect();
    reviewers.retain(|r| r != pull_request.user.login());
    if !reviewers.is_empty() {
        session.request_review(owner, repo, new_pr.number, reviewers)?;
    }

    if whitespace_mode.len() > 0 {
        let msg = format!("Cherry-pick required option `{}`. Please verify correctness.", whitespace_mode);
        if let Err(e) = session.comment_pull_request(owner, repo, new_pr.number, &msg) {
            error!("Error making whitespace comment on pull request: {}", e);
        }
    }

    Ok(new_pr)
}

pub fn cherry_pick(
    git: &Git,
    commit_hash: &str,
    pr_branch_name: &str,
    pr_number: u32,
    target_branch: &str,
    orig_base_branch: &str,
) -> Result<(String, String, String)> {
    git.checkout_branch(pr_branch_name, &format!("origin/{}", target_branch))?;

    let (user, email) = git.get_commit_author(commit_hash)?;
    let email = format!("user.email={}", email);
    let user = format!("user.name={}", user);
    let user_opts = ["-c", &email, "-c", &user];

    // cherry-pick!

    let mut whitespace_mode = "";
    if let Err(e) = do_cherry_pick(git, &commit_hash, &[], &user_opts) {
        info!("Could not cherry-pick normally. Ignoring changed whitespace. {}", e);

        whitespace_mode = "ignore-space-change";
        if let Err(e) = do_cherry_pick(git, &commit_hash, &["-X", whitespace_mode], &user_opts) {
            info!("Could not cherry-pick with `-X {}`. Ignoring all whitespace. {}", whitespace_mode, e);

            whitespace_mode = "ignore-all-space";
            if let Err(e) = do_cherry_pick(git, &commit_hash, &["-X", whitespace_mode], &user_opts) {
                info!("Could not cherry-pick with `-X {}`: {}", whitespace_mode, e);
                return Err(e);
            }
        }
    }

    let desc = git.get_commit_desc(commit_hash)?;
    let (title, body) = make_merge_desc(desc, commit_hash, pr_number, target_branch, orig_base_branch);

    // change commit message
    let mut amend_args = vec![];
    amend_args.extend(user_opts.iter());
    amend_args.extend(["commit", "--amend", "-F", "-"].iter());
    git.run_with_stdin(&amend_args, &format!("{}\n\n{}", &title, &body))?;

    Ok((title, body, whitespace_mode.into()))
}

fn do_cherry_pick(git: &Git, commit_hash: &str, opts: &[&str], user_opts: &[&str]) -> Result<String> {
    git.run(&vec!["reset", "--hard"])?;

    let mut args = vec!["-c", "merge.renameLimit=999999"];
    args.extend(user_opts.iter());
    args.extend(vec!["cherry-pick", "--allow-empty"].iter());

    for i in 0..opts.len() {
        args.push(opts[i]);
    }
    args.push(commit_hash);

    git.run(&args)
}

fn make_merge_desc(
    orig_desc: (String, String),
    commit_hash: &str,
    pr_number: u32,
    target_branch: &str,
    orig_base_branch: &str,
) -> (String, String) {
    // grab original title and strip out the PR number at the end
    let pr_regex = Regex::new(r"(\s*\(#\d+\))+$").unwrap();
    let orig_title = pr_regex.replace(&orig_desc.0, "");
    // strip out 'release' from the prefix to keep titles shorter
    let release_branch_regex = Regex::new(r"^release/").unwrap();
    let title = format!("{}->{}: {}", orig_base_branch, release_branch_regex.replace(target_branch, ""), orig_title);
    let mut body = orig_desc.1;

    if body.len() != 0 {
        body += "\n\n";
    }
    body += format!("(cherry-picked from {}, PR #{})", commit_hash, pr_number).as_str();

    (title, body)
}

#[derive(Debug)]
pub struct PRMergeRequest {
    pub repo: github::Repo,
    pub pull_request: github::PullRequest,
    pub target_branch: String,
}

struct Runner {
    config: Arc<Config>,
    github_app: Arc<GithubSessionFactory>,
    clone_mgr: Arc<GitCloneManager>,
    slack: WorkSender<SlackRequest>,
    thread_pool: ThreadPool,
}

pub fn req(repo: &github::Repo, pull_request: &github::PullRequest, target_branch: &str) -> PRMergeRequest {
    PRMergeRequest {
        repo: repo.clone(),
        pull_request: pull_request.clone(),
        target_branch: target_branch.to_string(),
    }
}

pub fn new_worker(
    max_concurrency: usize,
    config: Arc<Config>,
    github_app: Arc<GithubSessionFactory>,
    clone_mgr: Arc<GitCloneManager>,
    slack: WorkSender<SlackRequest>,
) -> worker::Worker<PRMergeRequest> {
    worker::Worker::new(
        "pr-merge",
        Runner {
            config: config,
            github_app: github_app,
            clone_mgr: clone_mgr.clone(),
            slack: slack,
            thread_pool: threadpool::Builder::new()
                .num_threads(max_concurrency)
                .thread_name("pr-merge".to_string())
                .build(),
        },
    )
}

impl worker::Runner<PRMergeRequest> for Runner {
    fn handle(&self, req: PRMergeRequest) {
        let github_app = self.github_app.clone();
        let clone_mgr = self.clone_mgr.clone();
        let config = self.config.clone();

        let slack = self.slack.clone();

        // launch another thread to do the merge
        self.thread_pool.execute(move || {
            let merge_result = clone_and_merge_pull_request(
                github_app.borrow(),
                &clone_mgr,
                &req.repo.owner.login(),
                &req.repo.name,
                &req.pull_request,
                &req.target_branch,
            );
            if let Err(e) = merge_result {

                let attach = SlackAttachmentBuilder::new(&format!("{}", e))
                    .title(
                        format!("Source PR: #{}: \"{}\"", req.pull_request.number, req.pull_request.title)
                            .as_str(),
                    )
                    .title_link(req.pull_request.html_url.clone())
                    .color("danger")
                    .build();

                let messenger = messenger::new(config, slack);
                messenger.send_to_owner(
                    &format!(
                        "Error creating merge PR from {} to {}",
                        req.pull_request.head.ref_name,
                        req.target_branch
                    ),
                    &vec![attach],
                    &req.pull_request.user,
                    &req.repo,
                );
            }
        });
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_make_merge_desc() {
        let desc = make_merge_desc(
            (String::from("Yay, I made a change (#99)"), String::from("here is more data about it")),
            "abcdef",
            99,
            "release/target_branch",
            "source_branch",
        );

        assert_eq!(desc.0, "source_branch->target_branch: Yay, I made a change");
        assert_eq!(desc.1, "here is more data about it\n\n(cherry-picked from abcdef, PR #99)");
    }

    #[test]
    fn test_make_merge_desc_no_body() {
        let desc = make_merge_desc(
            (String::from("Yay, I made a change (#99)"), String::from("")),
            "abcdef",
            99,
            "release/target_branch",
            "source_branch",
        );

        assert_eq!(desc.0, "source_branch->target_branch: Yay, I made a change");
        assert_eq!(desc.1, "(cherry-picked from abcdef, PR #99)");
    }

    #[test]
    fn test_make_merge_desc_no_release_branch() {
        let desc = make_merge_desc(
            (String::from("Yay, I made a change (#99)"), String::from("")),
            "abcdef",
            99,
            "other_branch",
            "source_branch",
        );

        assert_eq!(desc.0, "source_branch->other_branch: Yay, I made a change");
        assert_eq!(desc.1, "(cherry-picked from abcdef, PR #99)");
    }
}
<|endoftext|>


<commit_before>use arbitrary::Arbitrary;
use generate::{Generator, GenerateCtx};
use shrink::{self, Shrink};
use testable::{Testable, TestResult, TestStatus};
use quick_fn::QuickFn;
use rose::Rose;

use std::marker::PhantomData;
use std::fmt::Debug;
use rand::Rng;

#[derive(Copy, Clone)]
pub struct Property<Args> {
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct ForAllProperty<Args, G, S, F> {
    generator: G,
    shrinker: S,
    f: F,
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct ForAll<Args, G, S> {
    generator: G,
    shrinker: S,
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct When<Args, P> {
    predicate: P,
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct WhenFn<Args, P, F> {
    predicate: P,
    f: F,
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct QuickFnArgs<A>(A);

impl <A: Generator> Generator for QuickFnArgs<A> {
    type Output = QuickFnArgs<A::Output>;

    #[inline]
    fn generate<R: Rng>(&self, ctx: &mut GenerateCtx<R>) -> <Self as Generator>::Output {
        QuickFnArgs(self.0.generate(ctx))
    }
}

impl <A: Arbitrary> Arbitrary for QuickFnArgs<A> {
    type Generator = QuickFnArgs<A::Generator>;
    type Shrink = shrink::Empty<Self>;

    fn arbitrary() -> Self::Generator {
        QuickFnArgs(A::arbitrary())
    }

    #[inline] fn shrink() -> Self::Shrink { shrink::Empty::empty() }
}

impl <G, S, T, F, Args: Debug> Testable for ForAllProperty<QuickFnArgs<Args>, G, S, F>
    where G: Generator<Output=Args>,
          S: Shrink<Item=Args>,
          F: QuickFn<Args, Output=T>,
          T: Into<TestStatus>
{
    #[inline]
    fn test<R: Rng>(&self, ctx: &mut GenerateCtx<R>) -> Rose<TestResult> {
        let args = self.generator.generate(ctx);
        Rose::single(TestResult {
            input: format!("{:?}", &args),
            status: self.f.call(args).into()
        })
    }
}

impl <G, S, Args> ForAll<QuickFnArgs<Args>, G, S> {
    #[inline]
    pub fn property<F, T>(self, f: F) -> ForAllProperty<QuickFnArgs<Args>, G, S, F>
        where F: QuickFn<Args, Output=T>,
              S: Shrink<Item=Args>,
              T: Into<TestStatus>,
              G: Generator<Output=Args>
    {
        ForAllProperty {
            generator: self.generator,
            shrinker: self.shrinker,
            f: f,
            _marker: PhantomData
        }
    }
}

impl <Args: Arbitrary> Property<QuickFnArgs<Args>> {
    pub fn new<F, T>(f: F) -> ForAllProperty<QuickFnArgs<Args>, Args::Generator, shrink::Empty<Args>, F>
        where F: QuickFn<Args, Output=T>,
              T: Into<TestStatus>
    {
        Property::<QuickFnArgs<Args>>::for_all(<Args>::arbitrary()).property(f)
    }

    pub fn for_all<G: Generator<Output=Args>>(g: G) -> ForAll<QuickFnArgs<Args>, G, shrink::Empty<Args>> {
        ForAll {
            generator: g,
            shrinker: shrink::Empty::empty(),
            _marker: PhantomData
        }
    }

    pub fn when<P: QuickFn<Args, Output=bool>>(p: P) -> When<QuickFnArgs<Args>, P> {
        When { predicate: p, _marker: PhantomData }
    }
}

macro_rules! fn_impls {
    ($($ident:ident),*) => {
        impl <G, S, T, F, $($ident: Debug),*> Testable for ForAllProperty<($($ident,)*), G, S, F>
            where G: Generator<Output=($($ident,)*)>,
                  S: Shrink<Item=($($ident,)*)>,
                  F: Fn($($ident),*) -> T,
                  T: Into<TestStatus>
        {
            #[inline]
            #[allow(non_snake_case)]
            fn test<R: Rng>(&self, ctx: &mut GenerateCtx<R>) -> Rose<TestResult> {
                let args = self.generator.generate(ctx);
                let ($($ident,)*) = args;
                Rose::single(TestResult {
                    input: format!("{:?}", ($(&$ident,)*)),
                    status: (self.f)($($ident),*).into()
                })
            }
        }

        impl <G, S, $($ident),*> ForAll<($($ident,)*), G, S> {
            #[inline]
            pub fn property<F, T>(self, f: F) -> ForAllProperty<($($ident,)*), G, S, F>
                where F: Fn($($ident),*) -> T,
                      T: Into<TestStatus>,
                      G: Generator<Output=($($ident,)*)>
            {
                ForAllProperty {
                    generator: self.generator,
                    shrinker: self.shrinker,
                    f: f,
                    _marker: PhantomData
                }
            }
        }

        impl <$($ident: Arbitrary),*> Property<($($ident,)*)> {
            pub fn new<F, T>(f: F) -> ForAllProperty<
                                            ($($ident,)*),
                                            <($($ident,)*) as Arbitrary>::Generator,
                                            shrink::Empty<($($ident,)*)>,
                                            F>
                where F: Fn($($ident),*) -> T,
                      T: Into<TestStatus>
            {
                Property::<($($ident,)*)>::for_all(<($($ident,)*)>::arbitrary()).property(f)
            }

            pub fn for_all<G: Generator<Output=($($ident,)*)>>(g: G) -> ForAll<($($ident,)*), G, shrink::Empty<($($ident,)*)>> {
                ForAll {
                    generator: g,
                    shrinker: shrink::Empty::empty(),
                    _marker: PhantomData
                }
            }

            pub fn when<P: Fn($($ident),*) -> bool>(p: P) -> When<($($ident,)*), P> {
                When { predicate: p, _marker: PhantomData }
            }
        }

        impl <P, F, T, $($ident: Clone),*> QuickFn<($($ident,)*)> for WhenFn<($($ident,)*), P, F>
            where P: Fn($($ident),*) -> bool,
                  F: Fn($($ident),*) -> T,
                  T: Into<TestStatus>
        {
            type Output = TestStatus;

            #[inline]
            #[allow(non_snake_case)]
            fn call(&self, args: ($($ident,)*)) -> Self::Output {
                let ($($ident,)*) = args;
                match (self.predicate)($($ident.clone()),*) {
                    false => TestStatus::Discard,
                    true  => (self.f)($($ident),*).into()
                }
            }
        }

        impl <P, $($ident: Arbitrary + Clone),*> When<($($ident,)*), P> {
            #[inline]
            pub fn property<F, T>(self, f: F)
                -> ForAllProperty<QuickFnArgs<($($ident,)*)>,
                                  <($($ident,)*) as Arbitrary>::Generator,
                                  shrink::Empty<($($ident,)*)>,
                                  WhenFn<($($ident,)*), P, F>>
                where P: Fn($($ident),*) -> bool,
                      F: Fn($($ident),*) -> T,
                      T: Into<TestStatus>
            {
                Property::<QuickFnArgs<($($ident,)*)>>::new(WhenFn {
                    predicate: self.predicate,
                    f: f,
                    _marker: PhantomData
                })
            }
        }
    }
}

macro_tuples_impl! { fn_impls }

#[cfg(test)]
mod tests {
    use super::*;
    use arbitrary::Arbitrary;

    #[test]
    fn test_simple_property() {
        Property::<()>::new(|| false);
    }

    #[test]
    fn test_for_all_property() {
        Property::<()>::for_all(<()>::arbitrary()).property(|| true);
    }

    #[test]
    fn test_when_property() {
        Property::<()>::when(|| false).property(|| true);
    }

    #[test]
    fn test_generic_property() {
        fn property<A>(_: A) -> bool {
            ::std::mem::size_of::<A>() == 0
        }

        Property::<(usize,)>::new(property);
        // Doesn't type check
        // Property::new(property as fn(usize) -> bool);
    }
}
<commit_msg>added `Property::for_all_shrink`.<commit_after>use arbitrary::Arbitrary;
use generate::{Generator, GenerateCtx};
use shrink::{self, Shrink};
use testable::{Testable, TestResult, TestStatus};
use quick_fn::QuickFn;
use rose::Rose;

use std::marker::PhantomData;
use std::fmt::Debug;
use rand::Rng;

#[derive(Copy, Clone)]
pub struct Property<Args> {
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct ForAllProperty<Args, G, S, F> {
    generator: G,
    shrinker: S,
    f: F,
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct ForAll<Args, G, S> {
    generator: G,
    shrinker: S,
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct When<Args, P> {
    predicate: P,
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct WhenFn<Args, P, F> {
    predicate: P,
    f: F,
    _marker: PhantomData<Args>
}

#[derive(Copy, Clone)]
pub struct QuickFnArgs<A>(A);

impl <A: Generator> Generator for QuickFnArgs<A> {
    type Output = QuickFnArgs<A::Output>;

    #[inline]
    fn generate<R: Rng>(&self, ctx: &mut GenerateCtx<R>) -> <Self as Generator>::Output {
        QuickFnArgs(self.0.generate(ctx))
    }
}

impl <A: Arbitrary> Arbitrary for QuickFnArgs<A> {
    type Generator = QuickFnArgs<A::Generator>;
    type Shrink = shrink::Empty<Self>;

    fn arbitrary() -> Self::Generator {
        QuickFnArgs(A::arbitrary())
    }

    #[inline] fn shrink() -> Self::Shrink { shrink::Empty::empty() }
}

impl <G, S, T, F, Args: Debug> Testable for ForAllProperty<QuickFnArgs<Args>, G, S, F>
    where G: Generator<Output=Args>,
          S: Shrink<Item=Args>,
          F: QuickFn<Args, Output=T>,
          T: Into<TestStatus>
{
    #[inline]
    fn test<R: Rng>(&self, ctx: &mut GenerateCtx<R>) -> Rose<TestResult> {
        let args = self.generator.generate(ctx);
        Rose::single(TestResult {
            input: format!("{:?}", &args),
            status: self.f.call(args).into()
        })
    }
}

impl <G, S, Args> ForAll<QuickFnArgs<Args>, G, S> {
    #[inline]
    pub fn property<F, T>(self, f: F) -> ForAllProperty<QuickFnArgs<Args>, G, S, F>
        where F: QuickFn<Args, Output=T>,
              S: Shrink<Item=Args>,
              T: Into<TestStatus>,
              G: Generator<Output=Args>
    {
        ForAllProperty {
            generator: self.generator,
            shrinker: self.shrinker,
            f: f,
            _marker: PhantomData
        }
    }
}

impl <Args: Arbitrary> Property<QuickFnArgs<Args>> {
    pub fn new<F, T>(f: F) -> ForAllProperty<QuickFnArgs<Args>, Args::Generator, Args::Shrink, F>
        where F: QuickFn<Args, Output=T>,
              T: Into<TestStatus>
    {
        Property::<QuickFnArgs<Args>>::for_all_shrink(<Args>::arbitrary(), <Args>::shrink()).property(f)
    }

    pub fn for_all<G: Generator<Output=Args>>(g: G) -> ForAll<QuickFnArgs<Args>, G, shrink::Empty<Args>> {
        Property::<QuickFnArgs<Args>>::for_all_shrink(g, shrink::Empty::empty())
    }

    pub fn for_all_shrink<G, S>(g: G, s: S) -> ForAll<QuickFnArgs<Args>, G, S>
        where G: Generator<Output=Args>,
              S: Shrink<Item=Args>
    {
        ForAll {
            generator: g,
            shrinker: s,
            _marker: PhantomData
        }
    }

    pub fn when<P: QuickFn<Args, Output=bool>>(p: P) -> When<QuickFnArgs<Args>, P> {
        When { predicate: p, _marker: PhantomData }
    }
}

macro_rules! fn_impls {
    ($($ident:ident),*) => {
        impl <G, S, T, F, $($ident: Debug),*> Testable for ForAllProperty<($($ident,)*), G, S, F>
            where G: Generator<Output=($($ident,)*)>,
                  S: Shrink<Item=($($ident,)*)>,
                  F: Fn($($ident),*) -> T,
                  T: Into<TestStatus>
        {
            #[inline]
            #[allow(non_snake_case)]
            fn test<R: Rng>(&self, ctx: &mut GenerateCtx<R>) -> Rose<TestResult> {
                let args = self.generator.generate(ctx);
                let ($($ident,)*) = args;
                Rose::single(TestResult {
                    input: format!("{:?}", ($(&$ident,)*)),
                    status: (self.f)($($ident),*).into()
                })
            }
        }

        impl <G, S, $($ident),*> ForAll<($($ident,)*), G, S> {
            #[inline]
            pub fn property<F, T>(self, f: F) -> ForAllProperty<($($ident,)*), G, S, F>
                where F: Fn($($ident),*) -> T,
                      T: Into<TestStatus>,
                      G: Generator<Output=($($ident,)*)>
            {
                ForAllProperty {
                    generator: self.generator,
                    shrinker: self.shrinker,
                    f: f,
                    _marker: PhantomData
                }
            }
        }

        impl <$($ident: Arbitrary),*> Property<($($ident,)*)> {
            pub fn new<F, T>(f: F) -> ForAllProperty<
                                            ($($ident,)*),
                                            <($($ident,)*) as Arbitrary>::Generator,
                                            <($($ident,)*) as Arbitrary>::Shrink,
                                            F>
                where F: Fn($($ident),*) -> T,
                      T: Into<TestStatus>
            {
                Property::<($($ident,)*)>::for_all_shrink(
                    <($($ident,)*) as Arbitrary>::arbitrary(),
                    <($($ident,)*) as Arbitrary>::shrink()
                ).property(f)
            }

            pub fn for_all<G>(g: G) -> ForAll<($($ident,)*), G, shrink::Empty<($($ident,)*)>>
                where G: Generator<Output=($($ident,)*)>
            {
                Property::<($($ident,)*)>::for_all_shrink(g, shrink::Empty::empty())
            }

            pub fn for_all_shrink<G, S>(g: G, s: S) -> ForAll<($($ident,)*), G, S>
                where G: Generator<Output=($($ident,)*)>,
                      S: Shrink<Item=($($ident,)*)>
            {
                ForAll {
                    generator: g,
                    shrinker: s,
                    _marker: PhantomData
                }
            }

            pub fn when<P: Fn($($ident),*) -> bool>(p: P) -> When<($($ident,)*), P> {
                When { predicate: p, _marker: PhantomData }
            }
        }

        impl <P, F, T, $($ident: Clone),*> QuickFn<($($ident,)*)> for WhenFn<($($ident,)*), P, F>
            where P: Fn($($ident),*) -> bool,
                  F: Fn($($ident),*) -> T,
                  T: Into<TestStatus>
        {
            type Output = TestStatus;

            #[inline]
            #[allow(non_snake_case)]
            fn call(&self, args: ($($ident,)*)) -> Self::Output {
                let ($($ident,)*) = args;
                match (self.predicate)($($ident.clone()),*) {
                    false => TestStatus::Discard,
                    true  => (self.f)($($ident),*).into()
                }
            }
        }

        impl <P, $($ident: Arbitrary + Clone),*> When<($($ident,)*), P> {
            #[inline]
            pub fn property<F, T>(self, f: F)
                -> ForAllProperty<QuickFnArgs<($($ident,)*)>,
                                  <($($ident,)*) as Arbitrary>::Generator,
                                  shrink::Empty<($($ident,)*)>,
                                  WhenFn<($($ident,)*), P, F>>
                where P: Fn($($ident),*) -> bool,
                      F: Fn($($ident),*) -> T,
                      T: Into<TestStatus>
            {
                Property::<QuickFnArgs<($($ident,)*)>>::new(WhenFn {
                    predicate: self.predicate,
                    f: f,
                    _marker: PhantomData
                })
            }
        }
    }
}

macro_tuples_impl! { fn_impls }

#[cfg(test)]
mod tests {
    use super::*;
    use arbitrary::Arbitrary;

    #[test]
    fn test_simple_property() {
        Property::<()>::new(|| false);
    }

    #[test]
    fn test_for_all_property() {
        Property::<()>::for_all(<()>::arbitrary()).property(|| true);
    }

    #[test]
    fn test_when_property() {
        Property::<()>::when(|| false).property(|| true);
    }

    #[test]
    fn test_generic_property() {
        fn property<A>(_: A) -> bool {
            ::std::mem::size_of::<A>() == 0
        }

        Property::<(usize,)>::new(property);
        // Doesn't type check
        // Property::new(property as fn(usize) -> bool);
    }
}
<|endoftext|>