<commit_before>// This is a part of rust-chrono.
// Copyright (c) 2014, Kang Seonghoon.
// See README.md and LICENSE.txt for details.

/*!
 * ISO 8601 calendar date with timezone.
 */

use std::{fmt, hash};

use {Weekday, Datelike};
use duration::Duration;
use offset::{Offset, UTC};
use naive;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use datetime::DateTime;
use format::DelayedFormat;

/// ISO 8601 calendar date with timezone.
#[deriving(Clone)]
pub struct Date<Off> {
    date: NaiveDate,
    offset: Off,
}

/// The minimum possible `Date`.
pub const MIN: Date<UTC> = Date { date: naive::date::MIN, offset: UTC };
/// The maximum possible `Date`.
pub const MAX: Date<UTC> = Date { date: naive::date::MAX, offset: UTC };

impl<Off:Offset> Date<Off> {
    /// Makes a new `Date` with given *UTC* date and offset.
    /// The local date should be constructed via the `Offset` trait.
    #[inline]
    pub fn from_utc(date: NaiveDate, offset: Off) -> Date<Off> {
        Date { date: date, offset: offset }
    }

    /// Makes a new `DateTime` from the current date and given `NaiveTime`.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid datetime.
    #[inline]
    pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Off>> {
        let localdt = self.offset.to_local_date(&self.date).and_time(time);
        self.offset.from_local_datetime(&localdt).single()
    }

    /// Makes a new `DateTime` from the current date, hour, minute and second.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid hour, minute and/or second.
    #[inline]
    pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Off> {
        self.and_hms_opt(hour, min, sec).expect("invalid time")
    }

    /// Makes a new `DateTime` from the current date, hour, minute and second.
    /// The offset in the current date is preserved.
    ///
    /// Returns `None` on invalid hour, minute and/or second.
    #[inline]
    pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Off>> {
        NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time))
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
    /// The millisecond part can exceed 1,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid hour, minute, second and/or millisecond.
    #[inline]
    pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Off> {
        self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
    /// The millisecond part can exceed 1,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Returns `None` on invalid hour, minute, second and/or millisecond.
    #[inline]
    pub fn and_hms_milli_opt(&self, hour: u32, min: u32, sec: u32,
                             milli: u32) -> Option<DateTime<Off>> {
        NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time))
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
    /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid hour, minute, second and/or microsecond.
    #[inline]
    pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Off> {
        self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
    /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Returns `None` on invalid hour, minute, second and/or microsecond.
    #[inline]
    pub fn and_hms_micro_opt(&self, hour: u32, min: u32, sec: u32,
                             micro: u32) -> Option<DateTime<Off>> {
        NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time))
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
    /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid hour, minute, second and/or nanosecond.
    #[inline]
    pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Off> {
        self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
    /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Returns `None` on invalid hour, minute, second and/or nanosecond.
    #[inline]
    pub fn and_hms_nano_opt(&self, hour: u32, min: u32, sec: u32,
                            nano: u32) -> Option<DateTime<Off>> {
        NaiveTime::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time))
    }

    /// Makes a new `Date` for the next date.
    ///
    /// Fails when `self` is the last representable date.
    #[inline]
    pub fn succ(&self) -> Date<Off> {
        self.succ_opt().expect("out of bound")
    }

    /// Makes a new `Date` for the next date.
    ///
    /// Returns `None` when `self` is the last representable date.
    #[inline]
    pub fn succ_opt(&self) -> Option<Date<Off>> {
        self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone()))
    }

    /// Makes a new `Date` for the prior date.
    ///
    /// Fails when `self` is the first representable date.
    #[inline]
    pub fn pred(&self) -> Date<Off> {
        self.pred_opt().expect("out of bound")
    }

    /// Makes a new `Date` for the prior date.
    ///
    /// Returns `None` when `self` is the first representable date.
    #[inline]
    pub fn pred_opt(&self) -> Option<Date<Off>> {
        self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
    }

    /// Retrieves an associated offset.
    #[inline]
    pub fn offset<'a>(&'a self) -> &'a Off {
        &self.offset
    }

    /// Changes the associated offset.
    /// This does not change the actual `Date` (but will change the string representation).
    #[inline]
    pub fn with_offset<Off2:Offset>(&self, offset: Off2) -> Date<Off2> {
        Date::from_utc(self.date, offset)
    }

    /// Formats the date in the specified format string.
    /// See the `format` module on the supported escape sequences.
    #[inline]
    pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
        DelayedFormat::new_with_offset(Some(self.local()), None, &self.offset, fmt)
    }

    /// Returns a view to the local date.
    fn local(&self) -> NaiveDate {
        self.offset.to_local_date(&self.date)
    }
}

impl<Off:Offset> Datelike for Date<Off> {
    #[inline] fn year(&self) -> i32 { self.local().year() }
    #[inline] fn month(&self) -> u32 { self.local().month() }
    #[inline] fn month0(&self) -> u32 { self.local().month0() }
    #[inline] fn day(&self) -> u32 { self.local().day() }
    #[inline] fn day0(&self) -> u32 { self.local().day0() }
    #[inline] fn ordinal(&self) -> u32 { self.local().ordinal() }
    #[inline] fn ordinal0(&self) -> u32 { self.local().ordinal0() }
    #[inline] fn weekday(&self) -> Weekday { self.local().weekday() }
    #[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.local().isoweekdate() }

    #[inline]
    fn with_year(&self, year: i32) -> Option<Date<Off>> {
        self.local().with_year(year)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_month(&self, month: u32) -> Option<Date<Off>> {
        self.local().with_month(month)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_month0(&self, month0: u32) -> Option<Date<Off>> {
        self.local().with_month0(month0)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_day(&self, day: u32) -> Option<Date<Off>> {
        self.local().with_day(day)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_day0(&self, day0: u32) -> Option<Date<Off>> {
        self.local().with_day0(day0)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_ordinal(&self, ordinal: u32) -> Option<Date<Off>> {
        self.local().with_ordinal(ordinal)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Off>> {
        self.local().with_ordinal0(ordinal0)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }
}

impl<Off:Offset, Off2:Offset> PartialEq<Date<Off2>> for Date<Off> {
    fn eq(&self, other: &Date<Off2>) -> bool { self.date == other.date }
}

impl<Off:Offset, Off2:Offset> Eq<Date<Off2>> for Date<Off> {
}

impl<Off:Offset> PartialOrd for Date<Off> {
    fn partial_cmp(&self, other: &Date<Off>) -> Option<Ordering> {
        self.date.partial_cmp(&other.date)
    }
}

impl<Off:Offset> Ord for Date<Off> {
    fn cmp(&self, other: &Date<Off>) -> Ordering { self.date.cmp(&other.date) }
}

impl<Off:Offset> hash::Hash for Date<Off> {
    fn hash(&self, state: &mut hash::sip::SipState) { self.date.hash(state) }
}

impl<Off:Offset> Add<Duration,Date<Off>> for Date<Off> {
    fn add(self, rhs: Duration) -> Date<Off> {
        Date { date: self.date + rhs, offset: self.offset }
    }
}

impl<Off:Offset> Add<Date<Off>,Date<Off>> for Duration {
    #[inline]
    fn add(self, rhs: Date<Off>) -> Date<Off> { rhs.add(self) }
}

impl<Off:Offset, Off2:Offset> Sub<Date<Off2>,Duration> for Date<Off> {
    fn sub(self, rhs: Date<Off2>) -> Duration { self.date - rhs.date }
}

impl<Off:Offset> Sub<Duration,Date<Off>> for Date<Off> {
    #[inline]
    fn sub(self, rhs: Duration) -> Date<Off> { self.add(-rhs) }
}

impl<Off:Offset> fmt::Show for Date<Off> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.local(), self.offset)
    }
}

#[cfg(test)]
mod tests {
    use std::fmt;
    use std::str::SendStr;

    use duration::Duration;
    use naive::date::NaiveDate;
    use naive::time::NaiveTime;
    use naive::datetime::NaiveDateTime;
    use super::Date;
    use time::Time;
    use datetime::DateTime;
    use offset::{Offset, LocalResult};

    #[deriving(Copy, Clone, PartialEq, Eq)]
    struct UTC1y; // same to UTC but with an offset of 365 days

    impl Offset for UTC1y {
        fn name(&self) -> SendStr { "UTC+8760".into_cow() } // yes, no kidding
        fn local_minus_utc(&self) -> Duration { Duration::zero() }

        fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC1y>> {
            LocalResult::Single(Date::from_utc(*local - Duration::days(365), UTC1y))
        }
        fn from_local_time(&self, local: &NaiveTime) -> LocalResult<Time<UTC1y>> {
            LocalResult::Single(Time::from_utc(local.clone(), UTC1y))
        }
        fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<UTC1y>> {
            LocalResult::Single(DateTime::from_utc(*local - Duration::days(365), UTC1y))
        }

        fn to_local_date(&self, utc: &NaiveDate) -> NaiveDate { *utc + Duration::days(365) }
        fn to_local_time(&self, utc: &NaiveTime) -> NaiveTime { utc.clone() }
        fn to_local_datetime(&self, utc: &NaiveDateTime) -> NaiveDateTime {
            *utc + Duration::days(365)
        }
    }

    impl fmt::Show for UTC1y {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "+8760:00") }
    }

    #[test]
    fn test_date_weird_offset() {
        assert_eq!(UTC1y.ymd(2012, 2, 29).to_string(),
                   "2012-02-29+8760:00".to_string());
        assert_eq!(UTC1y.ymd(2012, 2, 29).and_hms(5, 6, 7).to_string(),
                   "2012-02-29T05:06:07+8760:00".to_string());
        assert_eq!(UTC1y.ymd(2012, 3, 4).to_string(),
                   "2012-03-04+8760:00".to_string());
        assert_eq!(UTC1y.ymd(2012, 3, 4).and_hms(5, 6, 7).to_string(),
                   "2012-03-04T05:06:07+8760:00".to_string());
    }
}

<commit_msg>Removed reflexive type specifier from Date<Off>'s Eq implementation<commit_after>// This is a part of rust-chrono.
// Copyright (c) 2014, Kang Seonghoon.
// See README.md and LICENSE.txt for details.

/*!
 * ISO 8601 calendar date with timezone.
 */

use std::{fmt, hash};

use {Weekday, Datelike};
use duration::Duration;
use offset::{Offset, UTC};
use naive;
use naive::date::NaiveDate;
use naive::time::NaiveTime;
use datetime::DateTime;
use format::DelayedFormat;

/// ISO 8601 calendar date with timezone.
#[deriving(Clone)]
pub struct Date<Off> {
    date: NaiveDate,
    offset: Off,
}

/// The minimum possible `Date`.
pub const MIN: Date<UTC> = Date { date: naive::date::MIN, offset: UTC };
/// The maximum possible `Date`.
pub const MAX: Date<UTC> = Date { date: naive::date::MAX, offset: UTC };

impl<Off:Offset> Date<Off> {
    /// Makes a new `Date` with given *UTC* date and offset.
    /// The local date should be constructed via the `Offset` trait.
    #[inline]
    pub fn from_utc(date: NaiveDate, offset: Off) -> Date<Off> {
        Date { date: date, offset: offset }
    }

