<commit_before>//! An adapter providing time-related services, such as the current
//! timestamp or the current time of day.

use foxbox_taxonomy::api::{ Error, InternalError, Operation, User };
use foxbox_taxonomy::channel::*;
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ format, Duration as ValDuration, Range, TimeStamp, Value };

use transformable_channels::mpsc::*;

use std::collections::HashMap;
use std::sync::{ Arc, Mutex };

use chrono;
use chrono::{ DateTime, Duration, Local, NaiveTime, Timelike };
use timer;

static ADAPTER_NAME: &'static str = "Clock adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

#[derive(Clone)]
enum Op {
    Enter(Id<Channel>, Value),
    Exit(Id<Channel>, Value),
}

enum Movement { Enter, Exit }

pub struct Clock {
    /// Timer used to dispatch `register_watch` requests.
    timer: Mutex<timer::Timer>,

    getter_timestamp_id: Id<Channel>,
    getter_time_of_day_id: Id<Channel>,
    getter_interval_id: Id<Channel>,
}

/// A guard used to cancel watching for values.
struct Guard(Vec<timer::Guard>);
impl AdapterWatchGuard for Guard {
}

impl Clock {
    pub fn id() -> Id<AdapterId> {
        Id::new("clock@link.mozilla.org")
    }
    pub fn service_clock_id() -> Id<ServiceId> {
        Id::new("service:clock@link.mozilla.org")
    }
    pub fn getter_timestamp_id() -> Id<Channel> {
        Id::new("getter:timestamp.clock@link.mozilla.org")
    }
    pub fn getter_time_of_day_id() -> Id<Channel> {
        Id::new("getter:timeofday.clock@link.mozilla.org")
    }
    pub fn getter_interval_id() -> Id<Channel> {
        Id::new("getter:interval.clock@link.mozilla.org")
    }
}
impl Adapter for Clock {
    fn id(&self) -> Id<AdapterId> {
        Self::id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            if id == self.getter_timestamp_id {
                let date = TimeStamp::from_datetime(chrono::UTC::now());
                (id, Ok(Some(Value::TimeStamp(date))))
            } else if id == self.getter_time_of_day_id {
                use chrono::Timelike;
                let date = chrono::Local::now();
                let duration = chrono::Duration::seconds(date.num_seconds_from_midnight() as i64);
                (id, Ok(Some(Value::Duration(ValDuration::from(duration)))))
            } else {
                (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            }
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User) -> ResultMap<Id<Channel>, (), Error> {
        values.drain()
            .map(|(id, _)| {
                (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            })
            .collect()
    }

    fn register_watch(&self, mut watch: Vec<WatchTarget>) -> WatchResult
    {
        watch.drain(..).map(|(id, filter, tx)| {
            let tx = tx.map(|msg| {
                match msg {
                    Op::Enter(id, value) => {
                        WatchEvent::Enter {
                            id: id,
                            value: value
                        }
                    },
                    Op::Exit(id, value) => {
                        WatchEvent::Exit {
                            id: id,
                            value: value
                        }
                    },
                }
            });
            (id.clone(), match filter {
                Some(Value::Range(range)) => self.aux_register_watch(&id, &*range, Box::new(tx.clone())),
                _ => Err(Error::GetterRequiresThresholdForWatching(id)),
            })
        }).collect()
    }
}

impl Clock {
    fn aux_register_watch(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        match () {
            _ if *id == self.getter_time_of_day_id => self.aux_register_watch_timeofday(id, range, tx),
            _ if *id == self.getter_timestamp_id => self.aux_register_watch_timestamp(id, range, tx),
            _ if *id == self.getter_interval_id => self.aux_register_watch_interval(id, range, tx),
            _ => Err(Error::OperationNotSupported(Operation::Watch, id.clone()))
        }
    }

    fn aux_register_watch_interval(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        // Determine when to call the trigger.
        let duration = match *range {
            Eq (ref val) | Geq (ref val) => {
                try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into()
            }
            _ => return Err(Error::InvalidValue)
        };

        debug!(target: "clock@link.mozilla.org", "[clock@link.mozilla.org] Scheduling a repeating watch with a duration of {}", duration);

        let id = id.clone();
        let guard = self.timer.lock().unwrap().schedule_repeating(duration, move || {
            // Send Enter followed immediately by Exit, to make sure that Thinkerbell
            // rules reset themselves.
            let _ = tx.send(Op::Enter(id.clone(),
                Value::Duration(ValDuration::from(duration))));
            let _ = tx.send(Op::Exit(id.clone(),
                Value::Duration(ValDuration::from(duration))));
        });
        Ok(Box::new(Guard(vec![guard])))
    }

    fn aux_register_watch_timeofday(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        // Determine when to call the trigger. Repeat duration is always one day.
        let mut thresholds = match *range {
            Leq (ref val) => {
                // Equivalent to BetweenEq { min: 0am, max: val }
                let ts : chrono::Duration = try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, chrono::Duration::seconds(0)), (Movement::Exit, ts)]
            }
            Geq (ref val) => {
                // Equivalent to BetweenEq { min: val, max: 0am }
                let ts = try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, ts), (Movement::Exit, Duration::days(1))]
            }
            BetweenEq { ref min, ref max } => {
                let ts_min = try!(min.as_duration().map_err(Error::TypeError))
                    .clone().into();
                let ts_max = try!(max.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, ts_min), (Movement::Exit, ts_max)]
            }
            OutOfStrict { ref min, ref max } => {
                // Equivalent to BetweenEq {min: 0am, max: min} and BetweenEq {min: max, max: 0am}
                let ts_min = try!(min.as_duration().map_err(Error::TypeError))
                    .clone().into();
                let ts_max = try!(max.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Exit, ts_min), (Movement::Enter, ts_max)]
            }
            Eq (ref val) => {
                let ts : chrono::Duration = try!(val.as_duration().map_err(Error::TypeError))
                    .clone().into();
                vec![(Movement::Enter, ts.clone()), (Movement::Exit, ts)]
            }
        };

        // Determine when the next timers needs to launch.
        let now = chrono::Local::now();
        let guards : Vec<timer::Guard> = thresholds.drain(..).filter_map(|(movement, threshold)| {
            let date = match Self::get_next_date(&now, threshold) {
                Err(_) => return None,
                Ok(date) => date,
            };
            let id = id.clone();
            let tx = tx.clone();
            let guard = self.timer.lock().unwrap().schedule(date, Some(Duration::days(1)), move || {
                let naive_time = chrono::Local::now().time();
                let duration = Duration::hours(naive_time.hour() as i64)
                    + Duration::minutes(naive_time.minute() as i64)
                    + Duration::seconds(naive_time.second() as i64);

                let event = match movement {
                    Movement::Enter => Op::Enter(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                    Movement::Exit => Op::Exit(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                };
                let _ = tx.send(event);
            });
            Some(guard)
        }).collect();
        Ok(Box::new(Guard(guards)))
    }

    fn get_next_date(now: &DateTime<Local>, time_of_day: Duration)
        -> Result<DateTime<Local>, Error>
    {
        match chrono::Local::today().and_time(NaiveTime::from_hms(0, 0, 0) + time_of_day) {
            None => Err(Error::InvalidValue),
            Some(date) => {
                if date >= *now  {
                    Ok(date)
                } else {
                    // Otherwise, shift to tomorrow.
                    match date.checked_add(Duration::days(1)) {
                        None => Err(Error::InvalidValue),
                        Some(date) => Ok(date)
                    }
                }
            }
        }
    }

    fn aux_register_watch_timestamp(&self, id: &Id<Channel>, range: &Range, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        // Now determine when/if to call the trigger.
        let mut thresholds = match *range {
            Leq (_) => {
                // This variant doesn't make sense.
                return Ok(Box::new(Guard(vec![])))
            }
            Geq (ref val) | Eq (ref val) => {
                let ts = *try!(val.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                vec![(Movement::Enter, ts)]
            }
            OutOfStrict { ref min, ref max } => {
                let ts_min = *try!(min.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                let ts_max = *try!(max.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                vec![(Movement::Exit, ts_min), (Movement::Enter, ts_max)]
            }
            BetweenEq { ref min, ref max } => {
                let ts_min = *try!(min.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                let ts_max = *try!(max.as_timestamp().map_err(Error::TypeError))
                    .as_datetime();
                vec![(Movement::Enter, ts_min), (Movement::Exit, ts_max)]
            }
        };

        // Determine when/if the next timers needs to launch.
        let now = chrono::UTC::now();
        let guards : Vec<timer::Guard> = thresholds.drain(..).filter_map(|(movement, date)| {
            if date < now {
                return None
            }
            let id = id.clone();
            let tx = tx.clone();
            let guard = self.timer.lock().unwrap().schedule_with_date(date, move || {
                let naive_time = chrono::Local::now().time();
                let duration = Duration::hours(naive_time.hour() as i64)
                    + Duration::minutes(naive_time.minute() as i64)
                    + Duration::seconds(naive_time.second() as i64);

                let event = match movement {
                    Movement::Enter => Op::Enter(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                    Movement::Exit => Op::Exit(id.clone(),
                        Value::Duration(ValDuration::from(duration))),
                };
                let _ = tx.send(event);
            });
            Some(guard)
        }).collect();
        Ok(Box::new(Guard(guards)))
    }
}

impl Clock {
    pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
        let getter_timestamp_id = Clock::getter_timestamp_id();
        let getter_time_of_day_id = Clock::getter_time_of_day_id();
        let getter_interval_id = Clock::getter_interval_id();
        let service_clock_id = Clock::service_clock_id();
        let adapter_id = Clock::id();
        let clock = Arc::new(Clock {
            timer: Mutex::new(timer::Timer::new()),
            getter_timestamp_id: getter_timestamp_id.clone(),
            getter_time_of_day_id: getter_time_of_day_id.clone(),
            getter_interval_id: getter_interval_id.clone(),
        });
        try!(adapt.add_adapter(clock));
        let mut service = Service::empty(&service_clock_id, &adapter_id);
        service.properties.insert("model".to_owned(), "Mozilla clock v1".to_owned());
        try!(adapt.add_service(service));
        try!(adapt.add_channel(Channel {
            feature: Id::new("clock/time-of-day-seconds"),
            supports_fetch: Some(Signature::returns(Maybe::Required(format::DURATION.clone()))),
            supports_watch: Some(Signature {
                accepts: Maybe::Required(format::DURATION.clone()),
                returns: Maybe::Required(format::DURATION.clone())
            }),
            id: getter_time_of_day_id,
            service: service_clock_id.clone(),
            adapter: adapter_id.clone(),
            ..Channel::default()
        }));
        try!(adapt.add_channel(Channel {
            feature: Id::new("clock/time-timestamp-rfc-3339"),
            supports_fetch: Some(Signature::returns(Maybe::Required(format::TIMESTAMP.clone()))),
            supports_watch: Some(Signature {
                accepts: Maybe::Required(format::TIMESTAMP.clone()),
                returns: Maybe::Required(format::TIMESTAMP.clone())
            }),
            id: getter_timestamp_id,
            service: service_clock_id.clone(),
            adapter: adapter_id.clone(),
            ..Channel::default()
        }));
        try!(adapt.add_channel(Channel {
            feature: Id::new("clock/time-interval-seconds"),
            supports_watch: Some(Signature {
                accepts: Maybe::Required(format::DURATION.clone()),
                returns: Maybe::Required(format::TIMESTAMP.clone())
            }),
            id: getter_interval_id,
            service: service_clock_id.clone(),
            adapter: adapter_id.clone(),
            ..Channel::default()
        }));
        Ok(())
    }
}
<commit_msg>Clock: Getting rid of centralized Value<commit_after>//! An adapter providing time-related services, such as the current
//! timestamp or the current time of day.

use foxbox_taxonomy::api::{ Error, InternalError, Operation, User };
use foxbox_taxonomy::channel::*;
use foxbox_taxonomy::manager::*;
use foxbox_taxonomy::services::*;
use foxbox_taxonomy::values::{ format, Duration as ValDuration, Range, TimeStamp, Value };

use transformable_channels::mpsc::*;

use std::collections::HashMap;
use std::sync::{ Arc, Mutex };

use chrono;
use chrono::{ DateTime, Duration, Local, NaiveTime, Timelike };
use timer;

static ADAPTER_NAME: &'static str = "Clock adapter (built-in)";
static ADAPTER_VENDOR: &'static str = "team@link.mozilla.org";
static ADAPTER_VERSION: [u32;4] = [0, 0, 0, 0];

#[derive(Clone)]
enum Op {
    Enter(Id<Channel>, Value),
    Exit(Id<Channel>, Value),
}

enum Movement { Enter, Exit }

pub struct Clock {
    /// Timer used to dispatch `register_watch` requests.
    timer: Mutex<timer::Timer>,

    getter_timestamp_id: Id<Channel>,
    getter_time_of_day_id: Id<Channel>,
    getter_interval_id: Id<Channel>,
}

/// A guard used to cancel watching for values.
struct Guard(Vec<timer::Guard>);
impl AdapterWatchGuard for Guard {
}

impl Clock {
    pub fn id() -> Id<AdapterId> {
        Id::new("clock@link.mozilla.org")
    }
    pub fn service_clock_id() -> Id<ServiceId> {
        Id::new("service:clock@link.mozilla.org")
    }
    pub fn getter_timestamp_id() -> Id<Channel> {
        Id::new("getter:timestamp.clock@link.mozilla.org")
    }
    pub fn getter_time_of_day_id() -> Id<Channel> {
        Id::new("getter:timeofday.clock@link.mozilla.org")
    }
    pub fn getter_interval_id() -> Id<Channel> {
        Id::new("getter:interval.clock@link.mozilla.org")
    }
}
impl Adapter for Clock {
    fn id(&self) -> Id<AdapterId> {
        Self::id()
    }

    fn name(&self) -> &str {
        ADAPTER_NAME
    }

    fn vendor(&self) -> &str {
        ADAPTER_VENDOR
    }

    fn version(&self) -> &[u32;4] {
        &ADAPTER_VERSION
    }

    fn fetch_values(&self, mut set: Vec<Id<Channel>>, _: User) -> ResultMap<Id<Channel>, Option<Value>, Error> {
        set.drain(..).map(|id| {
            if id == self.getter_timestamp_id {
                let date = TimeStamp::from_datetime(chrono::UTC::now());
                (id, Ok(Some(Value::new(date))))
            } else if id == self.getter_time_of_day_id {
                use chrono::Timelike;
                let date = chrono::Local::now();
                let duration = chrono::Duration::seconds(date.num_seconds_from_midnight() as i64);
                (id, Ok(Some(Value::new(ValDuration::from(duration)))))
            } else {
                (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            }
        }).collect()
    }

    fn send_values(&self, mut values: HashMap<Id<Channel>, Value>, _: User) -> ResultMap<Id<Channel>, (), Error> {
        values.drain()
            .map(|(id, _)| {
                (id.clone(), Err(Error::InternalError(InternalError::NoSuchChannel(id))))
            })
            .collect()
    }

    fn register_watch(&self, mut watch: Vec<WatchTarget>) -> WatchResult
    {
        watch.drain(..).map(|(id, filter, tx)| {
            let tx = tx.map(|msg| {
                match msg {
                    Op::Enter(id, value) => {
                        WatchEvent::Enter {
                            id: id,
                            value: value
                        }
                    },
                    Op::Exit(id, value) => {
                        WatchEvent::Exit {
                            id: id,
                            value: value
                        }
                    },
                }
            });
            (id.clone(), match filter {
                Some(range) => self.aux_register_watch(&id, &range, Box::new(tx.clone())),
                _ => Err(Error::GetterRequiresThresholdForWatching(id)),
            })
        }).collect()
    }
}

impl Clock {
    fn aux_register_watch(&self, id: &Id<Channel>, range: &Value, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        match () {
            _ if *id == self.getter_time_of_day_id => self.aux_register_watch_timeofday(id, range, tx),
            _ if *id == self.getter_timestamp_id => self.aux_register_watch_timestamp(id, range, tx),
            _ if *id == self.getter_interval_id => self.aux_register_watch_interval(id, range, tx),
            _ => Err(Error::OperationNotSupported(Operation::Watch, id.clone()))
        }
    }

    fn aux_register_watch_interval(&self, id: &Id<Channel>, value: &Value, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        // Determine when to call the trigger.
        let duration = try!(value.cast::<ValDuration>()).clone();

        debug!(target: "clock@link.mozilla.org", "[clock@link.mozilla.org] Scheduling a repeating watch with a duration of {:?}", duration);

        let id = id.clone();
        let guard = self.timer.lock().unwrap().schedule_repeating(duration.clone().into(), move || {
            // Send Enter followed immediately by Exit, to make sure that Thinkerbell
            // rules reset themselves.
            let _ = tx.send(Op::Enter(id.clone(),
                Value::new(duration.clone())));
            let _ = tx.send(Op::Exit(id.clone(),
                Value::new(duration.clone())));
        });
        Ok(Box::new(Guard(vec![guard])))
    }

    fn aux_register_watch_timeofday(&self, id: &Id<Channel>, value: &Value, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;

        let range = try!(value.cast::<Range<ValDuration>>());

        // Determine when to call the trigger. Repeat duration is always one day.
        let mut thresholds = match *range {
            Leq (ref val) => {
                // Equivalent to BetweenEq { min: 0am, max: val }
                let ts : chrono::Duration = val.clone().into();
                vec![(Movement::Enter, chrono::Duration::seconds(0)), (Movement::Exit, ts)]
            }
            Geq (ref val) => {
                // Equivalent to BetweenEq { min: val, max: 0am }
                let ts = val.clone().into();
                vec![(Movement::Enter, ts), (Movement::Exit, Duration::days(1))]
            }
            BetweenEq { ref min, ref max } => {
                let ts_min = min.clone().into();
                let ts_max = max.clone().into();
                vec![(Movement::Enter, ts_min), (Movement::Exit, ts_max)]
            }
            OutOfStrict { ref min, ref max } => {
                // Equivalent to BetweenEq {min: 0am, max: min} and BetweenEq {min: max, max: 0am}
                let ts_min = min.clone().into();
                let ts_max = max.clone().into();
                vec![(Movement::Exit, ts_min), (Movement::Enter, ts_max)]
            }
            Eq (ref val) => {
                let ts : chrono::Duration = val.clone().into();
                vec![(Movement::Enter, ts.clone()), (Movement::Exit, ts)]
            }
        };

        // Determine when the next timers needs to launch.
        let now = chrono::Local::now();
        let guards : Vec<timer::Guard> = thresholds.drain(..).filter_map(|(movement, threshold)| {
            let date = match Self::get_next_date(&now, threshold) {
                Err(_) => return None,
                Ok(date) => date,
            };
            let id = id.clone();
            let tx = tx.clone();
            let guard = self.timer.lock().unwrap().schedule(date, Some(Duration::days(1)), move || {
                let naive_time = chrono::Local::now().time();
                let duration = Duration::hours(naive_time.hour() as i64)
                    + Duration::minutes(naive_time.minute() as i64)
                    + Duration::seconds(naive_time.second() as i64);

                let event = match movement {
                    Movement::Enter => Op::Enter(id.clone(),
                        Value::new(ValDuration::from(duration))),
                    Movement::Exit => Op::Exit(id.clone(),
                        Value::new(ValDuration::from(duration))),
                };
                let _ = tx.send(event);
            });
            Some(guard)
        }).collect();
        Ok(Box::new(Guard(guards)))
    }

    fn get_next_date(now: &DateTime<Local>, time_of_day: Duration)
        -> Result<DateTime<Local>, Error>
    {
        match chrono::Local::today().and_time(NaiveTime::from_hms(0, 0, 0) + time_of_day) {
            None => Err(Error::InvalidValue),
            Some(date) => {
                if date >= *now  {
                    Ok(date)
                } else {
                    // Otherwise, shift to tomorrow.
                    match date.checked_add(Duration::days(1)) {
                        None => Err(Error::InvalidValue),
                        Some(date) => Ok(date)
                    }
                }
            }
        }
    }

    fn aux_register_watch_timestamp(&self, id: &Id<Channel>, value: &Value, tx: Box<ExtSender<Op>>)
        -> Result<Box<AdapterWatchGuard>, Error>
    {
        use foxbox_taxonomy::values::Range::*;
        let range = try!(value.cast::<Range<TimeStamp>>());

        // Now determine when/if to call the trigger.
        let mut thresholds = match *range {
            Leq (_) => {
                // This variant doesn't make sense.
                return Ok(Box::new(Guard(vec![])))
            }
            Geq (ref val) | Eq (ref val) => {
                let ts = *val.as_datetime();
                vec![(Movement::Enter, ts)]
            }
            OutOfStrict { ref min, ref max } => {
                let ts_min = *min.as_datetime();
                let ts_max = *max.as_datetime();
                vec![(Movement::Exit, ts_min), (Movement::Enter, ts_max)]
            }
            BetweenEq { ref min, ref max } => {
                let ts_min = *min.as_datetime();
                let ts_max = *max.as_datetime();
                vec![(Movement::Enter, ts_min), (Movement::Exit, ts_max)]
            }
        };

        // Determine when/if the next timers needs to launch.
        let now = chrono::UTC::now();
        let guards : Vec<timer::Guard> = thresholds.drain(..).filter_map(|(movement, date)| {
            if date < now {
                return None
            }
            let id = id.clone();
            let tx = tx.clone();
            let guard = self.timer.lock().unwrap().schedule_with_date(date, move || {
                let naive_time = chrono::Local::now().time();
                let duration = Duration::hours(naive_time.hour() as i64)
                    + Duration::minutes(naive_time.minute() as i64)
                    + Duration::seconds(naive_time.second() as i64);

                let event = match movement {
                    Movement::Enter => Op::Enter(id.clone(),
                        Value::new(ValDuration::from(duration))),
                    Movement::Exit => Op::Exit(id.clone(),
                        Value::new(ValDuration::from(duration))),
                };
                let _ = tx.send(event);
            });
            Some(guard)
        }).collect();
        Ok(Box::new(Guard(guards)))
    }
}

impl Clock {
    pub fn init(adapt: &Arc<AdapterManager>) -> Result<(), Error> {
        let getter_timestamp_id = Clock::getter_timestamp_id();
        let getter_time_of_day_id = Clock::getter_time_of_day_id();
        let getter_interval_id = Clock::getter_interval_id();
        let service_clock_id = Clock::service_clock_id();
        let adapter_id = Clock::id();
        let clock = Arc::new(Clock {
            timer: Mutex::new(timer::Timer::new()),
            getter_timestamp_id: getter_timestamp_id.clone(),
            getter_time_of_day_id: getter_time_of_day_id.clone(),
            getter_interval_id: getter_interval_id.clone(),
        });
        try!(adapt.add_adapter(clock));
        let mut service = Service::empty(&service_clock_id, &adapter_id);
        service.properties.insert("model".to_owned(), "Mozilla clock v1".to_owned());
        try!(adapt.add_service(service));
        try!(adapt.add_channel(Channel {
            feature: Id::new("clock/time-of-day-seconds"),
            supports_fetch: Some(Signature::returns(Maybe::Required(format::DURATION.clone()))),
            supports_watch: Some(Signature {
                accepts: Maybe::Required(format::DURATION.clone()),
                returns: Maybe::Required(format::DURATION.clone())
            }),
            id: getter_time_of_day_id,
            service: service_clock_id.clone(),
            adapter: adapter_id.clone(),
            ..Channel::default()
        }));
        try!(adapt.add_channel(Channel {
            feature: Id::new("clock/time-timestamp-rfc-3339"),
            supports_fetch: Some(Signature::returns(Maybe::Required(format::TIMESTAMP.clone()))),
            supports_watch: Some(Signature {
                accepts: Maybe::Required(format::TIMESTAMP.clone()),
                returns: Maybe::Required(format::TIMESTAMP.clone())
            }),
            id: getter_timestamp_id,
            service: service_clock_id.clone(),
            adapter: adapter_id.clone(),
            ..Channel::default()
        }));
        try!(adapt.add_channel(Channel {
            feature: Id::new("clock/time-interval-seconds"),
            supports_watch: Some(Signature {
                accepts: Maybe::Required(format::DURATION.clone()),
                returns: Maybe::Required(format::TIMESTAMP.clone())
            }),
            id: getter_interval_id,
            service: service_clock_id.clone(),
            adapter: adapter_id.clone(),
            ..Channel::default()
        }));
        Ok(())
    }
}
<|endoftext|>


<commit_before>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives like
//! `// ignore-tidy-linelength`.

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

const COLS: usize = 100;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns whether `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns whether `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let skip_cr = contents.contains("ignore-tidy-cr");
        let skip_tab = contents.contains("ignore-tidy-tab");
        let skip_length = contents.contains("ignore-tidy-linelength");
        let skip_end_whitespace = contents.contains("ignore-tidy-end-whitespace");
        let mut trailing_new_lines = 0;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if !skip_length && line.chars().count() > COLS
                && !long_line_is_ok(line) {
                    err(&format!("line longer than {} chars", COLS));
            }
            if line.contains('\t') && !skip_tab {
                err("tab character");
            }
            if !skip_end_whitespace && (line.ends_with(' ') || line.ends_with('\t')) {
                err("trailing whitespace");
            }
            if line.contains('\r') && !skip_cr {
                err("CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 | 2 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
    })
}
<commit_msg>Added check for copyright notices.<commit_after>//! Tidy check to enforce various stylistic guidelines on the Rust codebase.
//!
//! Example checks are:
//!
//! * No lines over 100 characters.
//! * No tabs.
//! * No trailing whitespace.
//! * No CR characters.
//! * No `TODO` or `XXX` directives.
//! * No unexplained ` ```ignore ` or ` ```rust,ignore ` doc tests.
//!
//! A number of these checks can be opted-out of with various directives like
//! `// ignore-tidy-linelength`.

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

const COLS: usize = 100;

const UNEXPLAINED_IGNORE_DOCTEST_INFO: &str = r#"unexplained "```ignore" doctest; try one:

* make the test actually pass, by adding necessary imports and declarations, or
* use "```text", if the code is not Rust code, or
* use "```compile_fail,Ennnn", if the code is expected to fail at compile time, or
* use "```should_panic", if the code is expected to fail at run time, or
* use "```no_run", if the code should type-check but not necessary linkable/runnable, or
* explain it like "```ignore (cannot-test-this-because-xxxx)", if the annotation cannot be avoided.

"#;

const LLVM_UNREACHABLE_INFO: &str = r"\
C++ code used llvm_unreachable, which triggers undefined behavior
when executed when assertions are disabled.
Use llvm::report_fatal_error for increased robustness.";

/// Parser states for `line_is_url`.
#[derive(PartialEq)]
#[allow(non_camel_case_types)]
enum LIUState {
    EXP_COMMENT_START,
    EXP_LINK_LABEL_OR_URL,
    EXP_URL,
    EXP_END,
}

/// Returns whether `line` appears to be a line comment containing an URL,
/// possibly with a Markdown link label in front, and nothing else.
/// The Markdown link label, if present, may not contain whitespace.
/// Lines of this form are allowed to be overlength, because Markdown
/// offers no way to split a line in the middle of a URL, and the lengths
/// of URLs to external references are beyond our control.
fn line_is_url(line: &str) -> bool {
    use self::LIUState::*;
    let mut state: LIUState = EXP_COMMENT_START;

    for tok in line.split_whitespace() {
        match (state, tok) {
            (EXP_COMMENT_START, "//") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "///") => state = EXP_LINK_LABEL_OR_URL,
            (EXP_COMMENT_START, "//!") => state = EXP_LINK_LABEL_OR_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.len() >= 4 && w.starts_with('[') && w.ends_with("]:")
                => state = EXP_URL,

            (EXP_LINK_LABEL_OR_URL, w)
                if w.starts_with("http://") || w.starts_with("https://")
                => state = EXP_END,

            (EXP_URL, w)
                if w.starts_with("http://") || w.starts_with("https://") || w.starts_with("../")
                => state = EXP_END,

            (_, _) => return false,
        }
    }

    state == EXP_END
}

/// Returns whether `line` is allowed to be longer than the normal limit.
/// Currently there is only one exception, for long URLs, but more
/// may be added in the future.
fn long_line_is_ok(line: &str) -> bool {
    if line_is_url(line) {
        return true;
    }

    false
}

pub fn check(path: &Path, bad: &mut bool) {
    let mut contents = String::new();
    super::walk(path, &mut super::filter_dirs, &mut |file| {
        let filename = file.file_name().unwrap().to_string_lossy();
        let extensions = [".rs", ".py", ".js", ".sh", ".c", ".cpp", ".h"];
        if extensions.iter().all(|e| !filename.ends_with(e)) ||
           filename.starts_with(".#") {
            return
        }

        contents.truncate(0);
        t!(t!(File::open(file), file).read_to_string(&mut contents));

        if contents.is_empty() {
            tidy_error!(bad, "{}: empty file", file.display());
        }

        let skip_cr = contents.contains("ignore-tidy-cr");
        let skip_tab = contents.contains("ignore-tidy-tab");
        let skip_length = contents.contains("ignore-tidy-linelength");
        let skip_end_whitespace = contents.contains("ignore-tidy-end-whitespace");
        let skip_copyright = contents.contains("ignore-tidy-copyright");
        let mut trailing_new_lines = 0;
        for (i, line) in contents.split('\n').enumerate() {
            let mut err = |msg: &str| {
                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
            };
            if !skip_length && line.chars().count() > COLS
                && !long_line_is_ok(line) {
                    err(&format!("line longer than {} chars", COLS));
            }
            if !skip_tab && line.contains('\t') {
                err("tab character");
            }
            if !skip_end_whitespace && (line.ends_with(' ') || line.ends_with('\t')) {
                err("trailing whitespace");
            }
            if !skip_cr && line.contains('\r') {
                err("CR character");
            }
            if filename != "style.rs" {
                if line.contains("TODO") {
                    err("TODO is deprecated; use FIXME")
                }
                if line.contains("//") && line.contains(" XXX") {
                    err("XXX is deprecated; use FIXME")
                }
            }
            if !skip_copyright && (line.starts_with("// Copyright") ||
                                   line.starts_with("# Copyright") ||
                                   line.starts_with("Copyright"))
                               && (line.contains("Rust Developers") ||
                                   line.contains("Rust Project Developers")) {
                err("copyright notices attributed to the Rust Project Developers are deprecated");
            }
            if line.ends_with("```ignore") || line.ends_with("```rust,ignore") {
                err(UNEXPLAINED_IGNORE_DOCTEST_INFO);
            }
            if filename.ends_with(".cpp") && line.contains("llvm_unreachable") {
                err(LLVM_UNREACHABLE_INFO);
            }
            if line.is_empty() {
                trailing_new_lines += 1;
            } else {
                trailing_new_lines = 0;
            }
        }
        match trailing_new_lines {
            0 => tidy_error!(bad, "{}: missing trailing newline", file.display()),
            1 | 2 => {}
            n => tidy_error!(bad, "{}: too many trailing newlines ({})", file.display(), n),
        };
    })
}
<|endoftext|>


<commit_before>use std::result;

use library;
use nameutil::module_name;

pub type Result = result::Result<String, String>;

pub trait AsStr {
    fn as_str(&self) -> &str;
}

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(library: &library::Library, type_id: library::TypeId) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let type_ = library.type_(type_id);
    match type_ {
        &Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => ok("String"),

                Type => ok("Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        &Enumeration(ref enum_) => Ok(enum_.name.clone()),
        &Interface(ref interface) => Ok(interface.name.clone()),
        &Class(ref klass) => Ok(klass.name.clone()),
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

pub fn used_rust_type(library: &library::Library, type_id: library::TypeId) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let rust_type = match library.type_(type_id) {
        &Fundamental(fund) => {
            match fund {
                Type => Ok("Type".into()),
                _ => Err("Don't need use".into()),
            }
        },
        &Enumeration(ref enum_) => Ok(enum_.name.clone()),
        &Interface(ref interface) => Ok(interface.name.clone()),
        &Class(ref klass) => Ok(klass.name.clone()),
        _ => Err("Don't need use".into()),
    };
    if type_id.ns_id == library::MAIN_NAMESPACE || type_id.ns_id == library::INTERNAL_NAMESPACE {
        rust_type
    } else {
        rust_type.map(|s| format!("{}::{}",
            module_name(&library.namespace(type_id.ns_id).name), s))
    }
}

pub fn parameter_rust_type(library: &library::Library, type_id:library::TypeId, direction: library::ParameterDirection) -> Result {
    use library::Type::*;
    let type_ = library.type_(type_id);
    let rust_type = if type_id.ns_id == library::MAIN_NAMESPACE ||
        type_id.ns_id == library::INTERNAL_NAMESPACE {
        rust_type(library, type_id)
    } else {
        rust_type(library, type_id).map(|s| format!("{}::{}",
            module_name(&library.namespace(type_id.ns_id).name), s))
    };
    match type_ {
        &Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 {
                match direction {
                    library::ParameterDirection::In => Ok("&str".into()),
                    library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        &Enumeration(_) => format_parameter(rust_type, direction),

        &Class(_) => {
            match direction {
                library::ParameterDirection::In => rust_type.map(|s| format!("&{}", s)),
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        },
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}
<commit_msg>Fixed usage of glib::types::Type<commit_after>use std::result;

use library;
use nameutil::module_name;

pub type Result = result::Result<String, String>;

pub trait AsStr {
    fn as_str(&self) -> &str;
}

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(library: &library::Library, type_id: library::TypeId) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let type_ = library.type_(type_id);
    match type_ {
        &Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => ok("String"),

                Type => ok("types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        &Enumeration(ref enum_) => Ok(enum_.name.clone()),
        &Interface(ref interface) => Ok(interface.name.clone()),
        &Class(ref klass) => Ok(klass.name.clone()),
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

pub fn used_rust_type(library: &library::Library, type_id: library::TypeId) -> Result {
    use library::Type::*;
    let rust_type = match library.type_(type_id) {
        &Enumeration(ref enum_) => Ok(enum_.name.clone()),
        &Interface(ref interface) => Ok(interface.name.clone()),
        &Class(ref klass) => Ok(klass.name.clone()),
        _ => Err("Don't need use".into()),
    };
    if type_id.ns_id == library::MAIN_NAMESPACE || type_id.ns_id == library::INTERNAL_NAMESPACE {
        rust_type
    } else {
        rust_type.map(|s| format!("{}::{}",
            module_name(&library.namespace(type_id.ns_id).name), s))
    }
}

pub fn parameter_rust_type(library: &library::Library, type_id:library::TypeId, direction: library::ParameterDirection) -> Result {
    use library::Type::*;
    let type_ = library.type_(type_id);
    let rust_type = if type_id.ns_id == library::MAIN_NAMESPACE ||
        type_id.ns_id == library::INTERNAL_NAMESPACE {
        rust_type(library, type_id)
    } else {
        rust_type(library, type_id).map(|s| format!("{}::{}",
            module_name(&library.namespace(type_id.ns_id).name), s))
    };
    match type_ {
        &Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 {
                match direction {
                    library::ParameterDirection::In => Ok("&str".into()),
                    library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        &Enumeration(_) => format_parameter(rust_type, direction),

        &Class(_) => {
            match direction {
                library::ParameterDirection::In => rust_type.map(|s| format!("&{}", s)),
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        },
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}
<|endoftext|>


<commit_before>#![allow(clippy::single_match_else)]
use rustc_tools_util::VersionInfo;

#[test]
fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() {
    let clippy_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");

    for krate in &["clippy_lints", "clippy_utils"] {
        let krate_meta = clippy_meta
            .packages
            .iter()
            .find(|package| package.name == *krate)
            .expect("could not obtain cargo metadata");
        assert_eq!(krate_meta.version, clippy_meta.packages[0].version);
        for package in &clippy_meta.packages[0].dependencies {
            if package.name == *krate {
                assert!(package.req.matches(&krate_meta.version));
                break;
            }
        }
    }
}

#[test]
fn check_that_clippy_has_the_same_major_version_as_rustc() {
    // do not run this test inside the upstream rustc repo:
    // https://github.com/rust-lang/rust-clippy/issues/6683
    if option_env!("RUSTC_TEST_SUITE").is_some() {
        return;
    }

    let clippy_version = rustc_tools_util::get_version_info!();
    let clippy_major = clippy_version.major;
    let clippy_minor = clippy_version.minor;
    let clippy_patch = clippy_version.patch;

    // get the rustc version either from the rustc installed with the toolchain file or from
    // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`.
    let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string());
    let rustc_version = String::from_utf8(
        std::process::Command::new(&rustc)
            .arg("--version")
            .output()
            .expect("failed to run `rustc --version`")
            .stdout,
    )
    .unwrap();
    // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
    let vsplit: Vec<&str> = rustc_version
        .split(' ')
        .nth(1)
        .unwrap()
        .split('-')
        .next()
        .unwrap()
        .split('.')
        .collect();
    match vsplit.as_slice() {
        [rustc_major, rustc_minor, _rustc_patch] => {
            // clippy 0.1.XX should correspond to rustc 1.XX.0
            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
            assert_eq!(
                clippy_minor.to_string(),
                *rustc_major,
                "clippy minor version does not equal rustc major version"
            );
            assert_eq!(
                clippy_patch.to_string(),
                *rustc_minor,
                "clippy patch version does not equal rustc minor version"
            );
            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
            // we don't want our tests failing suddenly
        },
        _ => {
            panic!("Failed to parse rustc version: {:?}", vsplit);
        },
    };
}
<commit_msg>Revert "Fix versioncheck test"<commit_after>#![allow(clippy::single_match_else)]
use rustc_tools_util::VersionInfo;

#[test]
fn check_that_clippy_lints_and_clippy_utils_have_the_same_version_as_clippy() {
    let clippy_meta = cargo_metadata::MetadataCommand::new()
        .no_deps()
        .exec()
        .expect("could not obtain cargo metadata");

    for krate in &["clippy_lints", "clippy_utils"] {
        let krate_meta = cargo_metadata::MetadataCommand::new()
            .current_dir(std::env::current_dir().unwrap().join(krate))
            .no_deps()
            .exec()
            .expect("could not obtain cargo metadata");
        assert_eq!(krate_meta.packages[0].version, clippy_meta.packages[0].version);
        for package in &clippy_meta.packages[0].dependencies {
            if package.name == *krate {
                assert!(package.req.matches(&krate_meta.packages[0].version));
                break;
            }
        }
    }
}

#[test]
fn check_that_clippy_has_the_same_major_version_as_rustc() {
    // do not run this test inside the upstream rustc repo:
    // https://github.com/rust-lang/rust-clippy/issues/6683
    if option_env!("RUSTC_TEST_SUITE").is_some() {
        return;
    }

    let clippy_version = rustc_tools_util::get_version_info!();
    let clippy_major = clippy_version.major;
    let clippy_minor = clippy_version.minor;
    let clippy_patch = clippy_version.patch;

    // get the rustc version either from the rustc installed with the toolchain file or from
    // `RUSTC_REAL` if Clippy is build in the Rust repo with `./x.py`.
    let rustc = std::env::var("RUSTC_REAL").unwrap_or_else(|_| "rustc".to_string());
    let rustc_version = String::from_utf8(
        std::process::Command::new(&rustc)
            .arg("--version")
            .output()
            .expect("failed to run `rustc --version`")
            .stdout,
    )
    .unwrap();
    // extract "1 XX 0" from "rustc 1.XX.0-nightly (<commit> <date>)"
    let vsplit: Vec<&str> = rustc_version
        .split(' ')
        .nth(1)
        .unwrap()
        .split('-')
        .next()
        .unwrap()
        .split('.')
        .collect();
    match vsplit.as_slice() {
        [rustc_major, rustc_minor, _rustc_patch] => {
            // clippy 0.1.XX should correspond to rustc 1.XX.0
            assert_eq!(clippy_major, 0); // this will probably stay the same for a long time
            assert_eq!(
                clippy_minor.to_string(),
                *rustc_major,
                "clippy minor version does not equal rustc major version"
            );
            assert_eq!(
                clippy_patch.to_string(),
                *rustc_minor,
                "clippy patch version does not equal rustc minor version"
            );
            // do not check rustc_patch because when a stable-patch-release is made (like 1.50.2),
            // we don't want our tests failing suddenly
        },
        _ => {
            panic!("Failed to parse rustc version: {:?}", vsplit);
        },
    };
}
<|endoftext|>


<commit_before>//  * This file is part of the uutils coreutils package.
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.
//! Provides a summary representation of a filesystem.
//!
//! A [`Filesystem`] struct represents a device containing a
//! filesystem mounted at a particular directory. It also includes
//! information on amount of space available and amount of space used.
use std::path::Path;

#[cfg(unix)]
use uucore::fsext::statfs;
use uucore::fsext::{FsUsage, MountInfo};

/// Summary representation of a filesystem.
///
/// A [`Filesystem`] struct represents a device containing a
/// filesystem mounted at a particular directory. The
/// [`Filesystem::mount_info`] field exposes that information. The
/// [`Filesystem::usage`] field provides information on the amount of
/// space available on the filesystem and the amount of space used.
#[derive(Debug, Clone)]
pub(crate) struct Filesystem {
    /// Information about the mounted device, mount directory, and related options.
    pub mount_info: MountInfo,

    /// Information about the amount of space used on the filesystem.
    pub usage: FsUsage,
}

/// Find the mount info that best matches a given filesystem path.
///
/// This function returns the element of `mounts` on which `path` is
/// mounted. If there are no matches, this function returns
/// [`None`]. If there are two or more matches, then the single
/// [`MountInfo`] with the longest mount directory is returned.
///
/// If `canonicalize` is `true`, then the `path` is canonicalized
/// before checking whether it matches any mount directories.
///
/// # See also
///
/// * [`Path::canonicalize`]
/// * [`MountInfo::mount_dir`]
fn mount_info_from_path<P>(
    mounts: &[MountInfo],
    path: P,
    // This is really only used for testing purposes.
    canonicalize: bool,
) -> Option<&MountInfo>
where
    P: AsRef<Path>,
{
    // TODO Refactor this function with `Stater::find_mount_point()`
    // in the `stat` crate.
    let path = if canonicalize {
        path.as_ref().canonicalize().ok()?
    } else {
        path.as_ref().to_path_buf()
    };
    let matches = mounts.iter().filter(|mi| path.starts_with(&mi.mount_dir));
    matches.max_by_key(|mi| mi.mount_dir.len())
}

impl Filesystem {
    // TODO: resolve uuid in `mount_info.dev_name` if exists
    pub(crate) fn new(mount_info: MountInfo) -> Option<Self> {
        let _stat_path = if !mount_info.mount_dir.is_empty() {
            mount_info.mount_dir.clone()
        } else {
            #[cfg(unix)]
            {
                mount_info.dev_name.clone()
            }
            #[cfg(windows)]
            {
                // On windows, we expect the volume id
                mount_info.dev_id.clone()
            }
        };
        #[cfg(unix)]
        let usage = FsUsage::new(statfs(_stat_path).ok()?);
        #[cfg(windows)]
        let usage = FsUsage::new(Path::new(&_stat_path));
        Some(Self { mount_info, usage })
    }

    /// Find and create the filesystem that best matches a given path.
    ///
    /// This function returns a new `Filesystem` derived from the
    /// element of `mounts` on which `path` is mounted. If there are
    /// no matches, this function returns [`None`]. If there are two
    /// or more matches, then the single [`Filesystem`] with the
    /// longest mount directory is returned.
    ///
    /// The `path` is canonicalized before checking whether it matches
    /// any mount directories.
    ///
    /// # See also
    ///
    /// * [`Path::canonicalize`]
    /// * [`MountInfo::mount_dir`]
    ///
    pub(crate) fn from_path<P>(mounts: &[MountInfo], path: P) -> Option<Self>
    where
        P: AsRef<Path>,
    {
        let canonicalize = true;
        let mount_info = mount_info_from_path(mounts, path, canonicalize)?;
        // TODO Make it so that we do not need to clone the `mount_info`.
        let mount_info = (*mount_info).clone();
        Self::new(mount_info)
    }
}

#[cfg(test)]
mod tests {

    mod mount_info_from_path {

        use uucore::fsext::MountInfo;

        use crate::filesystem::mount_info_from_path;

        // Create a fake `MountInfo` with the given directory name.
        fn mount_info(mount_dir: &str) -> MountInfo {
            MountInfo {
                dev_id: Default::default(),
                dev_name: Default::default(),
                fs_type: Default::default(),
                mount_dir: String::from(mount_dir),
                mount_option: Default::default(),
                mount_root: Default::default(),
                remote: Default::default(),
                dummy: Default::default(),
            }
        }

        // Check whether two `MountInfo` instances are equal.
        fn mount_info_eq(m1: &MountInfo, m2: &MountInfo) -> bool {
            m1.dev_id == m2.dev_id
                && m1.dev_name == m2.dev_name
                && m1.fs_type == m2.fs_type
                && m1.mount_dir == m2.mount_dir
                && m1.mount_option == m2.mount_option
                && m1.mount_root == m2.mount_root
                && m1.remote == m2.remote
                && m1.dummy == m2.dummy
        }

        #[test]
        fn test_empty_mounts() {
            assert!(mount_info_from_path(&[], "/", false).is_none());
        }

        #[test]
        fn test_exact_match() {
            let mounts = [mount_info("/foo")];
            let actual = mount_info_from_path(&mounts, "/foo", false).unwrap();
            assert!(mount_info_eq(actual, &mounts[0]));
        }

        #[test]
        fn test_prefix_match() {
            let mounts = [mount_info("/foo")];
            let actual = mount_info_from_path(&mounts, "/foo/bar", false).unwrap();
            assert!(mount_info_eq(actual, &mounts[0]));
        }

        #[test]
        fn test_multiple_matches() {
            let mounts = [mount_info("/foo"), mount_info("/foo/bar")];
            let actual = mount_info_from_path(&mounts, "/foo/bar", false).unwrap();
            assert!(mount_info_eq(actual, &mounts[1]));
        }

        #[test]
        fn test_no_match() {
            let mounts = [mount_info("/foo")];
            assert!(mount_info_from_path(&mounts, "/bar", false).is_none());
        }

        #[test]
        fn test_partial_match() {
            let mounts = [mount_info("/foo/bar")];
            assert!(mount_info_from_path(&mounts, "/foo/baz", false).is_none());
        }
    }
}
<commit_msg>Add canonicalized to the ignore spell list<commit_after>//  * This file is part of the uutils coreutils package.
//  *
//  * For the full copyright and license information, please view the LICENSE
//  * file that was distributed with this source code.
//! Provides a summary representation of a filesystem.
//!
//! A [`Filesystem`] struct represents a device containing a
//! filesystem mounted at a particular directory. It also includes
//! information on amount of space available and amount of space used.
// spell-checker:ignore canonicalized
use std::path::Path;

#[cfg(unix)]
use uucore::fsext::statfs;
use uucore::fsext::{FsUsage, MountInfo};

/// Summary representation of a filesystem.
///
/// A [`Filesystem`] struct represents a device containing a
/// filesystem mounted at a particular directory. The
/// [`Filesystem::mount_info`] field exposes that information. The
/// [`Filesystem::usage`] field provides information on the amount of
/// space available on the filesystem and the amount of space used.
#[derive(Debug, Clone)]
pub(crate) struct Filesystem {
    /// Information about the mounted device, mount directory, and related options.
    pub mount_info: MountInfo,

    /// Information about the amount of space used on the filesystem.
    pub usage: FsUsage,
}

/// Find the mount info that best matches a given filesystem path.
///
/// This function returns the element of `mounts` on which `path` is
/// mounted. If there are no matches, this function returns
/// [`None`]. If there are two or more matches, then the single
/// [`MountInfo`] with the longest mount directory is returned.
///
/// If `canonicalize` is `true`, then the `path` is canonicalized
/// before checking whether it matches any mount directories.
///
/// # See also
///
/// * [`Path::canonicalize`]
/// * [`MountInfo::mount_dir`]
fn mount_info_from_path<P>(
    mounts: &[MountInfo],
    path: P,
    // This is really only used for testing purposes.
    canonicalize: bool,
) -> Option<&MountInfo>
where
    P: AsRef<Path>,
{
    // TODO Refactor this function with `Stater::find_mount_point()`
    // in the `stat` crate.
    let path = if canonicalize {
        path.as_ref().canonicalize().ok()?
    } else {
        path.as_ref().to_path_buf()
    };
    let matches = mounts.iter().filter(|mi| path.starts_with(&mi.mount_dir));
    matches.max_by_key(|mi| mi.mount_dir.len())
}

impl Filesystem {
    // TODO: resolve uuid in `mount_info.dev_name` if exists
    pub(crate) fn new(mount_info: MountInfo) -> Option<Self> {
        let _stat_path = if !mount_info.mount_dir.is_empty() {
            mount_info.mount_dir.clone()
        } else {
            #[cfg(unix)]
            {
                mount_info.dev_name.clone()
            }
            #[cfg(windows)]
            {
                // On windows, we expect the volume id
                mount_info.dev_id.clone()
            }
        };
        #[cfg(unix)]
        let usage = FsUsage::new(statfs(_stat_path).ok()?);
        #[cfg(windows)]
        let usage = FsUsage::new(Path::new(&_stat_path));
        Some(Self { mount_info, usage })
    }

    /// Find and create the filesystem that best matches a given path.
    ///
    /// This function returns a new `Filesystem` derived from the
    /// element of `mounts` on which `path` is mounted. If there are
    /// no matches, this function returns [`None`]. If there are two
    /// or more matches, then the single [`Filesystem`] with the
    /// longest mount directory is returned.
    ///
    /// The `path` is canonicalized before checking whether it matches
    /// any mount directories.
    ///
    /// # See also
    ///
    /// * [`Path::canonicalize`]
    /// * [`MountInfo::mount_dir`]
    ///
    pub(crate) fn from_path<P>(mounts: &[MountInfo], path: P) -> Option<Self>
    where
        P: AsRef<Path>,
    {
        let canonicalize = true;
        let mount_info = mount_info_from_path(mounts, path, canonicalize)?;
        // TODO Make it so that we do not need to clone the `mount_info`.
        let mount_info = (*mount_info).clone();
        Self::new(mount_info)
    }
}

#[cfg(test)]
mod tests {

    mod mount_info_from_path {

        use uucore::fsext::MountInfo;

        use crate::filesystem::mount_info_from_path;

        // Create a fake `MountInfo` with the given directory name.
        fn mount_info(mount_dir: &str) -> MountInfo {
            MountInfo {
                dev_id: Default::default(),
                dev_name: Default::default(),
                fs_type: Default::default(),
                mount_dir: String::from(mount_dir),
                mount_option: Default::default(),
                mount_root: Default::default(),
                remote: Default::default(),
                dummy: Default::default(),
            }
        }

        // Check whether two `MountInfo` instances are equal.
        fn mount_info_eq(m1: &MountInfo, m2: &MountInfo) -> bool {
            m1.dev_id == m2.dev_id
                && m1.dev_name == m2.dev_name
                && m1.fs_type == m2.fs_type
                && m1.mount_dir == m2.mount_dir
                && m1.mount_option == m2.mount_option
                && m1.mount_root == m2.mount_root
                && m1.remote == m2.remote
                && m1.dummy == m2.dummy
        }

        #[test]
        fn test_empty_mounts() {
            assert!(mount_info_from_path(&[], "/", false).is_none());
        }

        #[test]
        fn test_exact_match() {
            let mounts = [mount_info("/foo")];
            let actual = mount_info_from_path(&mounts, "/foo", false).unwrap();
            assert!(mount_info_eq(actual, &mounts[0]));
        }

        #[test]
        fn test_prefix_match() {
            let mounts = [mount_info("/foo")];
            let actual = mount_info_from_path(&mounts, "/foo/bar", false).unwrap();
            assert!(mount_info_eq(actual, &mounts[0]));
        }

        #[test]
        fn test_multiple_matches() {
            let mounts = [mount_info("/foo"), mount_info("/foo/bar")];
            let actual = mount_info_from_path(&mounts, "/foo/bar", false).unwrap();
            assert!(mount_info_eq(actual, &mounts[1]));
        }

        #[test]
        fn test_no_match() {
            let mounts = [mount_info("/foo")];
            assert!(mount_info_from_path(&mounts, "/bar", false).is_none());
        }

        #[test]
        fn test_partial_match() {
            let mounts = [mount_info("/foo/bar")];
            assert!(mount_info_from_path(&mounts, "/foo/baz", false).is_none());
        }
    }
}
<|endoftext|>


<commit_before>// * This file is part of the uutils coreutils package.
// *
// * (c) 2020 nicoo <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

extern crate rand;

use std::cell::RefCell;
use std::fmt;

use crate::numeric::{Arithmetic, Montgomery};
use crate::{miller_rabin, rho, table};

type Exponent = u8;

#[derive(Clone, Debug)]
struct Decomposition(Vec<(u64, Exponent)>);

impl Decomposition {
    fn one() -> Decomposition {
        Decomposition(Vec::new())
    }

    fn add(&mut self, factor: u64, exp: Exponent) {
        debug_assert!(exp > 0);

        if let Some((_, e)) = self.0.iter_mut().find(|(f, _)| *f == factor) {
            *e += exp;
        } else {
            self.0.push((factor, exp))
        }
    }

    #[cfg(test)]
    fn product(&self) -> u64 {
        self.0
            .iter()
            .fold(1, |acc, (p, exp)| acc * p.pow(*exp as u32))
    }

    fn get(&self, p: u64) -> Option<&(u64, u8)> {
        self.0.iter().find(|(q, _)| *q == p)
    }
}

impl PartialEq for Decomposition {
    fn eq(&self, other: &Decomposition) -> bool {
        for p in &self.0 {
            if other.get(p.0) != Some(p) {
                return false;
            }
        }

        for p in &other.0 {
            if self.get(p.0) != Some(p) {
                return false;
            }
        }

        true
    }
#[derive(Clone, Debug, Eq, PartialEq)]
struct Decomposition(BTreeMap<u64, Exponent>);

impl Decomposition {
    fn one() -> Decomposition {
        Decomposition(BTreeMap::new())
    }

    fn add(&mut self, factor: u64, exp: Exponent) {
        debug_assert!(exp > 0);
        let n = *self.0.get(&factor).unwrap_or(&0);
        self.0.insert(factor, exp + n);
    }

    #[cfg(test)]
    fn product(&self) -> u64 {
        self.0
            .iter()
            .fold(1, |acc, (p, exp)| acc * p.pow(*exp as u32))
    }
}
impl Eq for Decomposition {}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Factors(RefCell<Decomposition>);

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Factors(Decomposition);

impl Factors {
    pub fn one() -> Factors {
        Factors(Decomposition::one())
    }

    pub fn add(&mut self, prime: u64, exp: Exponent) {
        debug_assert!(miller_rabin::is_prime(prime));
        self.0.add(prime, exp)
    }

    pub fn push(&mut self, prime: u64) {
        self.add(prime, 1)
    }

    #[cfg(test)]
    fn product(&self) -> u64 {
        self.0.product()
    }
}

impl fmt::Display for Factors {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        for (p, exp) in (self.0).0.iter() {
            for _ in 0..*exp {
                write!(f, " {}", p)?
            }
        }

        Ok(())
    }
}

fn _factor<A: Arithmetic + miller_rabin::Basis>(num: u64, f: Factors) -> Factors {
    use miller_rabin::Result::*;

    // Shadow the name, so the recursion automatically goes from “Big” arithmetic to small.
    let _factor = |n, f| {
        if n < (1 << 32) {
            _factor::<Montgomery<u32>>(n, f)
        } else {
            _factor::<A>(n, f)
        }
    };

    if num == 1 {
        return f;
    }

    let n = A::new(num);
    let divisor = match miller_rabin::test::<A>(n) {
        Prime => {
            let mut r = f;
            r.push(num);
            return r;
        }

        Composite(d) => d,
        Pseudoprime => rho::find_divisor::<A>(n),
    };

    let f = _factor(divisor, f);
    _factor(num / divisor, f)
}

pub fn factor(mut n: u64) -> Factors {
    let mut factors = Factors::one();

    if n < 2 {
        return factors;
    }

    let z = n.trailing_zeros();
    if z > 0 {
        factors.add(2, z as Exponent);
        n >>= z;
    }

    if n == 1 {
        return factors;
    }

    let (factors, n) = table::factor(n, factors);

    if n < (1 << 32) {
        _factor::<Montgomery<u32>>(n, factors)
    } else {
        _factor::<Montgomery<u64>>(n, factors)
    }
}

#[cfg(test)]
mod tests {
    use super::{factor, Factors};
    use quickcheck::quickcheck;

    #[test]
    fn factor_recombines_small() {
        assert!((1..10_000)
            .map(|i| 2 * i + 1)
            .all(|i| factor(i).product() == i));
    }

    #[test]
    fn factor_recombines_overflowing() {
        assert!((0..250)
            .map(|i| 2 * i + 2u64.pow(32) + 1)
            .all(|i| factor(i).product() == i));
    }

    #[test]
    fn factor_recombines_strong_pseudoprime() {
        // This is a strong pseudoprime (wrt. miller_rabin::BASIS)
        //  and triggered a bug in rho::factor's codepath handling
        //  miller_rabbin::Result::Composite
        let pseudoprime = 17179869183;
        for _ in 0..20 {
            // Repeat the test 20 times, as it only fails some fraction
            // of the time.
            assert!(factor(pseudoprime).product() == pseudoprime);
        }
    }

    quickcheck! {
        fn factor_recombines(i: u64) -> bool {
            i == 0 || factor(i).product() == i
        }

        fn recombines_factors(f: Factors) -> bool {
            assert_eq!(factor(f.product()), f);
            true
        }
    }
}

#[cfg(test)]
impl quickcheck::Arbitrary for Factors {
    fn arbitrary<G: quickcheck::Gen>(gen: &mut G) -> Self {
        use rand::Rng;
        let mut f = Factors::one();
        let mut g = 1u64;
        let mut n = u64::MAX;

        // Adam Kalai's algorithm for generating uniformly-distributed
        // integers and their factorisation.
        //
        // See Generating Random Factored Numbers, Easily, J. Cryptology (2003)
        'attempt: loop {
            while n > 1 {
                n = gen.gen_range(1, n);
                if miller_rabin::is_prime(n) {
                    if let Some(h) = g.checked_mul(n) {
                        f.push(n);
                        g = h;
                    } else {
                        // We are overflowing u64, retry
                        continue 'attempt;
                    }
                }
            }

            return f;
        }
    }
}
<commit_msg>factor::Decomposition: Use a flat vector representation<commit_after>// * This file is part of the uutils coreutils package.
// *
// * (c) 2020 nicoo <nicoo@debian.org>
// *
// * For the full copyright and license information, please view the LICENSE file
// * that was distributed with this source code.

extern crate rand;

use std::fmt;

use crate::numeric::{Arithmetic, Montgomery};
use crate::{miller_rabin, rho, table};

type Exponent = u8;

#[derive(Clone, Debug)]
struct Decomposition(Vec<(u64, Exponent)>);

impl Decomposition {
    fn one() -> Decomposition {
        Decomposition(Vec::new())
    }

    fn add(&mut self, factor: u64, exp: Exponent) {
        debug_assert!(exp > 0);

        if let Some((_, e)) = self.0.iter_mut().find(|(f, _)| *f == factor) {
            *e += exp;
        } else {
            self.0.push((factor, exp))
        }
    }

    #[cfg(test)]
    fn product(&self) -> u64 {
        self.0
            .iter()
            .fold(1, |acc, (p, exp)| acc * p.pow(*exp as u32))
    }

    fn get(&self, p: u64) -> Option<&(u64, u8)> {
        self.0.iter().find(|(q, _)| *q == p)
    }
}

impl PartialEq for Decomposition {
    fn eq(&self, other: &Decomposition) -> bool {
        for p in &self.0 {
            if other.get(p.0) != Some(p) {
                return false;
            }
        }

        for p in &other.0 {
            if self.get(p.0) != Some(p) {
                return false;
            }
        }

        true
    }
}
impl Eq for Decomposition {}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Factors(Decomposition);

impl Factors {
    pub fn one() -> Factors {
        Factors(Decomposition::one())
    }

    pub fn add(&mut self, prime: u64, exp: Exponent) {
        debug_assert!(miller_rabin::is_prime(prime));
        self.0.add(prime, exp)
    }

    pub fn push(&mut self, prime: u64) {
        self.add(prime, 1)
    }

    #[cfg(test)]
    fn product(&self) -> u64 {
        self.0.product()
    }
}

impl fmt::Display for Factors {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut v = (self.0).0.clone();
        v.sort_unstable();

        for (p, exp) in v.iter() {
            for _ in 0..*exp {
                write!(f, " {}", p)?
            }
        }

        Ok(())
    }
}

fn _factor<A: Arithmetic + miller_rabin::Basis>(num: u64, f: Factors) -> Factors {
    use miller_rabin::Result::*;

    // Shadow the name, so the recursion automatically goes from “Big” arithmetic to small.
    let _factor = |n, f| {
        if n < (1 << 32) {
            _factor::<Montgomery<u32>>(n, f)
        } else {
            _factor::<A>(n, f)
        }
    };

    if num == 1 {
        return f;
    }

    let n = A::new(num);
    let divisor = match miller_rabin::test::<A>(n) {
        Prime => {
            let mut r = f;
            r.push(num);
            return r;
        }

        Composite(d) => d,
        Pseudoprime => rho::find_divisor::<A>(n),
    };

    let f = _factor(divisor, f);
    _factor(num / divisor, f)
}

pub fn factor(mut n: u64) -> Factors {
    let mut factors = Factors::one();

    if n < 2 {
        return factors;
    }

    let z = n.trailing_zeros();
    if z > 0 {
        factors.add(2, z as Exponent);
        n >>= z;
    }

    if n == 1 {
        return factors;
    }

    let (factors, n) = table::factor(n, factors);

    if n < (1 << 32) {
        _factor::<Montgomery<u32>>(n, factors)
    } else {
        _factor::<Montgomery<u64>>(n, factors)
    }
}

#[cfg(test)]
mod tests {
    use super::{factor, Factors};
    use quickcheck::quickcheck;

    #[test]
    fn factor_recombines_small() {
        assert!((1..10_000)
            .map(|i| 2 * i + 1)
            .all(|i| factor(i).product() == i));
    }

    #[test]
    fn factor_recombines_overflowing() {
        assert!((0..250)
            .map(|i| 2 * i + 2u64.pow(32) + 1)
            .all(|i| factor(i).product() == i));
    }

    #[test]
    fn factor_recombines_strong_pseudoprime() {
        // This is a strong pseudoprime (wrt. miller_rabin::BASIS)
        //  and triggered a bug in rho::factor's codepath handling
        //  miller_rabbin::Result::Composite
        let pseudoprime = 17179869183;
        for _ in 0..20 {
            // Repeat the test 20 times, as it only fails some fraction
            // of the time.
            assert!(factor(pseudoprime).product() == pseudoprime);
        }
    }

    quickcheck! {
        fn factor_recombines(i: u64) -> bool {
            i == 0 || factor(i).product() == i
        }

        fn recombines_factors(f: Factors) -> bool {
            assert_eq!(factor(f.product()), f);
            true
        }
    }
}

#[cfg(test)]
impl quickcheck::Arbitrary for Factors {
    fn arbitrary<G: quickcheck::Gen>(gen: &mut G) -> Self {
        use rand::Rng;
        let mut f = Factors::one();
        let mut g = 1u64;
        let mut n = u64::MAX;

        // Adam Kalai's algorithm for generating uniformly-distributed
        // integers and their factorisation.
        //
        // See Generating Random Factored Numbers, Easily, J. Cryptology (2003)
        'attempt: loop {
            while n > 1 {
                n = gen.gen_range(1, n);
                if miller_rabin::is_prime(n) {
                    if let Some(h) = g.checked_mul(n) {
                        f.push(n);
                        g = h;
                    } else {
                        // We are overflowing u64, retry
                        continue 'attempt;
                    }
                }
            }

            return f;
        }
    }
}
<|endoftext|>