    /// Makes a new `DateTime` from the current date and given `NaiveTime`.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid datetime.
    #[inline]
    pub fn and_time(&self, time: NaiveTime) -> Option<DateTime<Off>> {
        let localdt = self.offset.to_local_date(&self.date).and_time(time);
        self.offset.from_local_datetime(&localdt).single()
    }

    /// Makes a new `DateTime` from the current date, hour, minute and second.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid hour, minute and/or second.
    #[inline]
    pub fn and_hms(&self, hour: u32, min: u32, sec: u32) -> DateTime<Off> {
        self.and_hms_opt(hour, min, sec).expect("invalid time")
    }

    /// Makes a new `DateTime` from the current date, hour, minute and second.
    /// The offset in the current date is preserved.
    ///
    /// Returns `None` on invalid hour, minute and/or second.
    #[inline]
    pub fn and_hms_opt(&self, hour: u32, min: u32, sec: u32) -> Option<DateTime<Off>> {
        NaiveTime::from_hms_opt(hour, min, sec).and_then(|time| self.and_time(time))
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
    /// The millisecond part can exceed 1,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid hour, minute, second and/or millisecond.
    #[inline]
    pub fn and_hms_milli(&self, hour: u32, min: u32, sec: u32, milli: u32) -> DateTime<Off> {
        self.and_hms_milli_opt(hour, min, sec, milli).expect("invalid time")
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and millisecond.
    /// The millisecond part can exceed 1,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Returns `None` on invalid hour, minute, second and/or millisecond.
    #[inline]
    pub fn and_hms_milli_opt(&self, hour: u32, min: u32, sec: u32,
                             milli: u32) -> Option<DateTime<Off>> {
        NaiveTime::from_hms_milli_opt(hour, min, sec, milli).and_then(|time| self.and_time(time))
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
    /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid hour, minute, second and/or microsecond.
    #[inline]
    pub fn and_hms_micro(&self, hour: u32, min: u32, sec: u32, micro: u32) -> DateTime<Off> {
        self.and_hms_micro_opt(hour, min, sec, micro).expect("invalid time")
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and microsecond.
    /// The microsecond part can exceed 1,000,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Returns `None` on invalid hour, minute, second and/or microsecond.
    #[inline]
    pub fn and_hms_micro_opt(&self, hour: u32, min: u32, sec: u32,
                             micro: u32) -> Option<DateTime<Off>> {
        NaiveTime::from_hms_micro_opt(hour, min, sec, micro).and_then(|time| self.and_time(time))
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
    /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Fails on invalid hour, minute, second and/or nanosecond.
    #[inline]
    pub fn and_hms_nano(&self, hour: u32, min: u32, sec: u32, nano: u32) -> DateTime<Off> {
        self.and_hms_nano_opt(hour, min, sec, nano).expect("invalid time")
    }

    /// Makes a new `DateTime` from the current date, hour, minute, second and nanosecond.
    /// The nanosecond part can exceed 1,000,000,000 in order to represent the leap second.
    /// The offset in the current date is preserved.
    ///
    /// Returns `None` on invalid hour, minute, second and/or nanosecond.
    #[inline]
    pub fn and_hms_nano_opt(&self, hour: u32, min: u32, sec: u32,
                            nano: u32) -> Option<DateTime<Off>> {
        NaiveTime::from_hms_nano_opt(hour, min, sec, nano).and_then(|time| self.and_time(time))
    }

    /// Makes a new `Date` for the next date.
    ///
    /// Fails when `self` is the last representable date.
    #[inline]
    pub fn succ(&self) -> Date<Off> {
        self.succ_opt().expect("out of bound")
    }

    /// Makes a new `Date` for the next date.
    ///
    /// Returns `None` when `self` is the last representable date.
    #[inline]
    pub fn succ_opt(&self) -> Option<Date<Off>> {
        self.date.succ_opt().map(|date| Date::from_utc(date, self.offset.clone()))
    }

    /// Makes a new `Date` for the prior date.
    ///
    /// Fails when `self` is the first representable date.
    #[inline]
    pub fn pred(&self) -> Date<Off> {
        self.pred_opt().expect("out of bound")
    }

    /// Makes a new `Date` for the prior date.
    ///
    /// Returns `None` when `self` is the first representable date.
    #[inline]
    pub fn pred_opt(&self) -> Option<Date<Off>> {
        self.date.pred_opt().map(|date| Date::from_utc(date, self.offset.clone()))
    }

    /// Retrieves an associated offset.
    #[inline]
    pub fn offset<'a>(&'a self) -> &'a Off {
        &self.offset
    }

    /// Changes the associated offset.
    /// This does not change the actual `Date` (but will change the string representation).
    #[inline]
    pub fn with_offset<Off2:Offset>(&self, offset: Off2) -> Date<Off2> {
        Date::from_utc(self.date, offset)
    }

    /// Formats the date in the specified format string.
    /// See the `format` module on the supported escape sequences.
    #[inline]
    pub fn format<'a>(&'a self, fmt: &'a str) -> DelayedFormat<'a> {
        DelayedFormat::new_with_offset(Some(self.local()), None, &self.offset, fmt)
    }

    /// Returns a view to the local date.
    fn local(&self) -> NaiveDate {
        self.offset.to_local_date(&self.date)
    }
}

impl<Off:Offset> Datelike for Date<Off> {
    #[inline] fn year(&self) -> i32 { self.local().year() }
    #[inline] fn month(&self) -> u32 { self.local().month() }
    #[inline] fn month0(&self) -> u32 { self.local().month0() }
    #[inline] fn day(&self) -> u32 { self.local().day() }
    #[inline] fn day0(&self) -> u32 { self.local().day0() }
    #[inline] fn ordinal(&self) -> u32 { self.local().ordinal() }
    #[inline] fn ordinal0(&self) -> u32 { self.local().ordinal0() }
    #[inline] fn weekday(&self) -> Weekday { self.local().weekday() }
    #[inline] fn isoweekdate(&self) -> (i32, u32, Weekday) { self.local().isoweekdate() }

    #[inline]
    fn with_year(&self, year: i32) -> Option<Date<Off>> {
        self.local().with_year(year)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_month(&self, month: u32) -> Option<Date<Off>> {
        self.local().with_month(month)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_month0(&self, month0: u32) -> Option<Date<Off>> {
        self.local().with_month0(month0)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_day(&self, day: u32) -> Option<Date<Off>> {
        self.local().with_day(day)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_day0(&self, day0: u32) -> Option<Date<Off>> {
        self.local().with_day0(day0)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_ordinal(&self, ordinal: u32) -> Option<Date<Off>> {
        self.local().with_ordinal(ordinal)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }

    #[inline]
    fn with_ordinal0(&self, ordinal0: u32) -> Option<Date<Off>> {
        self.local().with_ordinal0(ordinal0)
            .and_then(|date| self.offset.from_local_date(&date).single())
    }
}

impl<Off:Offset, Off2:Offset> PartialEq<Date<Off2>> for Date<Off> {
    fn eq(&self, other: &Date<Off2>) -> bool { self.date == other.date }
}

impl<Off:Offset> Eq for Date<Off> {
}

impl<Off:Offset> PartialOrd for Date<Off> {
    fn partial_cmp(&self, other: &Date<Off>) -> Option<Ordering> {
        self.date.partial_cmp(&other.date)
    }
}

impl<Off:Offset> Ord for Date<Off> {
    fn cmp(&self, other: &Date<Off>) -> Ordering { self.date.cmp(&other.date) }
}

impl<Off:Offset> hash::Hash for Date<Off> {
    fn hash(&self, state: &mut hash::sip::SipState) { self.date.hash(state) }
}

impl<Off:Offset> Add<Duration,Date<Off>> for Date<Off> {
    fn add(self, rhs: Duration) -> Date<Off> {
        Date { date: self.date + rhs, offset: self.offset }
    }
}

impl<Off:Offset> Add<Date<Off>,Date<Off>> for Duration {
    #[inline]
    fn add(self, rhs: Date<Off>) -> Date<Off> { rhs.add(self) }
}

impl<Off:Offset, Off2:Offset> Sub<Date<Off2>,Duration> for Date<Off> {
    fn sub(self, rhs: Date<Off2>) -> Duration { self.date - rhs.date }
}

impl<Off:Offset> Sub<Duration,Date<Off>> for Date<Off> {
    #[inline]
    fn sub(self, rhs: Duration) -> Date<Off> { self.add(-rhs) }
}

impl<Off:Offset> fmt::Show for Date<Off> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}", self.local(), self.offset)
    }
}

#[cfg(test)]
mod tests {
    use std::fmt;
    use std::str::SendStr;

    use duration::Duration;
    use naive::date::NaiveDate;
    use naive::time::NaiveTime;
    use naive::datetime::NaiveDateTime;
    use super::Date;
    use time::Time;
    use datetime::DateTime;
    use offset::{Offset, LocalResult};

    #[deriving(Copy, Clone, PartialEq, Eq)]
    struct UTC1y; // same to UTC but with an offset of 365 days

    impl Offset for UTC1y {
        fn name(&self) -> SendStr { "UTC+8760".into_cow() } // yes, no kidding
        fn local_minus_utc(&self) -> Duration { Duration::zero() }

        fn from_local_date(&self, local: &NaiveDate) -> LocalResult<Date<UTC1y>> {
            LocalResult::Single(Date::from_utc(*local - Duration::days(365), UTC1y))
        }
        fn from_local_time(&self, local: &NaiveTime) -> LocalResult<Time<UTC1y>> {
            LocalResult::Single(Time::from_utc(local.clone(), UTC1y))
        }
        fn from_local_datetime(&self, local: &NaiveDateTime) -> LocalResult<DateTime<UTC1y>> {
            LocalResult::Single(DateTime::from_utc(*local - Duration::days(365), UTC1y))
        }

        fn to_local_date(&self, utc: &NaiveDate) -> NaiveDate { *utc + Duration::days(365) }
        fn to_local_time(&self, utc: &NaiveTime) -> NaiveTime { utc.clone() }
        fn to_local_datetime(&self, utc: &NaiveDateTime) -> NaiveDateTime {
            *utc + Duration::days(365)
        }
    }

    impl fmt::Show for UTC1y {
        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "+8760:00") }
    }

    #[test]
    fn test_date_weird_offset() {
        assert_eq!(UTC1y.ymd(2012, 2, 29).to_string(),
                   "2012-02-29+8760:00".to_string());
        assert_eq!(UTC1y.ymd(2012, 2, 29).and_hms(5, 6, 7).to_string(),
                   "2012-02-29T05:06:07+8760:00".to_string());
        assert_eq!(UTC1y.ymd(2012, 3, 4).to_string(),
                   "2012-03-04+8760:00".to_string());
        assert_eq!(UTC1y.ymd(2012, 3, 4).and_hms(5, 6, 7).to_string(),
                   "2012-03-04T05:06:07+8760:00".to_string());
    }
}

<|endoftext|>


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

use std::fmt::Debug;
use std::fmt::Display;
use std::marker::PhantomData;

use crate::process::Afters;
use crate::process::Initials;

//-------------------------------------------------------------------------------------------------
// Stop

/// The process that performs no actions (and prevents any other synchronized processes from
/// performing any, either).
pub struct Stop;

impl Display for Stop {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str("Stop")
    }
}

impl Debug for Stop {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        (self as &Display).fmt(f)
    }
}

pub struct StopInitials<E>(PhantomData<E>);

impl<E> IntoIterator for StopInitials<E> {
    type Item = E;
    type IntoIter = std::iter::Empty<E>;

    fn into_iter(self) -> Self::IntoIter {
        std::iter::empty()
    }
}

impl<E> Initials<E> for Stop {
    type Initials = StopInitials<E>;

    fn initials(&self) -> Self::Initials {
        StopInitials(PhantomData)
    }
}

pub struct StopAfters<P>(PhantomData<P>);

impl<P> IntoIterator for StopAfters<P> {
    type Item = P;
    type IntoIter = std::iter::Empty<P>;

    fn into_iter(self) -> Self::IntoIter {
        std::iter::empty()
    }
}

impl<E, P> Afters<E, P> for Stop {
    type Afters = StopAfters<P>;

    fn afters(&self, _initial: &E) -> Option<Self::Afters> {
        None
    }
}

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

    use std::collections::HashMap;

    use crate::process::transitions;

    #[test]
    fn stop_has_no_transitions() {
        let transitions: HashMap<(), Vec<Stop>> = transitions(&Stop);
        assert!(transitions.is_empty());
    }
}
<commit_msg>Add built-in tau and tick events<commit_after>// -*- coding: utf-8 -*-
// ------------------------------------------------------------------------------------------------
// Copyright © 2019, HST authors.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
// in compliance with the License.  You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software distributed under the
// License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
// express or implied.  See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------------------------------

use std::fmt::Debug;
use std::fmt::Display;
use std::marker::PhantomData;

use crate::process::Afters;
use crate::process::Initials;

//-------------------------------------------------------------------------------------------------
// Built-in CSP events

#[derive(Clone, Eq, Hash, PartialEq)]
pub struct Tau;

impl Display for Tau {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str("τ")
    }
}

impl Debug for Tau {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        (self as &Display).fmt(f)
    }
}

#[derive(Clone, Eq, Hash, PartialEq)]
pub struct Tick;

impl Display for Tick {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str("✔")
    }
}

impl Debug for Tick {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        (self as &Display).fmt(f)
    }
}

//-------------------------------------------------------------------------------------------------
// Stop

/// The process that performs no actions (and prevents any other synchronized processes from
/// performing any, either).
pub struct Stop;

impl Display for Stop {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f.write_str("Stop")
    }
}

impl Debug for Stop {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        (self as &Display).fmt(f)
    }
}

pub struct StopInitials<E>(PhantomData<E>);

impl<E> IntoIterator for StopInitials<E> {
    type Item = E;
    type IntoIter = std::iter::Empty<E>;

    fn into_iter(self) -> Self::IntoIter {
        std::iter::empty()
    }
}

impl<E> Initials<E> for Stop {
    type Initials = StopInitials<E>;

    fn initials(&self) -> Self::Initials {
        StopInitials(PhantomData)
    }
}

pub struct StopAfters<P>(PhantomData<P>);

impl<P> IntoIterator for StopAfters<P> {
    type Item = P;
    type IntoIter = std::iter::Empty<P>;

    fn into_iter(self) -> Self::IntoIter {
        std::iter::empty()
    }
}

impl<E, P> Afters<E, P> for Stop {
    type Afters = StopAfters<P>;

    fn afters(&self, _initial: &E) -> Option<Self::Afters> {
        None
    }
}

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

    use std::collections::HashMap;

    use crate::process::transitions;

    #[test]
    fn stop_has_no_transitions() {
        let transitions: HashMap<(), Vec<Stop>> = transitions(&Stop);
        assert!(transitions.is_empty());
    }
}
<|endoftext|>


<commit_before>use std::path::BytesContainer;

pub struct Adler32 {
	s1: u32,
	s2: u32
}

impl Adler32 {
	pub fn new() -> Adler32 {
		Adler32 {s1: 1, s2: 0}
	}

	pub fn update<T: BytesContainer>(&mut self, buf: T) {
		for &byte in buf.container_as_bytes().iter() {
			self.s1 = self.s1 + byte as u32;
			self.s2 = self.s1 + self.s2;

			self.s1 %= 65521;
			self.s2 %= 65521;
		}
	}

	pub fn checksum(&self) -> u32 {
		(self.s2 << 16) | self.s1
	}

	pub fn reset(&mut self) {
		self.s1 = 1;
		self.s2 = 0;
	}
}

static IEEE_POLYNOMIAL: u32 = 0xEDB88320;

pub struct Crc32 {
	table: [u32, ..256],
	crc: u32,
}

impl Crc32 {
	pub fn new() -> Crc32 {
		let mut t = [0u32, ..256];

		for n in range(0u32, 256) {
			let mut c = n;

			for _ in range(0, 8) {
				c = if c & 1 == 1 {
					IEEE_POLYNOMIAL ^ (c >> 1)
				}
				else {
					c >> 1
				};
			}

			t[n as uint] = c;
		}

		Crc32 {table: t, crc: 0xFFFFFFFF}
	}

	pub fn update<T: BytesContainer>(&mut self, buf: T) {
		for &byte in buf.container_as_bytes().iter() {
			let a = (self.crc ^ byte as u32) & 0xFF;
			let b = self.crc >> 8;

			self.crc = self.table[a as uint] as u32 ^ b;
		}
	}

	pub fn checksum(&self) -> u32 {
		self.crc ^ 0xFFFFFFFF
	}

	pub fn reset(&mut self) {
		self.crc = 0xFFFFFFFF;
	}
}<commit_msg>Use precomputed CRC32 table<commit_after>use std::path::BytesContainer;

pub struct Adler32 {
	s1: u32,
	s2: u32
}

impl Adler32 {
	pub fn new() -> Adler32 {
		Adler32 {s1: 1, s2: 0}
	}

	pub fn update<T: BytesContainer>(&mut self, buf: T) {
		for &byte in buf.container_as_bytes().iter() {
			self.s1 = self.s1 + byte as u32;
			self.s2 = self.s1 + self.s2;

			self.s1 %= 65521;
			self.s2 %= 65521;
		}
	}

	pub fn checksum(&self) -> u32 {
		(self.s2 << 16) | self.s1
	}

	pub fn reset(&mut self) {
		self.s1 = 1;
		self.s2 = 0;
	}
}

static CRC_TABLE: [u32, ..256] = [
	0x00000000_u32, 0x77073096_u32, 0xee0e612c_u32, 0x990951ba_u32, 0x076dc419_u32,
	0x706af48f_u32, 0xe963a535_u32, 0x9e6495a3_u32, 0x0edb8832_u32, 0x79dcb8a4_u32,
	0xe0d5e91e_u32, 0x97d2d988_u32, 0x09b64c2b_u32, 0x7eb17cbd_u32, 0xe7b82d07_u32,
	0x90bf1d91_u32, 0x1db71064_u32, 0x6ab020f2_u32, 0xf3b97148_u32, 0x84be41de_u32,
	0x1adad47d_u32, 0x6ddde4eb_u32, 0xf4d4b551_u32, 0x83d385c7_u32, 0x136c9856_u32,
	0x646ba8c0_u32, 0xfd62f97a_u32, 0x8a65c9ec_u32, 0x14015c4f_u32, 0x63066cd9_u32,
	0xfa0f3d63_u32, 0x8d080df5_u32, 0x3b6e20c8_u32, 0x4c69105e_u32, 0xd56041e4_u32,
	0xa2677172_u32, 0x3c03e4d1_u32, 0x4b04d447_u32, 0xd20d85fd_u32, 0xa50ab56b_u32,
	0x35b5a8fa_u32, 0x42b2986c_u32, 0xdbbbc9d6_u32, 0xacbcf940_u32, 0x32d86ce3_u32,
	0x45df5c75_u32, 0xdcd60dcf_u32, 0xabd13d59_u32, 0x26d930ac_u32, 0x51de003a_u32,
	0xc8d75180_u32, 0xbfd06116_u32, 0x21b4f4b5_u32, 0x56b3c423_u32, 0xcfba9599_u32,
	0xb8bda50f_u32, 0x2802b89e_u32, 0x5f058808_u32, 0xc60cd9b2_u32, 0xb10be924_u32,
	0x2f6f7c87_u32, 0x58684c11_u32, 0xc1611dab_u32, 0xb6662d3d_u32, 0x76dc4190_u32,
	0x01db7106_u32, 0x98d220bc_u32, 0xefd5102a_u32, 0x71b18589_u32, 0x06b6b51f_u32,
	0x9fbfe4a5_u32, 0xe8b8d433_u32, 0x7807c9a2_u32, 0x0f00f934_u32, 0x9609a88e_u32,
	0xe10e9818_u32, 0x7f6a0dbb_u32, 0x086d3d2d_u32, 0x91646c97_u32, 0xe6635c01_u32,
	0x6b6b51f4_u32, 0x1c6c6162_u32, 0x856530d8_u32, 0xf262004e_u32, 0x6c0695ed_u32,
	0x1b01a57b_u32, 0x8208f4c1_u32, 0xf50fc457_u32, 0x65b0d9c6_u32, 0x12b7e950_u32,
	0x8bbeb8ea_u32, 0xfcb9887c_u32, 0x62dd1ddf_u32, 0x15da2d49_u32, 0x8cd37cf3_u32,
	0xfbd44c65_u32, 0x4db26158_u32, 0x3ab551ce_u32, 0xa3bc0074_u32, 0xd4bb30e2_u32,
	0x4adfa541_u32, 0x3dd895d7_u32, 0xa4d1c46d_u32, 0xd3d6f4fb_u32, 0x4369e96a_u32,
	0x346ed9fc_u32, 0xad678846_u32, 0xda60b8d0_u32, 0x44042d73_u32, 0x33031de5_u32,
	0xaa0a4c5f_u32, 0xdd0d7cc9_u32, 0x5005713c_u32, 0x270241aa_u32, 0xbe0b1010_u32,
	0xc90c2086_u32, 0x5768b525_u32, 0x206f85b3_u32, 0xb966d409_u32, 0xce61e49f_u32,
	0x5edef90e_u32, 0x29d9c998_u32, 0xb0d09822_u32, 0xc7d7a8b4_u32, 0x59b33d17_u32,
	0x2eb40d81_u32, 0xb7bd5c3b_u32, 0xc0ba6cad_u32, 0xedb88320_u32, 0x9abfb3b6_u32,
	0x03b6e20c_u32, 0x74b1d29a_u32, 0xead54739_u32, 0x9dd277af_u32, 0x04db2615_u32,
	0x73dc1683_u32, 0xe3630b12_u32, 0x94643b84_u32, 0x0d6d6a3e_u32, 0x7a6a5aa8_u32,
	0xe40ecf0b_u32, 0x9309ff9d_u32, 0x0a00ae27_u32, 0x7d079eb1_u32, 0xf00f9344_u32,
	0x8708a3d2_u32, 0x1e01f268_u32, 0x6906c2fe_u32, 0xf762575d_u32, 0x806567cb_u32,
	0x196c3671_u32, 0x6e6b06e7_u32, 0xfed41b76_u32, 0x89d32be0_u32, 0x10da7a5a_u32,
	0x67dd4acc_u32, 0xf9b9df6f_u32, 0x8ebeeff9_u32, 0x17b7be43_u32, 0x60b08ed5_u32,
	0xd6d6a3e8_u32, 0xa1d1937e_u32, 0x38d8c2c4_u32, 0x4fdff252_u32, 0xd1bb67f1_u32,
	0xa6bc5767_u32, 0x3fb506dd_u32, 0x48b2364b_u32, 0xd80d2bda_u32, 0xaf0a1b4c_u32,
	0x36034af6_u32, 0x41047a60_u32, 0xdf60efc3_u32, 0xa867df55_u32, 0x316e8eef_u32,
	0x4669be79_u32, 0xcb61b38c_u32, 0xbc66831a_u32, 0x256fd2a0_u32, 0x5268e236_u32,
	0xcc0c7795_u32, 0xbb0b4703_u32, 0x220216b9_u32, 0x5505262f_u32, 0xc5ba3bbe_u32,
	0xb2bd0b28_u32, 0x2bb45a92_u32, 0x5cb36a04_u32, 0xc2d7ffa7_u32, 0xb5d0cf31_u32,
	0x2cd99e8b_u32, 0x5bdeae1d_u32, 0x9b64c2b0_u32, 0xec63f226_u32, 0x756aa39c_u32,
	0x026d930a_u32, 0x9c0906a9_u32, 0xeb0e363f_u32, 0x72076785_u32, 0x05005713_u32,
	0x95bf4a82_u32, 0xe2b87a14_u32, 0x7bb12bae_u32, 0x0cb61b38_u32, 0x92d28e9b_u32,
	0xe5d5be0d_u32, 0x7cdcefb7_u32, 0x0bdbdf21_u32, 0x86d3d2d4_u32, 0xf1d4e242_u32,
	0x68ddb3f8_u32, 0x1fda836e_u32, 0x81be16cd_u32, 0xf6b9265b_u32, 0x6fb077e1_u32,
	0x18b74777_u32, 0x88085ae6_u32, 0xff0f6a70_u32, 0x66063bca_u32, 0x11010b5c_u32,
	0x8f659eff_u32, 0xf862ae69_u32, 0x616bffd3_u32, 0x166ccf45_u32, 0xa00ae278_u32,
	0xd70dd2ee_u32, 0x4e048354_u32, 0x3903b3c2_u32, 0xa7672661_u32, 0xd06016f7_u32,
	0x4969474d_u32, 0x3e6e77db_u32, 0xaed16a4a_u32, 0xd9d65adc_u32, 0x40df0b66_u32,
	0x37d83bf0_u32, 0xa9bcae53_u32, 0xdebb9ec5_u32, 0x47b2cf7f_u32, 0x30b5ffe9_u32,
	0xbdbdf21c_u32, 0xcabac28a_u32, 0x53b39330_u32, 0x24b4a3a6_u32, 0xbad03605_u32,
	0xcdd70693_u32, 0x54de5729_u32, 0x23d967bf_u32, 0xb3667a2e_u32, 0xc4614ab8_u32,
	0x5d681b02_u32, 0x2a6f2b94_u32, 0xb40bbe37_u32, 0xc30c8ea1_u32, 0x5a05df1b_u32,
	0x2d02ef8d_u32
];
pub struct Crc32 {
	crc: u32,
}

impl Crc32 {
	pub fn new() -> Crc32 {
		Crc32 {crc: 0xFFFFFFFF}
	}

	pub fn update<T: BytesContainer>(&mut self, buf: T) {
		for &byte in buf.container_as_bytes().iter() {
			let a = (self.crc ^ byte as u32) & 0xFF;
			let b = self.crc >> 8;

			self.crc = CRC_TABLE[a as uint] as u32 ^ b;
		}
	}

	pub fn checksum(&self) -> u32 {
		self.crc ^ 0xFFFFFFFF
	}

	pub fn reset(&mut self) {
		self.crc = 0xFFFFFFFF;
	}
}<|endoftext|>


<commit_before>use super::settings::Settings;
use futures::{self, Future, Stream};
use futures::future::Either;
use hyper::{self, Client, Method, Request, Uri};
use hyper::header::{ContentLength, ContentType};
use std::io::{Error, ErrorKind};
use std::str::{self, FromStr};
use std::time::Duration;
use tokio_core::reactor::{Core, Timeout};

pub struct Response {
    pub status: u16,
    pub body: String,
}

pub fn request(path: &str, request_body: Option<String>) -> Result<Response, Error> {
    let mut request = Request::new(Method::Post, try!(uri(path)));
    match request_body {
        Some(json) => {
            debug!("Request body: {}", json);
            request.headers_mut().set(ContentType::json());
            request.headers_mut().set(ContentLength(json.len() as u64));
            request.set_body(json);
        }
        _ => {
            debug!("Empty request body...");
        }
    }

    let mut status: u16 = 0;
    let mut body = String::new();
    {
        let mut core = Core::new().unwrap();
        let handle = core.handle();
        let client = Client::new(&handle);
        let request = client.request(request).and_then(|res| {
            status = res.status().clone().into();
            res.body().for_each(|chunk| {
                body.push_str(str::from_utf8(&*chunk).unwrap());
                futures::future::ok(())
            })
        });

        let timeout = Timeout::new(Duration::from_millis(Settings::timeout()), &handle).unwrap();
        let work = request.select2(timeout).then(|res| match res {
            Ok(Either::A((got, _timeout))) => Ok(got),
            Ok(Either::B((_timeout_error, _get))) => Err(hyper::Error::Io(Error::new(
                ErrorKind::TimedOut,
                "Client timed out while connecting",
            ))),
            Err(Either::A((get_error, _timeout))) => Err(get_error),
            Err(Either::B((timeout_error, _get))) => Err(From::from(timeout_error)),
        });

        try!(
            core.run(work)
                .map_err(|err| Error::new(ErrorKind::Other, err))
        );
    }

    debug!("Response status: {}", status);
    debug!("Response body: {}", body);
    Ok(Response { status, body })
}

fn uri(path: &str) -> Result<Uri, Error> {
    let mut addr = Settings::base_url();
    addr.push_str(path);
    debug!("Request URL: {}", addr);

    Uri::from_str(&addr).map_err(|err| Error::new(ErrorKind::Other, err))
}

#[cfg(test)]
mod tests {

    use super::{request, Response};
    use rand;
    use std::env;
    use test_server::{self, http};

    #[test]
    fn request_with_body_is_content_type_json() {
        let server = test_server::serve(None);
        server.reply().status(http::StatusCode::BAD_REQUEST);
        env::set_var("K2_BASE_URL", format!("http://{}", &server.addr()));

        let mut body = String::new();
        for _ in 0..10 {
            body.push(rand::random::<u8>() as char);
        }

        let _r = request("", Some(body));

        let (parts, _body) = server.request().unwrap().into_parts();
        assert_eq!(
            parts.headers.get("content-type").unwrap(),
            "application/json"
        );
    }

    #[test]
    fn send_request_body_if_given() {
        let server = test_server::serve(None);
        server.reply().status(http::StatusCode::BAD_REQUEST);
        env::set_var("K2_BASE_URL", format!("http://{}", &server.addr()));

        let mut body = String::new();
        for _ in 0..10 {
            body.push(rand::random::<u8>() as char);
        }

        let _r = request("", Some(body.clone()));

        let (_parts, body) = server.request().unwrap().into_parts();
        assert_eq!(body, body);
    }

    #[test]
    fn if_no_json_is_given_send_empty_request_body() {
        let server = test_server::serve(None);
        server.reply().status(http::StatusCode::BAD_REQUEST);
        env::set_var("K2_BASE_URL", format!("http://{}", &server.addr()));

        let _r = request("", None);

        let (_parts, body) = server.request().unwrap().into_parts();
        assert_eq!(body.as_str(), "");
    }

    #[test]
    fn response_contains_status_and_body() {
        let server = test_server::serve(None);
        server
            .reply()
            .status(http::StatusCode::INTERNAL_SERVER_ERROR)
            .body("hello world");
        env::set_var("K2_BASE_URL", format!("http://{}", &server.addr()));

        let response: Response = request("", None).unwrap();
        assert_eq!(response.status, 500);
        assert_eq!(response.body, "hello world");
    }
}
<commit_msg>explicit assertion<commit_after>use super::settings::Settings;
use futures::{self, Future, Stream};
use futures::future::Either;
use hyper::{self, Client, Method, Request, Uri};
use hyper::header::{ContentLength, ContentType};
use std::io::{Error, ErrorKind};
use std::str::{self, FromStr};
use std::time::Duration;
use tokio_core::reactor::{Core, Timeout};

pub struct Response {
    pub status: u16,
    pub body: String,
}

pub fn request(path: &str, request_body: Option<String>) -> Result<Response, Error> {
    let mut request = Request::new(Method::Post, try!(uri(path)));
    match request_body {
        Some(json) => {
            debug!("Request body: {}", json);
            request.headers_mut().set(ContentType::json());
            request.headers_mut().set(ContentLength(json.len() as u64));
            request.set_body(json);
        }
        _ => {
            debug!("Empty request body...");
        }
    }

    let mut status: u16 = 0;
    let mut body = String::new();
    {
        let mut core = Core::new().unwrap();
        let handle = core.handle();
        let client = Client::new(&handle);
        let request = client.request(request).and_then(|res| {
            status = res.status().clone().into();
            res.body().for_each(|chunk| {
                body.push_str(str::from_utf8(&*chunk).unwrap());
                futures::future::ok(())
            })
        });

        let timeout = Timeout::new(Duration::from_millis(Settings::timeout()), &handle).unwrap();
        let work = request.select2(timeout).then(|res| match res {
            Ok(Either::A((got, _timeout))) => Ok(got),
            Ok(Either::B((_timeout_error, _get))) => Err(hyper::Error::Io(Error::new(
                ErrorKind::TimedOut,
                "Client timed out while connecting",
            ))),
            Err(Either::A((get_error, _timeout))) => Err(get_error),
            Err(Either::B((timeout_error, _get))) => Err(From::from(timeout_error)),
        });

        try!(
            core.run(work)
                .map_err(|err| Error::new(ErrorKind::Other, err))
        );
    }

    debug!("Response status: {}", status);
    debug!("Response body: {}", body);
    Ok(Response { status, body })
}

fn uri(path: &str) -> Result<Uri, Error> {
    let mut addr = Settings::base_url();
    addr.push_str(path);
    debug!("Request URL: {}", addr);

    Uri::from_str(&addr).map_err(|err| Error::new(ErrorKind::Other, err))
}

#[cfg(test)]
mod tests {

    use super::{request, Response};
    use rand;
    use std::env;
    use test_server::{self, http};

    #[test]
    fn request_with_body_is_content_type_json() {
        let server = test_server::serve(None);
        server.reply().status(http::StatusCode::BAD_REQUEST);
        env::set_var("K2_BASE_URL", format!("http://{}", &server.addr()));

        let mut body = String::new();
        for _ in 0..10 {
            body.push(rand::random::<u8>() as char);
        }

        let _r = request("", Some(body));

        let (parts, _body) = server.request().unwrap().into_parts();
        assert_eq!(
            parts.headers.get("content-type").unwrap(),
            "application/json"
        );
    }

    #[test]
    fn send_request_body_if_given() {
        let server = test_server::serve(None);
        server.reply().status(http::StatusCode::BAD_REQUEST);
        env::set_var("K2_BASE_URL", format!("http://{}", &server.addr()));

        let mut body = String::new();
        for _ in 0..10 {
            body.push(rand::random::<u8>() as char);
        }

        let _r = request("", Some(body.clone()));

        let (_parts, req_body) = server.request().unwrap().into_parts();
        assert_eq!(body, req_body);
    }

    #[test]
    fn if_no_json_is_given_send_empty_request_body() {
        let server = test_server::serve(None);
        server.reply().status(http::StatusCode::BAD_REQUEST);
        env::set_var("K2_BASE_URL", format!("http://{}", &server.addr()));

        let _r = request("", None);

        let (_parts, body) = server.request().unwrap().into_parts();
        assert_eq!(body.as_str(), "");
    }

    #[test]
    fn response_contains_status_and_body() {
        let server = test_server::serve(None);
        server
            .reply()
            .status(http::StatusCode::INTERNAL_SERVER_ERROR)
            .body("hello world");
        env::set_var("K2_BASE_URL", format!("http://{}", &server.addr()));

        let response: Response = request("", None).unwrap();
        assert_eq!(response.status, 500);
        assert_eq!(response.body, "hello world");
    }
}
<|endoftext|>


<commit_before>use conf;
use itertools::Itertools;
use lisp::LispExpr;
use lisp;
use rusqlite as sql;
use rustc::lint::{LateContext, LintArray, LintContext, LintPass, LateLintPass};
use rustc::middle::ty::TypeVariants;
use rustc_front::hir::*;
use std::io::Write;
use std::process::{Command, Stdio};
use std::str::from_utf8;
use syntax::ast::FloatTy;

#[derive(Debug)]
pub struct Herbie {
    conf: Option<conf::Conf>,
    initialized: bool,
    subs: Vec<(LispExpr, LispExpr)>,
}

impl Herbie {

    pub fn new() -> Herbie {
        Herbie {
            conf: None,
            initialized: false,
            subs: Vec::new(),
        }
    }

    pub fn init(&mut self) -> Result<(), sql::Error> {
        if self.initialized {
            return Ok(())
        }

        self.initialized = true;

        let conf = conf::read_conf();
        let conn = try!(sql::Connection::open_with_flags(
            conf.db_path.as_ref(), sql::SQLITE_OPEN_READ_ONLY
        ));
        let mut query = try!(conn.prepare("SELECT * FROM HerbieResults"));

        self.subs = try!(query.query(&[])).filter_map(|row| {
            match row {
                Ok(row) => {
                    let cmdin : String = row.get(1);
                    let cmdout : String = row.get(2);
                    // row.get(3) is opts ↔ Herbies options
                    let errin = row.get_checked(4).unwrap_or(0.);
                    let errout = row.get_checked(5).unwrap_or(0.);

                    if cmdin == cmdout || errin <= errout {
                        return None;
                    }

                    let mut parser = lisp::Parser::new();
                    match parser.parse(&cmdin) {
                        Ok(cmdin) => {
                            match parser.parse(&cmdout) {
                                Ok(cmdout) => {
                                    Some((cmdin, cmdout))
                                }
                                Err(..) => None,
                            }
                        }
                        Err(..) => None,
                    }
                }
                Err(..) => None,
            }
        }).collect();

        self.conf = Some(conf);

        Ok(())
    }

}

declare_lint!(pub HERBIE, Warn,
              "checks for numerical instability");

impl LintPass for Herbie {
    fn get_lints(&self) -> LintArray {
        lint_array!(HERBIE)
    }
}

impl LateLintPass for Herbie {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        let ty = cx.tcx.expr_ty(expr);

        if ty.sty != TypeVariants::TyFloat(FloatTy::TyF64) {
            return;
        }

        if let Err(err) =  self.init() {
            cx.span_lint_note(
                HERBIE,
                cx.krate.span,
                "Could not initialize Herbie-Lint",
                cx.krate.span,
                &format!("Got SQL error: {}", err)
            );
        }

        let mut got_match = false;
        for &(ref cmdin, ref cmdout) in &self.subs {
            if let Some(bindings) = LispExpr::match_expr(expr, cmdin) {
                report(cx, expr, cmdout, &bindings);
                got_match = true;
            }
        }

        let conf = self.conf.as_ref().unwrap();
        if !got_match && conf.use_herbie != conf::UseHerbieConf::No {
            try_with_herbie(cx, expr, &conf);
        }
    }
}

fn try_with_herbie(cx: &LateContext, expr: &Expr, conf: &conf::Conf) {
    let (lisp_expr, nb_ids, bindings) = match LispExpr::from_expr(expr) {
        Some(r) => r,
        None => return, // TODO: report
    };

    if lisp_expr.depth() <= 2 {
        return;
    }

    let seed : &str = &conf.herbie_seed;
    let mut command = Command::new("herbie-inout");
    let command = command
        .arg("--seed").arg(seed)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
    ;

    let mut child = if let Ok(child) = command.spawn() {
        child
    }
    else {
        if conf.use_herbie == conf::UseHerbieConf::Yes {
            // TODO: wiki
            cx.span_lint(HERBIE, expr.span, "Could not call Herbie");
        }
        return
    };

    // TODO: link to wiki about Herbie.toml
    cx.sess().diagnostic().span_note_without_error(expr.span, "Calling Herbie on the following expression, it might take a while");

    let params = (0..nb_ids).map(|id| format!("herbie{}", id)).join(" ");
    let lisp_expr = lisp_expr.to_lisp("herbie");
    let lisp_expr = format!("(lambda ({}) {})\n", params, lisp_expr);
    let lisp_expr = lisp_expr.as_bytes();
    child.stdin.as_mut().unwrap().write(lisp_expr).unwrap();

    let output = if let Ok(output) = child.wait_with_output() {
        if output.status.success() {
            if let Ok(output) = from_utf8(&output.stdout) {
                output.to_owned()
            }
            else {
                return
            }
        }
        else {
            return
        }
    }
    else {
        return
    };

    let mut output = output.lines();
    let errin = output.next().unwrap().split(' ').last().unwrap().parse::<f64>().unwrap();
    let errout = output.next().unwrap().split(' ').last().unwrap().parse::<f64>().unwrap();

    if errin <= errout {
        return
    }

    let mut parser = lisp::Parser::new();
    let cmdout = parser.parse(&output.next().unwrap()).unwrap();

    report(cx, expr, &cmdout, &bindings);
}

fn report(cx: &LateContext, expr: &Expr, cmdout: &LispExpr, bindings: &lisp::MatchBindings) {
    cx.struct_span_lint(HERBIE, expr.span, "Numerically unstable expression")
      .span_suggestion(expr.span, "Try this", cmdout.to_rust(cx, &bindings))
      .emit();
}
<commit_msg>Fix bug on SQL errors<commit_after>use conf;
use itertools::Itertools;
use lisp::LispExpr;
use lisp;
use rusqlite as sql;
use rustc::lint::{LateContext, LintArray, LintContext, LintPass, LateLintPass};
use rustc::middle::ty::TypeVariants;
use rustc_front::hir::*;
use std::io::Write;
use std::process::{Command, Stdio};
use std::str::from_utf8;
use syntax::ast::FloatTy;

#[derive(Debug)]
pub struct Herbie {
    conf: Option<conf::Conf>,
    initialized: bool,
    subs: Vec<(LispExpr, LispExpr)>,
}

impl Herbie {

    pub fn new() -> Herbie {
        Herbie {
            conf: None,
            initialized: false,
            subs: Vec::new(),
        }
    }

    pub fn init(&mut self) -> Result<(), sql::Error> {
        if self.initialized {
            return Ok(())
        }

        self.initialized = true;

        let conf = conf::read_conf();
        let conn = try!(sql::Connection::open_with_flags(
            conf.db_path.as_ref(), sql::SQLITE_OPEN_READ_ONLY
        ));
        let mut query = try!(conn.prepare("SELECT * FROM HerbieResults"));

        self.subs = try!(query.query(&[])).filter_map(|row| {
            match row {
                Ok(row) => {
                    let cmdin : String = row.get(1);
                    let cmdout : String = row.get(2);
                    // row.get(3) is opts ↔ Herbies options
                    let errin = row.get_checked(4).unwrap_or(0.);
                    let errout = row.get_checked(5).unwrap_or(0.);

                    if cmdin == cmdout || errin <= errout {
                        return None;
                    }

                    let mut parser = lisp::Parser::new();
                    match parser.parse(&cmdin) {
                        Ok(cmdin) => {
                            match parser.parse(&cmdout) {
                                Ok(cmdout) => {
                                    Some((cmdin, cmdout))
                                }
                                Err(..) => None,
                            }
                        }
                        Err(..) => None,
                    }
                }
                Err(..) => None,
            }
        }).collect();

        self.conf = Some(conf);

        Ok(())
    }

}

declare_lint!(pub HERBIE, Warn,
              "checks for numerical instability");

impl LintPass for Herbie {
    fn get_lints(&self) -> LintArray {
        lint_array!(HERBIE)
    }
}

impl LateLintPass for Herbie {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        let ty = cx.tcx.expr_ty(expr);

        if ty.sty != TypeVariants::TyFloat(FloatTy::TyF64) {
            return;
        }

        if let Err(err) =  self.init() {
            cx.span_lint_note(
                HERBIE,
                cx.krate.span,
                "Could not initialize Herbie-Lint",
                cx.krate.span,
                &format!("Got SQL error: {}", err)
            );
            return;
        }

        let mut got_match = false;
        for &(ref cmdin, ref cmdout) in &self.subs {
            if let Some(bindings) = LispExpr::match_expr(expr, cmdin) {
                report(cx, expr, cmdout, &bindings);
                got_match = true;
            }
        }

        let conf = self.conf.as_ref().unwrap();
        if !got_match && conf.use_herbie != conf::UseHerbieConf::No {
            try_with_herbie(cx, expr, &conf);
        }
    }
}

fn try_with_herbie(cx: &LateContext, expr: &Expr, conf: &conf::Conf) {
    let (lisp_expr, nb_ids, bindings) = match LispExpr::from_expr(expr) {
        Some(r) => r,
        None => return, // TODO: report
    };

    if lisp_expr.depth() <= 2 {
        return;
    }

    let seed : &str = &conf.herbie_seed;
    let mut command = Command::new("herbie-inout");
    let command = command
        .arg("--seed").arg(seed)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
    ;

    let mut child = if let Ok(child) = command.spawn() {
        child
    }
    else {
        if conf.use_herbie == conf::UseHerbieConf::Yes {
            // TODO: wiki
            cx.span_lint(HERBIE, expr.span, "Could not call Herbie");
        }
        return
    };

    // TODO: link to wiki about Herbie.toml
    cx.sess().diagnostic().span_note_without_error(expr.span, "Calling Herbie on the following expression, it might take a while");

    let params = (0..nb_ids).map(|id| format!("herbie{}", id)).join(" ");
    let lisp_expr = lisp_expr.to_lisp("herbie");
    let lisp_expr = format!("(lambda ({}) {})\n", params, lisp_expr);
    let lisp_expr = lisp_expr.as_bytes();
    child.stdin.as_mut().unwrap().write(lisp_expr).unwrap();

    let output = if let Ok(output) = child.wait_with_output() {
        if output.status.success() {
            if let Ok(output) = from_utf8(&output.stdout) {
                output.to_owned()
            }
            else {
                return
            }
        }
        else {
            return
        }
    }
    else {
        return
    };

    let mut output = output.lines();
    let errin = output.next().unwrap().split(' ').last().unwrap().parse::<f64>().unwrap();
    let errout = output.next().unwrap().split(' ').last().unwrap().parse::<f64>().unwrap();

    if errin <= errout {
        return
    }

    let mut parser = lisp::Parser::new();
    let cmdout = parser.parse(&output.next().unwrap()).unwrap();

    report(cx, expr, &cmdout, &bindings);
}

fn report(cx: &LateContext, expr: &Expr, cmdout: &LispExpr, bindings: &lisp::MatchBindings) {
    cx.struct_span_lint(HERBIE, expr.span, "Numerically unstable expression")
      .span_suggestion(expr.span, "Try this", cmdout.to_rust(cx, &bindings))
      .emit();
}
<|endoftext|>


<commit_before>#[repr(i32)]
pub enum PatternType {
  Solid = 0,
  Surface = 1,
  Linear = 2,
  Radial = 3,
  Mesh = 4,
  RasterSource = 5
}
<commit_msg>Copied Cairo documentation for pattern type<commit_after>//! Describe pattern type

///  cairo_pattern_type_t is used to describe the type of a given pattern.
///
/// The type of a pattern is determined by the function used to create it. The cairo_pattern_create_rgb() and cairo_pattern_create_rgba() functions create SOLID patterns. The remaining cairo_pattern_create functions map to pattern types in obvious ways.
///
/// The pattern type can be queried with cairo_pattern_get_type()
///
/// Most cairo_pattern_t functions can be called with a pattern of any type, (though trying to change the extend or filter for a solid pattern will have no effect). A notable exception is cairo_pattern_add_color_stop_rgb() and cairo_pattern_add_color_stop_rgba() which must only be called with gradient patterns (either LINEAR or RADIAL). Otherwise the pattern will be shutdown and put into an error state.
///
/// New entries may be added in future versions.
///
/// Since 1.2
#[repr(i32)]
pub enum PatternType {
  /// The pattern is a solid (uniform) color. It may be opaque or translucent, since 1.2.
  Solid = 0,
  /// The pattern is a based on a surface (an image), since 1.2.
  Surface = 1,
  /// The pattern is a linear gradient, since 1.2.
  Linear = 2,
  /// The pattern is a radial gradient, since 1.2.
  Radial = 3,
  /// The pattern is a mesh, since 1.12.
  Mesh = 4,
  /// The pattern is a user pattern providing raster data, since 1.12.
  RasterSource = 5
}
<|endoftext|>


<commit_before>use std::fs;
use std::fs::{PathExt, DirEntry};
use std::path::{Path, PathBuf};
use std::io::Error;

pub struct Index {
    path: PathBuf,
    entries: Vec<PathBuf>,
}

impl Index {
    fn new(path: &str) -> Index {
        return Index { path: PathBuf::from(path.to_string()), entries: vec![] }
    }

    // Finds all files inside and beneath the index path
    // and adds them to the index entries vector.
    fn populate(&mut self) {
        // The entries listed by read_dir include the root index path; we want
        // relative paths, so we get this length so that we can strip it.
        let prefix_length = match self.path.to_str() {
            Some(path) => path.len()+1,
            None => return,
        };

        // Start by getting any files in the root path. Since
        // read_directory_entries is already borrowing a mutable
        // reference to the index, so we can't also lend out a
        // reference to its path field, hence the clone.
        let path = self.path.clone();
        self.index_directory_files(&path, prefix_length);

        // Get an iterator that'll let us walk all of the subdirectories
        // of the index path, bailing out if there's an error of any kind.
        match fs::walk_dir(&self.path) {
            Ok(subdirectories) => {
                // Index any other files beneath the root directory.
                for directory in subdirectories {
                    match directory {
                        Ok(dir) => self.index_directory_files(&dir.path(), prefix_length),
                        Err(_) => (),
                    }
                }
            },
            Err(_) => return,
        };
    }

    // Helper method for populate.
    // Finds files for a particular directory, strips prefix_length leading
    // characters from their path, and adds them to the index entries vector.
    fn index_directory_files(&mut self, directory: &Path, prefix_length: usize) {
        match fs::read_dir(directory) {
            Ok(entries) => {
                // Put all of the file-based Path entries into the index.
                for entry in entries {
                    match relative_entry_path(entry, prefix_length) {
                        Some(entry_path) => self.entries.push(PathBuf::from(entry_path)),
                        _ => (),
                    }
                }
            },
            Err(_) => (),
        }
    }
}

/// Transforms a DirEntry object into an optional relative path string,
/// returning None if any errors occur or if the entry is not a file.
fn relative_entry_path(entry: Result<DirEntry, Error>, prefix_length: usize) -> Option<String> {
    match entry {
        Ok(e) => match e.path().metadata() {
            Ok(metadata) => if metadata.is_file() {
                match e.path().to_str() {
                    Some(path) => Some(path[prefix_length..].to_string()),
                    _ => None,
                }
            } else {
                None
            },
            _ => None,
        },
        _ => None,
    }
}

#[test]
fn new_creates_index_with_passed_path() {
    let path = "my path";
    let index = Index::new(path);

    // Get the index path as a string.
    let index_path = match index.path.to_str() {
        Some(value) => value,
        None => ""
    };

    assert_eq!(index_path, path);
}

#[test]
fn new_creates_index_with_empty_array() {
    let path = "my path";
    let index = Index::new(path);
    let empty_array: Vec<PathBuf> = vec![];

    assert!(index.entries == empty_array);
}

#[test]
fn populate_adds_all_files_to_entries() {
    let path = "tests/sample";
    let index = &mut Index::new(path);
    let expected_array = vec![PathBuf::from("root_file"), PathBuf::from("directory/nested_file")];
    index.populate();

    assert!(index.entries == expected_array);
}
<commit_msg>Use doc comments. Update constructor to take a PathBuf.<commit_after>use std::fs;
use std::fs::{PathExt, DirEntry};
use std::path::{Path, PathBuf};
use std::io::Error;

pub struct Index {
    path: PathBuf,
    entries: Vec<PathBuf>,
}

impl Index {
    /// Finds all files inside and beneath the index path
    /// and adds them to the index entries vector.
    pub fn populate(&mut self) {
        // The entries listed by read_dir include the root index path; we want
        // relative paths, so we get this length so that we can strip it.
        let prefix_length = match self.path.to_str() {
            Some(path) => path.len()+1,
            None => return,
        };

        // Start by getting any files in the root path. Since
        // read_directory_entries is already borrowing a mutable
        // reference to the index, so we can't also lend out a
        // reference to its path field, hence the clone.
        let path = self.path.clone();
        self.index_directory_files(&path, prefix_length);

        // Get an iterator that'll let us walk all of the subdirectories
        // of the index path, bailing out if there's an error of any kind.
        match fs::walk_dir(&self.path) {
            Ok(subdirectories) => {
                // Index any other files beneath the root directory.
                for directory in subdirectories {
                    match directory {
                        Ok(dir) => self.index_directory_files(&dir.path(), prefix_length),
                        Err(_) => (),
                    }
                }
            },
            Err(_) => return,
        };
    }

    /// Helper method for populate.
    /// Finds files for a particular directory, strips prefix_length leading
    /// characters from their path, and adds them to the index entries vector.
    fn index_directory_files(&mut self, directory: &Path, prefix_length: usize) {
        match fs::read_dir(directory) {
            Ok(entries) => {
                // Put all of the file-based Path entries into the index.
                for entry in entries {
                    match relative_entry_path(entry, prefix_length) {
                        Some(entry_path) => self.entries.push(PathBuf::from(entry_path)),
                        _ => (),
                    }
                }
            },
            Err(_) => (),
        }
    }
}

pub fn new(path: PathBuf) -> Index {
    return Index { path: path, entries: vec![] }
}

/// Transforms a DirEntry object into an optional relative path string,
/// returning None if any errors occur or if the entry is not a file.
fn relative_entry_path(entry: Result<DirEntry, Error>, prefix_length: usize) -> Option<String> {
    match entry {
        Ok(e) => match e.path().metadata() {
            Ok(metadata) => if metadata.is_file() {
                match e.path().to_str() {
                    Some(path) => Some(path[prefix_length..].to_string()),
                    _ => None,
                }
            } else {
                None
            },
            _ => None,
        },
        _ => None,
    }
}

#[test]
fn new_creates_index_with_passed_path() {
    let path = "my path";
    let index = Index::new(path);

    // Get the index path as a string.
    let index_path = match index.path.to_str() {
        Some(value) => value,
        None => ""
    };

    assert_eq!(index_path, path);
}

#[test]
fn new_creates_index_with_empty_array() {
    let path = "my path";
    let index = Index::new(path);
    let empty_array: Vec<PathBuf> = vec![];

    assert!(index.entries == empty_array);
}

#[test]
fn populate_adds_all_files_to_entries() {
    let path = "tests/sample";
    let index = &mut Index::new(path);
    let expected_array = vec![PathBuf::from("root_file"), PathBuf::from("directory/nested_file")];
    index.populate();

    assert!(index.entries == expected_array);
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate encoding;
extern crate html5ever;

mod lib1;

fn main() {
    // get html string with code
    let html_str = match lib1::get_page_html("063760") {
        Err(x)  => {
            println!("{}", x);
            return;
        },
        Ok(x)   => String::from(x)
    };

    // parse html
    let dom = match lib1::parse_html(html_str) {
        Err(x)  => {
            println!("{}", x);
            return;
        },
        Ok(x)   => x
    };

    //
    let doc_node = dom.document;
    let node = doc_node.borrow();

    println!("{:?}", node.node);
}
<commit_msg>파싱된 데이터에서 텍스트 엘리먼트만 분리하는 중<commit_after>extern crate hyper;
extern crate encoding;
extern crate html5ever;

use html5ever::rcdom::{RcDom, Handle, Node, NodeEnum};

mod lib1;

fn main() {
    // get html string with code
    let html_str = match lib1::get_page_html("063760") {
        Err(x)  => {
            println!("{}", x);
            return;
        },
        Ok(x)   => String::from(x)
    };

    // parse html
    let dom = match lib1::parse_html(html_str) {
        Err(x)  => {
            println!("{}", x);
            return;
        },
        Ok(x)   => x
    };

    // sort text elements
    let handle_root = dom.document;
    //let node = handle_root.borrow();

    let mut queue : Vec<Handle> = Vec::new();
    let mut text_queue : Vec<Handle> = Vec::new();

    queue.push(handle_root);
    while queue.len() != 0 {
        let handle = queue.remove(0);
        let node = handle.borrow();
        match &node.node {
            Text    => {
                text_queue.push(handle.clone());
            },
            _       => {}
        }
    }

    while text_queue.len() != 0 {
        let handle = text_queue.remove(0);
        println();
    }

    //println!("{:?}", node.node);
}
<|endoftext|>


<commit_before>extern crate log;
extern crate env_logger;

extern crate afterparty;
#[macro_use]
extern crate clap;
extern crate lettre;
extern crate git2;
extern crate hyper;
extern crate regex;
extern crate slack_hook;
extern crate toml;

mod auth;
mod config;
mod email;
mod repo;
mod slack;
mod taste;

use afterparty::{Delivery, Event, Hub};
use hyper::Server;
use std::collections::HashMap;
use std::error::Error;
use std::path::Path;
use std::sync::{Arc, Mutex};

use config::Config;

#[cfg_attr(rustfmt, rustfmt_skip)]
const TASTER_USAGE: &'static str = "\
EXAMPLES:
  taste -w /path/to/workdir -s my_secret
  taste -l 0.0.0.0:1234 -w /path/to/workdir -s my_secret";

#[derive(Clone, Debug)]
pub struct Commit {
    pub id: git2::Oid,
    pub msg: String,
    pub url: String,
}

#[derive(Clone, Debug)]
pub struct Push {
    pub head_commit: Commit,
    pub push_ref: Option<String>,
    pub pusher: Option<String>,
}

pub fn main() {
    use clap::{Arg, App};

    env_logger::init().unwrap();

    let args = App::new("taster")
        .version("0.0.1")
        .about("Tastes GitHub commits.")
        .arg(Arg::with_name("listen_addr")
            .short("l")
            .long("listen_addr")
            .takes_value(true)
            .value_name("IP:PORT")
            .default_value("0.0.0.0:4567")
            .help("Listen address and port for webhook delivery"))
        .arg(Arg::with_name("github_repo")
            .short("r")
            .long("github_repo")
            .takes_value(true)
            .required(true)
            .value_name("GH_REPO")
            .default_value("https://github.com/ms705/taster")
            .help("GitHub repository to taste"))
        .arg(Arg::with_name("email_addr")
            .long("email_addr")
            .takes_value(true)
            .required(false)
            .help("Email address to send notifications to"))
        .arg(Arg::with_name("default_regression_reporting_threshold")
            .long("default_regression_reporting_threshold")
            .takes_value(true)
            .default_value("0.1")
            .help("Relative performance threshold below which a result is considered a \
                   regression that needs reporting (0.1 = +/-10%)."))
        .arg(Arg::with_name("default_improvement_reporting_threshold")
            .long("default_improvement_reporting_threshold")
            .takes_value(true)
            .default_value("0.1")
            .help("Relative performance threshold above which a result is considered an \
                   improvement that needs reporting (0.1 = +/-10%)."))
        .arg(Arg::with_name("secret")
            .short("s")
            .long("secret")
            .takes_value(true)
            .required(false)
            .help("GitHub webhook secret"))
        .arg(Arg::with_name("slack_hook_url")
            .long("slack_hook_url")
            .takes_value(true)
            .required(false)
            .help("Slack webhook URL to push notifications to"))
        .arg(Arg::with_name("slack_channel")
            .long("slack_channel")
            .takes_value(true)
            .required(false)
            .default_value("#soup-test")
            .help("Slack channel for notifications"))
        .arg(Arg::with_name("taste_commit")
            .long("taste_commit")
            .short("t")
            .takes_value(true)
            .required(false)
            .help("Do a one-off taste of a specific commit"))
        .arg(Arg::with_name("taste_head_only")
            .long("taste_head_only")
            .required(false)
            .help("When multiple commits are pushed, taste the head commit only"))
        .arg(Arg::with_name("verbose_notifications")
            .long("verbose_notifications")
            .required(false)
            .help("List all benchmarks in notifications even if the results have not changed \
                   significantly"))
        .arg(Arg::with_name("workdir")
            .short("w")
            .long("workdir")
            .takes_value(true)
            .required(true)
            .value_name("REPO_DIR")
            .help("Directory holding the workspace repo"))
        .after_help(TASTER_USAGE)
        .get_matches();

    let addr = args.value_of("listen_addr").unwrap();
    let email_notification_addr = args.value_of("email_addr");
    let repo = args.value_of("github_repo").unwrap();
    let secret = args.value_of("secret");
    let slack_hook_url = args.value_of("slack_hook_url");
    let slack_channel = args.value_of("slack_channel");
    let taste_commit = args.value_of("taste_commit");
    let taste_head_only = args.is_present("taste_head_only");
    let workdir = Path::new(args.value_of("workdir").unwrap());
    let verbose_notify = args.is_present("verbose_notifications");
    let improvement_threshold =
        value_t_or_exit!(args, "default_improvement_reporting_threshold", f64);
    let regression_threshold =
        value_t_or_exit!(args, "default_regression_reporting_threshold", f64);

    let mut history = HashMap::new();
    let ws = repo::Workspace::new(repo, workdir);
    let en = if let Some(addr) = email_notification_addr {
        Some(email::EmailNotifier::new(addr, repo))
    } else {
        None
    };
    let sn = if let Some(url) = slack_hook_url {
        Some(slack::SlackNotifier::new(url, slack_channel.unwrap(), repo, verbose_notify))
    } else {
        None
    };

    if taste_commit.is_some() {
        let cid = if let Some("HEAD") = taste_commit {
            ws.repo.head().unwrap().target().unwrap().clone()
        } else {
            git2::Oid::from_str(taste_commit.unwrap()).unwrap()
        };
        match ws.repo.find_object(cid, None) {
            Err(e) => panic!(format!("{}", e.description())),
            Ok(o) => {
                let cobj = o.as_commit().unwrap();
                let hc = Commit {
                    id: cobj.id(),
                    msg: String::from(cobj.message().unwrap()),
                    url: format!("{}/commit/{}", repo, cobj.id()),
                };
                // fake a push
                let push = Push {
                    head_commit: hc,
                    push_ref: None,
                    pusher: None,
                };
                let res = taste::taste_commit(&ws,
                                              &mut history,
                                              &push,
                                              &push.head_commit,
                                              improvement_threshold,
                                              regression_threshold);
                match res {
                    Err(e) => println!("ERROR: failed to taste{}: {}", cid, e),
                    Ok((cfg, tr)) => {
                        // email notification
                        if en.is_some() {
                            en.as_ref().unwrap().notify(cfg.as_ref(), &tr, &push).unwrap();
                        }
                        // slack notification
                        if sn.is_some() {
                            sn.as_ref().unwrap().notify(cfg.as_ref(), &tr, &push).unwrap();
                        }
                        // We're done
                        return;
                    }
                }
            }
        };
    }

    // If we get here, we must be running in continuous mode
    if let None = secret {
        panic!("--secret must be set when in continuous webhook handler mode");
    }

    // Initialize history by tasting the HEAD commit of each branch
    {
        let branches = ws.branch_heads();
        for (b, c) in branches.iter() {
            println!("tasting HEAD of {}: {} / {}",
                     b,
                     c.id(),
                     c.message().unwrap());
            let hc = Commit {
                id: c.id(),
                msg: String::from(c.message().unwrap()),
                url: format!("{}/commit/{}", repo, c.id()),
            };
            // fake a push
            let push = Push {
                head_commit: hc,
                push_ref: Some(b.clone()),
                pusher: None,
            };
            let res = taste::taste_commit(&ws,
                                          &mut history,
                                          &push,
                                          &push.head_commit,
                                          improvement_threshold,
                                          regression_threshold);
            assert!(res.is_ok());
        }
    }

    let hl = Arc::new(Mutex::new(history));
    let wsl = Mutex::new(ws);

    let mut hub = Hub::new();
    hub.handle_authenticated("push", secret.unwrap(), move |delivery: &Delivery| {
        match delivery.payload {
            Event::Push { ref _ref, ref commits, ref head_commit, ref pusher, .. } => {
                println!("Handling {} commits pushed by {}",
                         commits.len(),
                         pusher.name);

                // Data structures to represent info from webhook
                let hc = Commit {
                    id: git2::Oid::from_str(&head_commit.id).unwrap(),
                    msg: head_commit.message.clone(),
                    url: head_commit.url.clone(),
                };
                let push = Push {
                    head_commit: hc,
                    push_ref: Some(_ref.clone()),
                    pusher: Some(pusher.name.clone()),
                };

                let notify = |cfg: Option<&Config>, res: &taste::TastingResult, push: &Push| {
                    // email notification
                    if en.is_some() {
                        en.as_ref().unwrap().notify(cfg, &res, &push).unwrap();
                    }
                    // slack notification
                    if sn.is_some() {
                        sn.as_ref().unwrap().notify(cfg, &res, &push).unwrap();
                    }
                };

                {
                    let ws = wsl.lock().unwrap();
                    let mut history = hl.lock().unwrap();
                    // First taste the head commit
                    ws.fetch().unwrap();
                    let head_res = taste::taste_commit(&ws,
                                                       &mut history,
                                                       &push,
                                                       &push.head_commit,
                                                       improvement_threshold,
                                                       regression_threshold);
                    match head_res {
                        Err(e) => {
                            println!("ERROR: failed to taste HEAD commit {}: {}",
                                     head_commit.id,
                                     e)
                        }
                        Ok((cfg, tr)) => {
                            notify(cfg.as_ref(), &tr, &push);
                            // Taste others if needed
                            if !taste_head_only || !tr.build || !tr.bench {
                                for c in commits.iter() {
                                    if c.id == head_commit.id {
                                        // skip HEAD as we've already tested it
                                        continue;
                                    }
                                    let cur_c = Commit {
                                        id: git2::Oid::from_str(&c.id).unwrap(),
                                        msg: c.message.clone(),
                                        url: c.url.clone(),
                                    };
                                    // taste
                                    let res = taste::taste_commit(&ws,
                                                                  &mut history,
                                                                  &push,
                                                                  &cur_c,
                                                                  improvement_threshold,
                                                                  regression_threshold);
                                    match res {
                                        Err(e) => {
                                            println!("ERROR: failed to taste commit {}: {}",
                                                     c.id,
                                                     e)
                                        }
                                        Ok((cfg, tr)) => notify(cfg.as_ref(), &tr, &push),
                                    }
                                }
                            } else if !commits.is_empty() {
                                println!("Skipping {} remaining commits in push!",
                                         commits.len() - 1);
                            }
                        }
                    }
                }
            }
            _ => (),
        }
    });

    let srvc = Server::http(&addr[..])
        .unwrap()
        .handle(hub);

    println!("Taster listening on {}", addr);
    srvc.unwrap();
}
<commit_msg>Turn off broken bisect-ish logic<commit_after>extern crate log;
extern crate env_logger;

extern crate afterparty;
#[macro_use]
extern crate clap;
extern crate lettre;
extern crate git2;
extern crate hyper;
extern crate regex;
extern crate slack_hook;
extern crate toml;

mod auth;
mod config;
mod email;
mod repo;
mod slack;
mod taste;

use afterparty::{Delivery, Event, Hub};
use hyper::Server;
use std::collections::HashMap;
use std::error::Error;
use std::path::Path;
use std::sync::{Arc, Mutex};

use config::Config;

#[cfg_attr(rustfmt, rustfmt_skip)]
const TASTER_USAGE: &'static str = "\
EXAMPLES:
  taste -w /path/to/workdir -s my_secret
  taste -l 0.0.0.0:1234 -w /path/to/workdir -s my_secret";

#[derive(Clone, Debug)]
pub struct Commit {
    pub id: git2::Oid,
    pub msg: String,
    pub url: String,
}

#[derive(Clone, Debug)]
pub struct Push {
    pub head_commit: Commit,
    pub push_ref: Option<String>,
    pub pusher: Option<String>,
}

pub fn main() {
    use clap::{Arg, App};

    env_logger::init().unwrap();

    let args = App::new("taster")
        .version("0.0.1")
        .about("Tastes GitHub commits.")
        .arg(Arg::with_name("listen_addr")
            .short("l")
            .long("listen_addr")
            .takes_value(true)
            .value_name("IP:PORT")
            .default_value("0.0.0.0:4567")
            .help("Listen address and port for webhook delivery"))
        .arg(Arg::with_name("github_repo")
            .short("r")
            .long("github_repo")
            .takes_value(true)
            .required(true)
            .value_name("GH_REPO")
            .default_value("https://github.com/ms705/taster")
            .help("GitHub repository to taste"))
        .arg(Arg::with_name("email_addr")
            .long("email_addr")
            .takes_value(true)
            .required(false)
            .help("Email address to send notifications to"))
        .arg(Arg::with_name("default_regression_reporting_threshold")
            .long("default_regression_reporting_threshold")
            .takes_value(true)
            .default_value("0.1")
            .help("Relative performance threshold below which a result is considered a \
                   regression that needs reporting (0.1 = +/-10%)."))
        .arg(Arg::with_name("default_improvement_reporting_threshold")
            .long("default_improvement_reporting_threshold")
            .takes_value(true)
            .default_value("0.1")
            .help("Relative performance threshold above which a result is considered an \
                   improvement that needs reporting (0.1 = +/-10%)."))
        .arg(Arg::with_name("secret")
            .short("s")
            .long("secret")
            .takes_value(true)
            .required(false)
            .help("GitHub webhook secret"))
        .arg(Arg::with_name("slack_hook_url")
            .long("slack_hook_url")
            .takes_value(true)
            .required(false)
            .help("Slack webhook URL to push notifications to"))
        .arg(Arg::with_name("slack_channel")
            .long("slack_channel")
            .takes_value(true)
            .required(false)
            .default_value("#soup-test")
            .help("Slack channel for notifications"))
        .arg(Arg::with_name("taste_commit")
            .long("taste_commit")
            .short("t")
            .takes_value(true)
            .required(false)
            .help("Do a one-off taste of a specific commit"))
        .arg(Arg::with_name("taste_head_only")
            .long("taste_head_only")
            .required(false)
            .help("When multiple commits are pushed, taste the head commit only"))
        .arg(Arg::with_name("verbose_notifications")
            .long("verbose_notifications")
            .required(false)
            .help("List all benchmarks in notifications even if the results have not changed \
                   significantly"))
        .arg(Arg::with_name("workdir")
            .short("w")
            .long("workdir")
            .takes_value(true)
            .required(true)
            .value_name("REPO_DIR")
            .help("Directory holding the workspace repo"))
        .after_help(TASTER_USAGE)
        .get_matches();

    let addr = args.value_of("listen_addr").unwrap();
    let email_notification_addr = args.value_of("email_addr");
    let repo = args.value_of("github_repo").unwrap();
    let secret = args.value_of("secret");
    let slack_hook_url = args.value_of("slack_hook_url");
    let slack_channel = args.value_of("slack_channel");
    let taste_commit = args.value_of("taste_commit");
    let taste_head_only = args.is_present("taste_head_only");
    let workdir = Path::new(args.value_of("workdir").unwrap());
    let verbose_notify = args.is_present("verbose_notifications");
    let improvement_threshold =
        value_t_or_exit!(args, "default_improvement_reporting_threshold", f64);
    let regression_threshold =
        value_t_or_exit!(args, "default_regression_reporting_threshold", f64);

    let mut history = HashMap::new();
    let ws = repo::Workspace::new(repo, workdir);
    let en = if let Some(addr) = email_notification_addr {
        Some(email::EmailNotifier::new(addr, repo))
    } else {
        None
    };
    let sn = if let Some(url) = slack_hook_url {
        Some(slack::SlackNotifier::new(url, slack_channel.unwrap(), repo, verbose_notify))
    } else {
        None
    };

    if taste_commit.is_some() {
        let cid = if let Some("HEAD") = taste_commit {
            ws.repo.head().unwrap().target().unwrap().clone()
        } else {
            git2::Oid::from_str(taste_commit.unwrap()).unwrap()
        };
        match ws.repo.find_object(cid, None) {
            Err(e) => panic!(format!("{}", e.description())),
            Ok(o) => {
                let cobj = o.as_commit().unwrap();
                let hc = Commit {
                    id: cobj.id(),
                    msg: String::from(cobj.message().unwrap()),
                    url: format!("{}/commit/{}", repo, cobj.id()),
                };
                // fake a push
                let push = Push {
                    head_commit: hc,
                    push_ref: None,
                    pusher: None,
                };
                let res = taste::taste_commit(&ws,
                                              &mut history,
                                              &push,
                                              &push.head_commit,
                                              improvement_threshold,
                                              regression_threshold);
                match res {
                    Err(e) => println!("ERROR: failed to taste{}: {}", cid, e),
                    Ok((cfg, tr)) => {
                        // email notification
                        if en.is_some() {
                            en.as_ref().unwrap().notify(cfg.as_ref(), &tr, &push).unwrap();
                        }
                        // slack notification
                        if sn.is_some() {
                            sn.as_ref().unwrap().notify(cfg.as_ref(), &tr, &push).unwrap();
                        }
                        // We're done
                        return;
                    }
                }
            }
        };
    }

    // If we get here, we must be running in continuous mode
    if let None = secret {
        panic!("--secret must be set when in continuous webhook handler mode");
    }

    // Initialize history by tasting the HEAD commit of each branch
    {
        let branches = ws.branch_heads();
        for (b, c) in branches.iter() {
            println!("tasting HEAD of {}: {} / {}",
                     b,
                     c.id(),
                     c.message().unwrap());
            let hc = Commit {
                id: c.id(),
                msg: String::from(c.message().unwrap()),
                url: format!("{}/commit/{}", repo, c.id()),
            };
            // fake a push
            let push = Push {
                head_commit: hc,
                push_ref: Some(b.clone()),
                pusher: None,
            };
            let res = taste::taste_commit(&ws,
                                          &mut history,
                                          &push,
                                          &push.head_commit,
                                          improvement_threshold,
                                          regression_threshold);
            assert!(res.is_ok());
        }
    }

    let hl = Arc::new(Mutex::new(history));
    let wsl = Mutex::new(ws);

    let mut hub = Hub::new();
    hub.handle_authenticated("push", secret.unwrap(), move |delivery: &Delivery| {
        match delivery.payload {
            Event::Push { ref _ref, ref commits, ref head_commit, ref pusher, .. } => {
                println!("Handling {} commits pushed by {}",
                         commits.len(),
                         pusher.name);

                // Data structures to represent info from webhook
                let hc = Commit {
                    id: git2::Oid::from_str(&head_commit.id).unwrap(),
                    msg: head_commit.message.clone(),
                    url: head_commit.url.clone(),
                };
                let push = Push {
                    head_commit: hc,
                    push_ref: Some(_ref.clone()),
                    pusher: Some(pusher.name.clone()),
                };

                let notify = |cfg: Option<&Config>, res: &taste::TastingResult, push: &Push| {
                    // email notification
                    if en.is_some() {
                        en.as_ref().unwrap().notify(cfg, &res, &push).unwrap();
                    }
                    // slack notification
                    if sn.is_some() {
                        sn.as_ref().unwrap().notify(cfg, &res, &push).unwrap();
                    }
                };

                {
                    let ws = wsl.lock().unwrap();
                    let mut history = hl.lock().unwrap();
                    // First taste the head commit
                    ws.fetch().unwrap();
                    let head_res = taste::taste_commit(&ws,
                                                       &mut history,
                                                       &push,
                                                       &push.head_commit,
                                                       improvement_threshold,
                                                       regression_threshold);
                    match head_res {
                        Err(e) => {
                            println!("ERROR: failed to taste HEAD commit {}: {}",
                                     head_commit.id,
                                     e)
                        }
                        Ok((cfg, tr)) => {
                            notify(cfg.as_ref(), &tr, &push);
                            // Taste others if needed
                            if !taste_head_only {
                                for c in commits.iter() {
                                    if c.id == head_commit.id {
                                        // skip HEAD as we've already tested it
                                        continue;
                                    }
                                    let cur_c = Commit {
                                        id: git2::Oid::from_str(&c.id).unwrap(),
                                        msg: c.message.clone(),
                                        url: c.url.clone(),
                                    };
                                    // taste
                                    let res = taste::taste_commit(&ws,
                                                                  &mut history,
                                                                  &push,
                                                                  &cur_c,
                                                                  improvement_threshold,
                                                                  regression_threshold);
                                    match res {
                                        Err(e) => {
                                            println!("ERROR: failed to taste commit {}: {}",
                                                     c.id,
                                                     e)
                                        }
                                        Ok((cfg, tr)) => notify(cfg.as_ref(), &tr, &push),
                                    }
                                }
                            } else if !commits.is_empty() {
                                println!("Skipping {} remaining commits in push!",
                                         commits.len() - 1);
                            }
                        }
                    }
                }
            }
            _ => (),
        }
    });

    let srvc = Server::http(&addr[..])
        .unwrap()
        .handle(hub);

    println!("Taster listening on {}", addr);
    srvc.unwrap();
}
<|endoftext|>