diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..30c4b9f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,77 @@ +# Changelog + +## [Unreleased] + + +[unreleased]: https://github.com/FluenTech/embedded-time/compare/v0.6.0...HEAD + +## [0.6.0] - 2020-07-03 + +### Added + +- A `Timer` type supporting one-shot and periodic software timers utilizing a `Clock` implementation +- Fallibility and error handling for `Clock` methods +- `Instant::duration_until()` with order checking +- Order checking to `Instant::duration_since()` +- Bounds checking on `Instant` impls of Add/Sub +- Changelog back to v0.5.0 release +- [`crossbeam-utils`](https://crates.io/crates/crossbeam-utils) dev-dependency for scoped threads in tests + +### Changed + +- Add `&self` to `Clock` functions (make stateful, or at least allow stateful implementations) +- All time-type inner types from signed to unsigned +- `Instant::duration_since()` return type to `Result` +- Refactor `examples/nrf52_dk` + +[0.6.0]: https://github.com/FluenTech/embedded-time/compare/v0.5.2...v0.6.0 + +## [0.5.2] - 2020-06-21 + +### Added + +- Ability to convert to/from [`core::time::Duration`](https://doc.rust-lang.org/stable/core/time/struct.Duration.html) +- Missing documentation + +### Changed + +- Moved majority of `Duration`-related documentation to `Duration` trait +- Minor refactoring + +[0.5.2]: https://github.com/FluenTech/embedded-time/compare/v0.5.1...v0.5.2 + + +## [0.5.1] - 2020-06-21 + +### Changed + +- Repository location + +### Removed + +- `Period` from `prelude` mod as it is no longer a trait + +[0.5.1]: https://github.com/FluenTech/embedded-time/compare/v0.5.0...v0.5.1 + + +## [0.5.0] - 2020-06-17 + +### Added + +- `cargo doc` CI test +- Frequency-based type (`Hertz`) with conversion to/from `Period` +- CI tests for `stable` + +### Changed + +- Rename `duration::time_units` to `duration::units` (`units` is also re-exported) +- Rename `TimeRep` to `TimeInt` +- Update `num` to v0.3.x +- Make `Period` a struct that wraps a `Ratio`, rather than a trait + +### Removed + +- `associated_type_bounds` feature flag to allow `stable` build +- Re-export of the `duration` module (wasn't useful) + +[0.5.0]: https://github.com/FluenTech/embedded-time/compare/v0.4.0...v0.5.0 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..3cad194 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,76 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, sex characteristics, gender identity and expression, +level of experience, education, socio-economic status, nationality, personal +appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or + advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at ptaylor@fluentech.info. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see +https://www.contributor-covenant.org/faq diff --git a/Cargo.lock b/Cargo.lock index 63e4771..64167e5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,25 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +[[package]] +name = "aligned" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb1ce8b3382016136ab1d31a1b5ce807144f8b7eb2d5f16b2108f0f07edceb94" +dependencies = [ + "as-slice", +] + +[[package]] +name = "as-slice" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37dfb65bc03b2bc85ee827004f14a6817e04160e3b1a28931986a666a9290e70" +dependencies = [ + "generic-array 0.12.3", + "generic-array 0.13.2", + "stable_deref_trait", +] + [[package]] name = "autocfg" version = "1.0.0" @@ -15,12 +35,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "bitfield" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" - [[package]] name = "cast" version = "0.2.3" @@ -30,14 +44,20 @@ dependencies = [ "rustc_version", ] +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + [[package]] name = "cortex-m" version = "0.6.2" -source = "git+https://github.com/rust-embedded/cortex-m?branch=mutex_add#2dca8c948f47a1c23e9f33bff26721f5a33f2fd8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2954942fbbdd49996704e6f048ce57567c3e1a4e2dc59b41ae9fde06a01fc763" dependencies = [ + "aligned", "bare-metal", - "bitfield", - "mutex-trait", "volatile-register", ] @@ -62,11 +82,22 @@ dependencies = [ "syn", ] +[[package]] +name = "crossbeam-utils" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + [[package]] name = "embedded-hal" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4908a155094da7723c2d60d617b820061e3b4efcc3d9e293d206a5a76c170b" +checksum = "fa998ce59ec9765d15216393af37a58961ddcefb14c753b4816ba2191d865fcb" dependencies = [ "nb", "void", @@ -74,11 +105,11 @@ dependencies = [ [[package]] name = "embedded-time" -version = "0.5.2" +version = "0.6.0" dependencies = [ "cortex-m", "cortex-m-rt", - "mutex-trait", + "crossbeam-utils", "nrf52832-hal", "num", "panic_rtt", @@ -94,6 +125,24 @@ dependencies = [ "typenum", ] +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ed1e761351b56f54eb9dcd0cfaca9fd0daecf93918e1cfc01c8a3d26ee7adcd" +dependencies = [ + "typenum", +] + [[package]] name = "jlink_rtt" version = "0.2.0" @@ -101,10 +150,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f6af40154f64bd7095b5245d6933fef99b0a28c432b38a676cfafaa0aedfc2" [[package]] -name = "mutex-trait" -version = "0.2.0" +name = "lazy_static" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4bb1638d419e12f8b1c43d9e639abd0d1424285bdea2f76aa231e233c63cd3a" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "nb" @@ -282,11 +331,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "stable_deref_trait" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" + [[package]] name = "syn" -version = "1.0.31" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5304cfdf27365b7585c25d4af91b35016ed21ef88f17ced89c7093b43dba8b6" +checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd" dependencies = [ "proc-macro2", "quote", @@ -301,9 +356,9 @@ checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" [[package]] name = "unicode-xid" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" [[package]] name = "vcell" diff --git a/Cargo.toml b/Cargo.toml index 91319f3..a8b5e16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "embedded-time" -version = "0.5.2" +version = "0.6.0" authors = ["Peter Taylor "] edition = "2018" description = "A library for abstracting clocks and handling time(ing) in embedded systems" @@ -20,7 +20,6 @@ cortex-m = "0.6.2" cortex-m-rt = "0.6.12" panic_rtt = "0.3.0" nrf52832-hal = { version = "0.10.0", default-features = false, features = ["rt", "xxAA-package"] } -mutex-trait = "0.2.0" -[patch.crates-io] -cortex-m = { git = "https://github.com/rust-embedded/cortex-m", branch = "mutex_add" } +[target.'cfg(not(target_arch = "arm"))'.dev-dependencies] +crossbeam-utils = "0.7.2" diff --git a/README.md b/README.md index 807e2cc..8418b8a 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,42 @@ [docs.rs]: https://docs.rs/embedded-time/badge.svg `embedded-time` provides a comprehensive library for implementing abstractions over -hardware and work with _clocks_, _instants_, _durations_, _periods_, and _frequencies_ in a more intuitive way. - +hardware and work with _clocks_, _timers_, _instants_, _durations_, _periods_, and _frequencies_ in a more intuitive way. + +## Hardware Abstraction + - `Clock` trait allowing abstraction of hardware timers for timekeeping. -- Work with time using _milliseconds_, _seconds_, _hertz_, etc. rather than _cycles_ or _ticks_. -- Includes example for the nRF52_DK board -- Conversion to/from core::time::Duration + +## Timers + +- Software timers spawned from a `Clock` impl object. +- One-shot or periodic/continuous +- Blocking delay +- Poll for expiration +- Read elapsed/remaining duration + +## Duration Types + +- Nanoseconds +- Microseconds +- Milliseconds +- Seconds +- Minutes +- Hours + +## Frequency Type + +- Hertz + +## `core` Compatibility + +- Conversion to/from `core::time::Duration` + +## Reliability and Usability +- Extensive tests +- Thorough documentation with examples +- Example for the nRF52_DK board + ## License This project is licensed under either of diff --git a/crates-io.md b/crates-io.md index 273f3bc..b2ce535 100644 --- a/crates-io.md +++ b/crates-io.md @@ -1,12 +1,41 @@ # embedded-time `embedded-time` provides a comprehensive library for implementing abstractions over -hardware and work with _clocks_, _instants_, _durations_, _periods_, and _frequencies_ in a more intuitive way. - +hardware and work with _clocks_, _timers_, _instants_, _durations_, _periods_, and _frequencies_ in a more intuitive way. + +## Hardware Abstraction + - `Clock` trait allowing abstraction of hardware timers for timekeeping. -- Work with time using _milliseconds_, _seconds_, _hertz_, etc. rather than _cycles_ or _ticks_. -- Includes example for the nRF52_DK board -- Conversion to/from core::time::Duration + +## Timers + +- Software timers spawned from a `Clock` impl object. +- One-shot or periodic/continuous +- Blocking delay +- Poll for expiration +- Read elapsed/remaining duration + +## Duration Types + +- Nanoseconds +- Microseconds +- Milliseconds +- Seconds +- Minutes +- Hours + +## Frequency Type + +- Hertz + +## `core` Compatibility + +- Conversion to/from `core::time::Duration` + +## Reliability and Usability +- Extensive tests +- Thorough documentation with examples +- Example for the nRF52_DK board ## Motivation The handling of time on embedded systems is generally much different than that of OSs. For instance, on an OS, the time is measured against an arbitrary epoch. Embedded systems generally don't know (nor do they care) what the *real* time is, but rather how much time has passed since the system has started. diff --git a/examples/nrf52_dk/main.rs b/examples/nrf52_dk/main.rs index d147614..036cd66 100644 --- a/examples/nrf52_dk/main.rs +++ b/examples/nrf52_dk/main.rs @@ -3,11 +3,9 @@ extern crate panic_rtt; -use core::borrow::Borrow; -use cortex_m::mutex::CriticalSectionMutex as Mutex; +use core::convert::Infallible; use cortex_m_rt::entry; -use embedded_time::{self as time, Clock, Instant, Period, TimeInt}; -use mutex_trait::Mutex as _; +use embedded_time::{self as time, traits::*}; pub mod nrf52 { pub use nrf52832_hal::{ @@ -15,47 +13,43 @@ pub mod nrf52 { prelude::*, target::{self as pac, Peripherals}, }; +} - pub struct Timer64 { - low: pac::TIMER0, - high: pac::TIMER1, - capture_task: pac::EGU0, - } - - impl Timer64 { - pub fn take(low: pac::TIMER0, high: pac::TIMER1, capture_task: pac::EGU0) -> Self { - Self { - low, - high, - capture_task, - } - } +pub struct SysClock { + low: nrf52::pac::TIMER0, + high: nrf52::pac::TIMER1, + capture_task: nrf52::pac::EGU0, +} - pub(crate) fn read(&mut self) -> u64 { - self.capture_task.tasks_trigger[0].write(|write| unsafe { write.bits(1) }); - self.low.cc[0].read().bits() as u64 | ((self.high.cc[0].read().bits() as u64) << 32) +impl SysClock { + pub fn take( + low: nrf52::pac::TIMER0, + high: nrf52::pac::TIMER1, + capture_task: nrf52::pac::EGU0, + ) -> Self { + Self { + low, + high, + capture_task, } } } -pub struct SysClock; - impl time::Clock for SysClock { - type Rep = i64; - const PERIOD: Period = Period::new(1, 16_000_000); + type Rep = u64; + const PERIOD: time::Period = ::new(1, 16_000_000); + type ImplError = Infallible; - fn now() -> Instant { - let ticks = (&SYSTEM_TICKS).lock(|timer64| match timer64 { - Some(timer64) => timer64.read(), - None => 0, - }); + fn now(&self) -> Result, time::clock::Error> { + self.capture_task.tasks_trigger[0].write(|write| unsafe { write.bits(1) }); - Instant::new(ticks as Self::Rep) + let ticks = + self.low.cc[0].read().bits() as u64 | ((self.high.cc[0].read().bits() as u64) << 32); + + Ok(time::Instant::new(ticks as Self::Rep)) } } -static SYSTEM_TICKS: Mutex> = Mutex::new(None); - #[entry] fn main() -> ! { let device = nrf52::pac::Peripherals::take().unwrap(); @@ -76,35 +70,21 @@ fn main() -> ! { unsafe { device.PPI.ch[0].eep.write(|w| { - w.bits( - device.TIMER0.events_compare[1].borrow() as *const nrf52::pac::generic::Reg<_, _> - as u32, - ) + w.bits(&device.TIMER0.events_compare[1] as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.ch[0].tep.write(|w| { - w.bits( - device.TIMER1.tasks_count.borrow() as *const nrf52::pac::generic::Reg<_, _> as u32, - ) + w.bits(&device.TIMER1.tasks_count as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.chen.modify(|_, w| w.ch0().enabled()); device.PPI.ch[1].eep.write(|w| { - w.bits( - device.EGU0.events_triggered[0].borrow() as *const nrf52::pac::generic::Reg<_, _> - as u32, - ) + w.bits(&device.EGU0.events_triggered[0] as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.ch[1].tep.write(|w| { - w.bits( - device.TIMER0.tasks_capture[0].borrow() as *const nrf52::pac::generic::Reg<_, _> - as u32, - ) + w.bits(&device.TIMER0.tasks_capture[0] as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.fork[1].tep.write(|w| { - w.bits( - device.TIMER1.tasks_capture[0].borrow() as *const nrf52::pac::generic::Reg<_, _> - as u32, - ) + w.bits(&device.TIMER1.tasks_capture[0] as *const nrf52::pac::generic::Reg<_, _> as u32) }); device.PPI.chen.modify(|_, w| w.ch1().enabled()); } @@ -118,8 +98,10 @@ fn main() -> ! { .tasks_start .write(|write| unsafe { write.bits(1) }); - let timer64 = nrf52::Timer64::take(device.TIMER0, device.TIMER1, device.EGU0); - (&SYSTEM_TICKS).lock(|ticks| *ticks = Some(timer64)); + // This moves these peripherals to prevent conflicting usage, however not the entire EGU0 is + // used. A ref to EGU0 could be sent instead, although that provides no protection for the + // fields that are being used by Clock64. + let mut clock = SysClock::take(device.TIMER0, device.TIMER1, device.EGU0); let port0 = nrf52::gpio::p0::Parts::new(device.P0); @@ -148,6 +130,7 @@ fn main() -> ! { &mut led2.degrade(), &mut led3.degrade(), &mut led4.degrade(), + &mut clock, ) .unwrap(); @@ -159,6 +142,7 @@ fn run( led2: &mut Led, led3: &mut Led, led4: &mut Led, + clock: &mut SysClock, ) -> Result<(), ::Error> where Led: nrf52::OutputPin, @@ -168,12 +152,12 @@ where led2.set_high()?; led3.set_high()?; led4.set_low()?; - SysClock::delay(250.milliseconds()); + clock.new_timer(250_u32.milliseconds()).start().wait(); led1.set_high()?; led2.set_low()?; led3.set_low()?; led4.set_high()?; - SysClock::delay(250.milliseconds()); + clock.new_timer(250_u32.milliseconds()).start().wait(); } } diff --git a/src/clock.rs b/src/clock.rs index a14386f..0d5a1c9 100644 --- a/src/clock.rs +++ b/src/clock.rs @@ -1,7 +1,28 @@ -use crate::{time_int::TimeInt, Duration, Instant, Period}; -use core::convert::TryFrom; +//! Clock abstractions -/// An abstraction for time-keeping items such as hardware timers +use crate::{ + duration::Duration, instant::Instant, period::Period, time_int::TimeInt, timer::param, + timer::Timer, +}; + +/// Potential `Clock` errors +#[non_exhaustive] +#[derive(Debug, Eq, PartialEq)] +pub enum Error { + /// implementation-specific error + Other(E), +} + +/// The `Clock` trait provides an abstraction of hardware-specific timer peripherals, external timer +/// devices, RTCs, etc. +/// +/// The `Clock` is characterized by an inner unsigned integer storage type (either [`u32`] or +/// [`u64`]), a [`u32`]/[`u32`] fraction ([`Period`]) defining the duration (in seconds) of one +/// count of this `Clock`, and a custom error type (must implement [`crate::Error`]) representing +/// errors that may be generated by the implementation. +/// +/// In addition to the [`Clock::now()`] method which returns an [`Instant`], an unlimited number of +/// software [`Timer`]s can be spawned from a single `Clock` instance. pub trait Clock: Sized { /// The type to hold the tick count type Rep: TimeInt; @@ -9,18 +30,23 @@ pub trait Clock: Sized { /// The duration of one clock tick in seconds, AKA the clock precision. const PERIOD: Period; + /// Implementation-specific error type + /// + /// This type can be returned using the [`Error::Other(E)`](enum.Error.html#variant.Other) + type ImplError: crate::Error; + /// Get the current Instant - fn now() -> Instant; + /// + /// # Errors + /// Implementation-specific error returned as + /// [`Error::Other(Self::ImplError)`](enum.Error.html#variant.Other) + fn now(&self) -> Result, Error>; - /// Blocking delay - fn delay(dur: Dur) - where - Dur: Duration, - Dur::Rep: TimeInt, - Self::Rep: TryFrom, - { - let start = Self::now(); - let end = start + dur; - while Self::now() < end {} + /// Spawn a new, `OneShot` [`Timer`] from this clock + fn new_timer( + &self, + duration: Dur, + ) -> Timer { + Timer::::new(&self, duration) } } diff --git a/src/duration.rs b/src/duration.rs index 9948946..222b2c4 100644 --- a/src/duration.rs +++ b/src/duration.rs @@ -1,137 +1,99 @@ //! Duration types/units creation and conversion. -use crate::{time_int::TimeInt, Period}; +use crate::{period::Period, time_int::TimeInt}; use core::{convert::TryFrom, fmt, mem::size_of, prelude::v1::*}; use num::Bounded; -/// A duration of time with signed, generic storage +/// An unsigned duration of time /// /// Each implementation defines a constant [`Period`] which is a fraction/ratio representing the /// period of the count's LSbit /// -/// # Implementation Example -/// ```rust,no_run -/// # use embedded_time::{Duration, Period, TimeInt}; -/// # use core::{fmt, fmt::Formatter}; -/// # -/// #[derive(Copy, Clone)] -/// struct Milliseconds(pub T); -/// -/// impl Duration for Milliseconds { -/// type Rep = T; // set the storage type -/// -/// // set LSbit period to 1 millisecond -/// const PERIOD: Period = Period::new(1, 1_000); -/// -/// fn new(value: Self::Rep) -> Self { -/// Self(value) -/// } -/// -/// fn count(self) -> Self::Rep { -/// self.0 -/// } -/// } -/// -/// impl fmt::Display for Milliseconds { -/// fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> { -/// unimplemented!() -/// } -/// -/// } -/// ``` /// /// # Constructing a duration /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(Milliseconds::::new(23), Milliseconds(23_i32)); -/// assert_eq!(Milliseconds(23), 23.milliseconds()); +/// assert_eq!(Milliseconds::::new(23), Milliseconds(23_u32)); +/// assert_eq!(Milliseconds(23_u32), 23_u32.milliseconds()); /// ``` /// /// # Get the integer count /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(Milliseconds(23).count(), 23); +/// assert_eq!(Milliseconds(23_u32).count(), 23_u32); /// ``` /// /// # Formatting /// Just forwards the underlying integer to [`core::fmt::Display::fmt()`] /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(format!("{}", Seconds(123)), "123"); +/// assert_eq!(format!("{}", Seconds(123_u32)), "123"); /// ``` /// /// # Getting H:M:S.MS... Components /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// let duration = 38_238_479.microseconds(); -/// let hours = Hours::::try_convert_from(duration).unwrap(); -/// let minutes = Minutes::::try_convert_from(duration).unwrap() % Hours(1); -/// let seconds = Seconds::::try_convert_from(duration).unwrap() % Minutes(1); -/// let milliseconds = Milliseconds::::try_convert_from(duration).unwrap() % Seconds(1); +/// let duration = 38_238_479_u32.microseconds(); +/// let hours = Hours::::try_convert_from(duration).unwrap(); +/// let minutes = Minutes::::try_convert_from(duration).unwrap() % Hours(1_u32); +/// let seconds = Seconds::::try_convert_from(duration).unwrap() % Minutes(1_u32); +/// let milliseconds = Milliseconds::::try_convert_from(duration).unwrap() % Seconds(1_u32); /// // ... /// ``` /// -/// # Converting to [`core::time::Duration`] +/// # Converting to `core` types +/// [`core::time::Duration`] +/// /// ## Examples /// ```rust -/// # use embedded_time::prelude::*; +/// # use embedded_time::traits::*; /// # use core::convert::TryFrom; /// # -/// let core_duration = core::time::Duration::try_from(2_569.milliseconds()).unwrap(); +/// let core_duration = core::time::Duration::try_from(2_569_u32.milliseconds()).unwrap(); /// assert_eq!(2, core_duration.as_secs()); /// assert_eq!(569_000_000, core_duration.subsec_nanos()); /// ``` /// ```rust -/// # use embedded_time::prelude::*; +/// # use embedded_time::traits::*; /// # use core::convert::TryInto; /// # -/// let core_duration: core::time::Duration = 2_569.milliseconds().try_into().unwrap(); +/// let core_duration: core::time::Duration = 2_569_u32.milliseconds().try_into().unwrap(); /// assert_eq!(2, core_duration.as_secs()); /// assert_eq!(569_000_000, core_duration.subsec_nanos()); /// ``` /// -/// ## Errors -/// Attempting to convert from a _negative_ duration will fail -/// ```rust -/// # use embedded_time::prelude::*; -/// # use core::convert::{TryFrom, TryInto}; -/// # -/// assert!(core::time::Duration::try_from((-2_569).milliseconds()).is_err()); +/// # Converting from `core` types +/// [`core::time::Duration`] /// -/// let core_duration: Result = (-2_569).milliseconds().try_into(); -/// assert!(core_duration.is_err()); -/// ``` -/// -/// # Converting from [`core::time::Duration`] /// ## Examples /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # use core::convert::TryFrom; /// # /// let core_duration = core::time::Duration::new(5, 730023852); -/// assert_eq!(Milliseconds::::try_from(core_duration), Ok(5_730.milliseconds())); +/// assert_eq!(Milliseconds::::try_from(core_duration), Ok(5_730.milliseconds())); /// ``` /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # use core::convert::TryInto; /// # -/// let duration: Result, _> = core::time::Duration::new(5, 730023852).try_into(); +/// let duration: Result, _> = core::time::Duration::new(5, 730023852).try_into(); /// assert_eq!(duration, Ok(5_730.milliseconds())); /// ``` /// /// ## Errors /// The duration doesn't fit in the type specified /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # use core::convert::{TryFrom, TryInto}; /// # -/// assert!(Milliseconds::::try_from(core::time::Duration::from_millis((i32::MAX as u64) + 1)).is_err()); +/// assert!(Milliseconds::::try_from(core::time::Duration::from_millis((u32::MAX as u64) + 1)).is_err()); /// -/// let duration: Result, _> = core::time::Duration::from_millis((i32::MAX as u64) + 1).try_into(); +/// let duration: Result, _> = core::time::Duration::from_millis((u32::MAX as u64) + 1).try_into(); /// assert!(duration.is_err()); /// ``` /// @@ -140,137 +102,137 @@ use num::Bounded; /// ## Panics /// Panics if the rhs duration cannot be converted into the lhs duration type /// -/// In this example, the maximum `i32` value of seconds is stored as `i32` and -/// converting that value to milliseconds (with `i32` storage type) causes an overflow. +/// In this example, the maximum `u32` value of seconds is stored as `u32` and +/// converting that value to milliseconds (with `u32` storage type) causes an overflow. /// ```rust,should_panic -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// let _ = Milliseconds(24) + Seconds(i32::MAX); +/// let _ = Milliseconds(24_u32) + Seconds(u32::MAX); /// ``` /// -/// This example works just fine as the seconds value is first cast to `i64`, then +/// This example works just fine as the seconds value is first cast to `u64`, then /// converted to milliseconds. /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// let _ = Milliseconds(24_i64) + Seconds(i32::MAX); +/// let _ = Milliseconds(24_u64) + Seconds(u32::MAX); /// ``` /// -/// Here, there is no units conversion to worry about, but `i32::MAX + 1` cannot be -/// cast to an `i32`. +/// Here, there is no units conversion to worry about, but `u32::MAX + 1` cannot be +/// cast to an `u32`. /// ```rust,should_panic -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// let _ = Seconds(i32::MAX) - Seconds(i32::MAX as i64 + 1); +/// let _ = Seconds(u32::MAX) - Seconds(u32::MAX as u64 + 1); /// ``` /// /// ## Examples /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!((Milliseconds(3_234) - Seconds(2)), Milliseconds(1_234)); -/// assert_eq!((Milliseconds(3_234_i64) - Seconds(2_i32)), Milliseconds(1_234_i64)); -/// assert_eq!((Seconds(i32::MAX) - Milliseconds((i32::MAX as i64) + 1)), -/// Seconds(2_145_336_164_i32)); +/// assert_eq!((Milliseconds(3_234_u32) - Seconds(2_u32)), Milliseconds(1_234_u32)); +/// assert_eq!((Milliseconds(3_234_u64) - Seconds(2_u32)), Milliseconds(1_234_u64)); +/// assert_eq!((Seconds(u32::MAX) - Milliseconds((u32::MAX as u64) + 1)), +/// Seconds(4_290_672_328_u32)); /// ``` /// /// # Equality /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(Seconds(123), Seconds(123)); -/// assert_eq!(Seconds(123), Milliseconds(123_000)); -/// -/// assert_ne!(Seconds(123), Milliseconds(123_001)); -/// assert_ne!(Milliseconds(123_001), Seconds(123)); -/// assert_ne!(Milliseconds(123_001_i64), Seconds(123_i64)); -/// assert_ne!(Seconds(123_i64), Milliseconds(123_001_i64)); -/// assert_ne!(Seconds(123_i64), Milliseconds(123_001_i32)); +/// assert_eq!(Seconds(123_u32), Seconds(123_u32)); +/// assert_eq!(Seconds(123_u32), Milliseconds(123_000_u32)); +/// +/// assert_ne!(Seconds(123_u32), Milliseconds(123_001_u32)); +/// assert_ne!(Milliseconds(123_001_u32), Seconds(123_u32)); +/// assert_ne!(Milliseconds(123_001_u64), Seconds(123_u64)); +/// assert_ne!(Seconds(123_u64), Milliseconds(123_001_u64)); +/// assert_ne!(Seconds(123_u64), Milliseconds(123_001_u32)); /// ``` /// /// # Comparisons /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert!(Seconds(2) < Seconds(3)); -/// assert!(Seconds(2) < Milliseconds(2_001)); -/// assert!(Seconds(2) == Milliseconds(2_000)); -/// assert!(Seconds(2) > Milliseconds(1_999)); -/// assert!(Seconds(2_i32) < Milliseconds(2_001_i64)); -/// assert!(Seconds(2_i64) < Milliseconds(2_001_i32)); +/// assert!(Seconds(2_u32) < Seconds(3_u32)); +/// assert!(Seconds(2_u32) < Milliseconds(2_001_u32)); +/// assert!(Seconds(2_u32) == Milliseconds(2_000_u32)); +/// assert!(Seconds(2_u32) > Milliseconds(1_999_u32)); +/// assert!(Seconds(2_u32) < Milliseconds(2_001_u64)); +/// assert!(Seconds(2_u64) < Milliseconds(2_001_u32)); /// ``` /// /// # Remainder /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// # -/// assert_eq!(Minutes(62) % Hours(1), Minutes(2)); +/// assert_eq!(Minutes(62_u32) % Hours(1_u32), Minutes(2_u32)); /// ``` pub trait Duration: Sized + Copy + fmt::Display { /// The inner type of the `Duration` representing the count of the implementation unit type Rep: TimeInt; - /// A fraction/ratio representing the period of the count's LSbit. The precision of the `Duration`. + /// A fraction/ratio representing the period of the count's LSbit. The precision of the + /// `Duration`. const PERIOD: Period; /// Not generally useful or needed as the duration can be constructed like this: /// ```no_run - /// # use embedded_time::{prelude::*, units::*}; - /// Seconds(123); - /// 123.seconds(); + /// # use embedded_time::{traits::*, units::*}; + /// Seconds(123_u32); + /// 123_u32.seconds(); /// ``` /// It only exists to allow Duration methods with default definitions to create a /// new duration fn new(value: Self::Rep) -> Self; - /// Returns the integer value of the [`Duration`] + /// Returns the integer value of the `Duration` /// /// # Examples /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds(123).count(), 123); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds(123_u32).count(), 123_u32); /// ``` fn count(self) -> Self::Rep; - /// Constructs a [`Duration`] from a value of ticks and a period + /// Constructs a `Duration` from a value of ticks and a period /// /// # Examples /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Microseconds::::from_ticks(5_i64, Period::new(1, 1_000)), - /// Some(Microseconds(5_000_i32))); + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Microseconds::::from_ticks(5_u64, ::new(1, 1_000)), + /// Some(Microseconds(5_000_u32))); /// /// // the conversion arithmetic will not cause overflow - /// assert_eq!(Milliseconds::::from_ticks((i32::MAX as i64) + 1, Period::new(1, 1_000_000)), - /// Some(Milliseconds((((i32::MAX as i64) + 1) / 1_000) as i32))); + /// assert_eq!(Milliseconds::::from_ticks((u32::MAX as u64) + 1, ::new(1, 1_000_000)), + /// Some(Milliseconds((((u32::MAX as u64) + 1) / 1_000) as u32))); /// ``` /// /// # Errors /// the conversion of periods causes an overflow: /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Milliseconds::::from_ticks(i32::MAX, Period::new(1, 1)), + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Milliseconds::::from_ticks(u32::MAX, ::new(1, 1)), /// None); /// ``` /// /// the Self integer cast to that of the provided type fails /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Seconds::::from_ticks(i32::MAX as i64 + 1, Period::new(1, 1)), + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Seconds::::from_ticks(u32::MAX as u64 + 1, ::new(1, 1)), /// None); /// ``` /// /// # Returns /// [`None`] if the result of the conversion does not fit in the requested integer size - fn from_ticks(ticks: Rep, period: Period) -> Option + fn from_ticks(ticks: Rep, period: Period) -> Option where - Self::Rep: TimeInt + TryFrom, - Rep: TimeInt, + Self::Rep: TryFrom, { if size_of::() > size_of::() { let converted_ticks = Self::Rep::try_from(ticks).ok()?; - if period > Period::new(1, 1) { + if period > ::new(1, 1) { Some(Self::new(TimeInt::checked_div_period( &TimeInt::checked_mul_period(&converted_ticks, &period)?, &Self::PERIOD, @@ -282,12 +244,12 @@ pub trait Duration: Sized + Copy + fmt::Display { )?)) } } else { - let ticks = if period > Period::new(1, 1) { + let ticks = if period > ::new(1, 1) { TimeInt::checked_div_period( &TimeInt::checked_mul_period(&ticks, &period)?, &Self::PERIOD, )? - } else if Self::PERIOD > Period::new(1, 1) { + } else if Self::PERIOD > ::new(1, 1) { TimeInt::checked_mul_period( &TimeInt::checked_div_period(&ticks, &Self::PERIOD)?, &period, @@ -308,28 +270,28 @@ pub trait Duration: Sized + Copy + fmt::Display { /// /// # Examples /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Microseconds(5_000_i32).into_ticks::(Period::new(1, 1_000)), Some(5_i32)); + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Microseconds(5_000_u32).into_ticks::(Period::new(1, 1_000)), Some(5_u32)); /// /// // the _into_ period can be any value - /// assert_eq!(Microseconds(5_000_i32).into_ticks::(Period::new(1, 200)), Some(1_i32)); + /// assert_eq!(Microseconds(5_000_u32).into_ticks::(Period::new(1, 200)), Some(1_u32)); /// /// // as long as the result fits in the provided integer, it will succeed - /// assert_eq!(Microseconds::(i32::MAX).into_ticks::(Period::new(1, 2_000_000)), - /// Some((i32::MAX as i64) * 2)); + /// assert_eq!(Microseconds::(u32::MAX).into_ticks::(Period::new(1, 2_000_000)), + /// Some((u32::MAX as u64) * 2)); /// ``` /// /// # Errors /// the conversion of periods causes an overflow: /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Seconds(i32::MAX).into_ticks::(Period::new(1, 1_000)), None); + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Seconds(u32::MAX).into_ticks::(Period::new(1, 1_000)), None); /// ``` /// /// the Self integer cast to that of the provided type fails /// ```rust - /// # use embedded_time::{prelude::*, units::*, Period}; - /// assert_eq!(Seconds(i32::MAX as i64 + 1).into_ticks::(Period::new(1, 1)), None); + /// # use embedded_time::{traits::*, units::*, Period}; + /// assert_eq!(Seconds(u32::MAX as u64 + 1).into_ticks::(Period::new(1, 1)), None); /// ``` /// /// # Returns @@ -342,7 +304,7 @@ pub trait Duration: Sized + Copy + fmt::Display { if size_of::() > size_of::() { let ticks = Rep::try_from(self.count()).ok()?; - if period > Period::new(1, 1) { + if period > ::new(1, 1) { Some(TimeInt::checked_div_period( &TimeInt::checked_mul_period(&ticks, &Self::PERIOD)?, &period, @@ -354,7 +316,7 @@ pub trait Duration: Sized + Copy + fmt::Display { )?) } } else { - let ticks = if Self::PERIOD > Period::new(1, 1) { + let ticks = if Self::PERIOD > ::new(1, 1) { TimeInt::checked_div_period( &TimeInt::checked_mul_period(&self.count(), &Self::PERIOD)?, &period, @@ -371,8 +333,8 @@ pub trait Duration: Sized + Copy + fmt::Display { } /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds::::min_value(), i32::MIN); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds::::min_value(), u32::MIN); /// ``` #[must_use] fn min_value() -> Self::Rep { @@ -380,8 +342,8 @@ pub trait Duration: Sized + Copy + fmt::Display { } /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds::::max_value(), i32::MAX); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds::::max_value(), u32::MAX); /// ``` #[must_use] fn max_value() -> Self::Rep { @@ -405,12 +367,9 @@ pub trait TryConvertInto { fn try_convert_into(self) -> Option; } -impl TryConvertFrom for Dest +impl TryConvertFrom for Dest where - Dest: Duration, - Dest::Rep: TimeInt + TryFrom, - Source: Duration, - Source::Rep: TimeInt, + Dest::Rep: TryFrom, { /// Attempt to convert from one duration type to another /// @@ -418,33 +377,33 @@ where /// /// # Examples /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds::::try_convert_from(Milliseconds(23_000_i64)), Some(Seconds(23_i32))); - /// assert_eq!(Seconds::::try_convert_from(Milliseconds(23_000_i32)), Some(Seconds(23_i64))); - /// assert_eq!(Seconds::::try_convert_from(Milliseconds(230_i32)), Some(Seconds(0))); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds::::try_convert_from(Milliseconds(23_000_u64)), Some(Seconds(23_u32))); + /// assert_eq!(Seconds::::try_convert_from(Milliseconds(23_000_u32)), Some(Seconds(23_u64))); + /// assert_eq!(Seconds::::try_convert_from(Milliseconds(230_u32)), Some(Seconds(0))); /// ``` /// /// # Errors /// the conversion of periods causes an overflow: /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Milliseconds::::try_convert_from(Seconds(i32::MAX)), None); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Milliseconds::::try_convert_from(Seconds(u32::MAX)), None); /// ``` /// /// the Self integer cast to that of the provided type fails /// ```rust - /// # use embedded_time::{prelude::*, units::*}; - /// assert_eq!(Seconds::::try_convert_from(Seconds(i32::MAX as i64 + 1)), None); + /// # use embedded_time::{traits::*, units::*}; + /// assert_eq!(Seconds::::try_convert_from(Seconds(u32::MAX as u64 + 1)), None); /// ``` /// /// However, these work because the sequence of cast/conversion adapts /// ```rust - /// # use embedded_time::{prelude::*, units::*}; + /// # use embedded_time::{traits::*, units::*}; /// // period conversion applied first - /// assert_eq!(Hours::::try_convert_from(Microseconds(3_600_000_000_i64)), Some(Hours(1_i32))); + /// assert_eq!(Hours::::try_convert_from(Microseconds(3_600_000_000_u64)), Some(Hours(1_u32))); /// /// // cast applied first - /// assert_eq!(Microseconds::::try_convert_from(Hours(1_i32)), Some(Microseconds(3_600_000_000_i64))); + /// assert_eq!(Microseconds::::try_convert_from(Hours(1_u32)), Some(Microseconds(3_600_000_000_u64))); /// ``` /// /// # Returns @@ -458,33 +417,33 @@ where /// /// # Examples /// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!(Seconds(23_i64).try_convert_into(), Some(Seconds(23_i32))); -/// assert_eq!(Some(Seconds(23_i64)), (Seconds(23_i32).try_convert_into())); -/// assert_eq!(Milliseconds(23_000_i64).try_convert_into(), Some(Seconds(23_i32))); +/// # use embedded_time::{traits::*, units::*}; +/// assert_eq!(Seconds(23_u64).try_convert_into(), Some(Seconds(23_u32))); +/// assert_eq!(Some(Seconds(23_u64)), (Seconds(23_u32).try_convert_into())); +/// assert_eq!(Milliseconds(23_000_u64).try_convert_into(), Some(Seconds(23_u32))); /// ``` /// /// # Errors /// the conversion of periods causes an overflow: /// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!(Seconds(i32::MAX).try_convert_into(), None::>); +/// # use embedded_time::{traits::*, units::*}; +/// assert_eq!(Seconds(u32::MAX).try_convert_into(), None::>); /// ``` /// /// the Self integer cast to that of the destination type fails /// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!(Seconds(i32::MAX as i64 + 1).try_convert_into(), None::>); +/// # use embedded_time::{traits::*, units::*}; +/// assert_eq!(Seconds(u32::MAX as u64 + 1).try_convert_into(), None::>); /// ``` /// /// However, these work because the sequence of cast/conversion adapts /// ```rust -/// # use embedded_time::{prelude::*, units::*}; +/// # use embedded_time::{traits::*, units::*}; /// // period conversion applied first -/// assert_eq!(Microseconds(3_600_000_000_i64).try_convert_into(), Some(Hours(1_i32))); +/// assert_eq!(Microseconds(3_600_000_000_u64).try_convert_into(), Some(Hours(1_u32))); /// /// // cast applied first -/// assert_eq!(Hours(1_i32).try_convert_into(), Some(Microseconds(3_600_000_000_i64))); +/// assert_eq!(Hours(1_u32).try_convert_into(), Some(Microseconds(3_600_000_000_u64))); /// ``` /// /// # Returns @@ -499,6 +458,7 @@ where } } +#[doc(hidden)] pub mod units { use crate::{ duration::{Duration, TryConvertFrom}, @@ -516,11 +476,11 @@ pub mod units { ( $name:ident, ($numer:expr, $denom:expr) ) => { /// A duration unit type #[derive(Copy, Clone, Debug, Eq, Ord)] - pub struct $name(pub T); + pub struct $name(pub T); impl Duration for $name { type Rep = Rep; - const PERIOD: Period = Period::new($numer, $denom); + const PERIOD: Period = ::new($numer, $denom); fn new(value: Self::Rep) -> Self { Self(value) @@ -635,8 +595,8 @@ pub mod units { /// Convert an embedded_time::[`Duration`] into a [`core::time::Duration`] fn try_from(duration: $name) -> Result { - let seconds = Seconds::::try_convert_from(duration).ok_or(())?; - Ok(Self::from_secs(seconds.count().try_into().or(Err(()))?)) + let seconds = Seconds::::try_convert_from(duration).ok_or(())?; + Ok(Self::from_secs(seconds.count())) } } @@ -645,7 +605,7 @@ pub mod units { /// Convert a [`core::time::Duration`] into an embedded_time::[`Duration`] fn try_from(core_duration: core::time::Duration) -> Result { - let seconds = Seconds::(core_duration.as_secs().try_into().or(Err(()))?); + let seconds = Seconds(core_duration.as_secs()); Ok(Self::try_convert_from(seconds).ok_or(())?) } } @@ -658,7 +618,7 @@ pub mod units { /// Convert an embedded_time::[`Duration`] into a [`core::time::Duration`] fn try_from(duration: $name) -> Result { - Ok(Self::$from_core_dur(duration.count().try_into()?)) + Ok(Self::$from_core_dur(duration.count().into())) } } @@ -688,24 +648,24 @@ mod tests { #[test] fn check_for_overflows() { - let mut time = 1_i64; + let mut time = 1_u64; time *= 60; - assert_eq!(Hours(1), Minutes(time)); + assert_eq!(Hours(1_u32), Minutes(time)); time *= 60; - assert_eq!(Hours(1), Seconds(time)); + assert_eq!(Hours(1_u32), Seconds(time)); time *= 1000; - assert_eq!(Hours(1), Milliseconds(time)); + assert_eq!(Hours(1_u32), Milliseconds(time)); time *= 1000; - assert_eq!(Hours(1), Microseconds(time)); + assert_eq!(Hours(1_u32), Microseconds(time)); time *= 1000; - assert_eq!(Hours(1), Nanoseconds(time)); + assert_eq!(Hours(1_u32), Nanoseconds(time)); } #[test] fn remainder() { - assert_eq!(Minutes(62) % Hours(1), Minutes(2)); - assert_eq!(Minutes(62) % Milliseconds(1), Minutes(0)); - assert_eq!(Minutes(62) % Minutes(60), Minutes(2)); + assert_eq!(Minutes(62_u32) % Hours(1_u32), Minutes(2_u32)); + assert_eq!(Minutes(62_u32) % Milliseconds(1_u32), Minutes(0_u32)); + assert_eq!(Minutes(62_u32) % Minutes(60_u32), Minutes(2_u32)); } #[test] @@ -713,42 +673,42 @@ mod tests { let core_duration = core::time::Duration::from_nanos(5_025_678_901_234); assert_eq!( core_duration.try_into(), - Ok(Nanoseconds::(5_025_678_901_234)) + Ok(Nanoseconds::(5_025_678_901_234)) ); assert_eq!( core_duration.try_into(), - Ok(Microseconds::(5_025_678_901)) + Ok(Microseconds::(5_025_678_901)) ); - assert_eq!(core_duration.try_into(), Ok(Milliseconds::(5_025_678))); - assert_eq!(core_duration.try_into(), Ok(Seconds::(5_025))); - assert_eq!(core_duration.try_into(), Ok(Minutes::(83))); - assert_eq!(core_duration.try_into(), Ok(Hours::(1))); + assert_eq!(core_duration.try_into(), Ok(Milliseconds::(5_025_678))); + assert_eq!(core_duration.try_into(), Ok(Seconds::(5_025))); + assert_eq!(core_duration.try_into(), Ok(Minutes::(83))); + assert_eq!(core_duration.try_into(), Ok(Hours::(1))); } #[test] fn convert_to_core_duration() { assert_eq!( - Nanoseconds(123).try_into(), + Nanoseconds(123_u32).try_into(), Ok(core::time::Duration::from_nanos(123)) ); assert_eq!( - Microseconds(123).try_into(), + Microseconds(123_u32).try_into(), Ok(core::time::Duration::from_micros(123)) ); assert_eq!( - Milliseconds(123).try_into(), + Milliseconds(123_u32).try_into(), Ok(core::time::Duration::from_millis(123)) ); assert_eq!( - Seconds(123).try_into(), + Seconds(123_u32).try_into(), Ok(core::time::Duration::from_secs(123)) ); assert_eq!( - Minutes(123).try_into(), + Minutes(123_u32).try_into(), Ok(core::time::Duration::from_secs(123 * 60)) ); assert_eq!( - Hours(123).try_into(), + Hours(123_u32).try_into(), Ok(core::time::Duration::from_secs(123 * 3600)) ); } diff --git a/src/frequency.rs b/src/frequency.rs index 6ea8223..db547da 100644 --- a/src/frequency.rs +++ b/src/frequency.rs @@ -5,13 +5,15 @@ pub(crate) mod units { use core::{convert, ops}; /// A frequency unit type + /// + /// Convertible to/from [`Period`]. #[derive(Ord, PartialOrd, Eq, PartialEq, Debug)] - pub struct Hertz(pub T); + pub struct Hertz(pub T); impl Hertz { /// ```rust /// # use embedded_time::{Period, units::*}; - /// assert_eq!(Hertz(1_000).into_period(), Period::new(1, 1_000)); + /// assert_eq!(Hertz(1_000_u32).into_period(), ::new(1, 1_000)); /// ``` pub fn into_period(self) -> Period { Period::from_frequency(self) @@ -19,7 +21,7 @@ pub(crate) mod units { /// ```rust /// # use embedded_time::{Period, units::*}; - /// assert_eq!(Hertz::from_period(Period::new(1, 1_000)), Hertz(1_000)); + /// assert_eq!(Hertz::from_period(Period::new(1, 1_000)), Hertz(1_000_u32)); /// ``` pub fn from_period(period: Period) -> Self { period.to_frequency() @@ -34,7 +36,7 @@ pub(crate) mod units { /// ```rust /// # use embedded_time::units::*; - /// assert_eq!(Hertz(100) * 3, Hertz(300)); + /// assert_eq!(Hertz(100_u32) * 3_u32, Hertz(300_u32)); /// ``` fn mul(self, rhs: Rhs) -> Self::Output { Self(self.0 * >::from(rhs)) @@ -49,7 +51,7 @@ pub(crate) mod units { /// ```rust /// # use embedded_time::units::*; - /// assert_eq!(Hertz(300) / 3, Hertz(100)); + /// assert_eq!(Hertz(300_u32) / 3_u32, Hertz(100_u32)); /// ``` fn div(self, rhs: Rhs) -> Self::Output { Self(self.0 / >::from(rhs)) diff --git a/src/instant.rs b/src/instant.rs index 452b0ba..d323cea 100644 --- a/src/instant.rs +++ b/src/instant.rs @@ -1,14 +1,39 @@ //! An instant of time -use crate::Duration; +use crate::duration::Duration; use core::{cmp::Ordering, convert::TryFrom, ops}; use num::traits::{WrappingAdd, WrappingSub}; /// Represents an instant of time relative to a specific [`Clock`](crate::clock::Clock) /// /// # Example -/// Create an `Instant` that is `23 * SomeClock::PERIOD` seconds since the clock's epoch: -/// ```rust,ignore +/// Typically an `Instant` will be obtained from a [`Clock`](crate::clock::Clock) +/// ```rust +/// # use embedded_time::{Period, traits::*, Instant}; +/// # #[derive(Debug)] +/// # struct SomeClock; +/// # impl embedded_time::Clock for SomeClock { +/// # type Rep = u32; +/// # const PERIOD: Period = ::new(1, 1_000); +/// # type ImplError = (); +/// # fn now(&self) -> Result, embedded_time::clock::Error> {Ok(Instant::::new(23))} +/// # } +/// let some_clock = SomeClock; +/// let some_instant = some_clock.now().unwrap(); +/// ``` +/// +/// However, an `Instant` can also be constructed directly. In this case the constructed `Instant` +/// is `23 * SomeClock::PERIOD` seconds since the clock's epoch +/// ```rust,no_run +/// # use embedded_time::{Period, Instant}; +/// # #[derive(Debug)] +/// # struct SomeClock; +/// # impl embedded_time::Clock for SomeClock { +/// # type Rep = u32; +/// # const PERIOD: Period = ::new(1, 1_000); +/// # type ImplError = (); +/// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} +/// # } /// Instant::::new(23); /// ``` #[derive(Debug)] @@ -22,49 +47,91 @@ impl Instant { Self { ticks } } - /// Calculates the difference between two `Instance`s resulting in a [`Duration`] + /// Returns the [`Duration`] since the given `Instant` + /// + /// # Errors + /// - `()`: RHS is a future `Instant` + /// - `()`: problem coverting to desired [`Duration`] /// /// # Examples /// ```rust /// # use embedded_time::{Period, units::*, Instant}; /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + /// # /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// - /// let diff: Option> = Instant::::new(5).duration_since(&Instant::::new(3)); - /// assert_eq!(diff, Some(Milliseconds(2_i32))); + /// assert_eq!(Instant::::new(5).duration_since::>(&Instant::::new(3)), + /// Ok(Microseconds(2_000_u64))); /// - /// let diff: Option> = Instant::::new(5).duration_since(&Instant::::new(3)); - /// assert_eq!(diff, Some(Microseconds(2_000_i64))); + /// assert_eq!(Instant::::new(3).duration_since::>(&Instant::::new(5)), + /// Err(())); + /// ``` + pub fn duration_since(&self, other: &Self) -> Result + where + Dur::Rep: TryFrom, + { + if self < other { + Err(()) + } else { + Dur::from_ticks(self.ticks.wrapping_sub(&other.ticks), Clock::PERIOD).ok_or(()) + } + } + + /// Returns the [`Duration`] until the given `Instant` + /// + /// # Errors + /// - `()`: RHS is a past `Instant` + /// - `()`: problem coverting to desired [`Duration`] + /// + /// # Examples + /// ```rust + /// # use embedded_time::{Period, units::*, Instant}; + /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + /// # + /// struct Clock; + /// impl embedded_time::Clock for Clock { + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); + /// // ... + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} + /// } /// - /// let diff: Option> = Instant::::new(i32::MIN).duration_since(&Instant::::new(i32::MAX)); - /// assert_eq!(diff, Some(Microseconds(1_000_i64))); + /// assert_eq!(Instant::::new(5).duration_until::>(&Instant::::new(7)), + /// Ok(Microseconds(2_000_u64))); /// - /// let diff: Option> = Instant::::new(1_000).duration_since(&Instant::::new(-1_000)); - /// assert_eq!(diff, Some(Seconds(2_i64))); + /// assert_eq!(Instant::::new(7).duration_until::>(&Instant::::new(5)), + /// Err(())); /// ``` - pub fn duration_since(&self, other: &Self) -> Option + pub fn duration_until(&self, other: &Self) -> Result where Dur::Rep: TryFrom, { - Dur::from_ticks(self.ticks.wrapping_sub(&other.ticks), Clock::PERIOD) + if self > other { + Err(()) + } else { + Dur::from_ticks(other.ticks.wrapping_sub(&self.ticks), Clock::PERIOD).ok_or(()) + } } - /// Returns the [`Duration`] (in the provided units) since the beginning of time (or the [`Clock`](crate::clock::Clock)'s 0) - pub fn duration_since_epoch(&self) -> Option + /// Returns the [`Duration`] (in the provided units) since the beginning of + /// time (or the [`Clock`](trait.Clock.html)'s 0) + pub fn duration_since_epoch(&self) -> Result where Dur::Rep: TryFrom, - Clock::Rep: From, + Clock::Rep: TryFrom, { Self::duration_since::( &self, &Self { - ticks: Clock::Rep::from(0_i32), + ticks: Clock::Rep::from(0), }, ) } @@ -95,15 +162,16 @@ impl PartialOrd for Instant { /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// /// assert!(Instant::::new(5) > Instant::::new(3)); /// assert!(Instant::::new(5) == Instant::::new(5)); - /// assert!(Instant::::new(i32::MAX) < Instant::::new(i32::MIN)); + /// assert!(Instant::::new(u32::MAX) < Instant::::new(u32::MIN)); /// ``` fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(&other)) @@ -114,7 +182,8 @@ impl Ord for Instant { fn cmp(&self, other: &Self) -> Ordering { self.ticks .wrapping_sub(&other.ticks) - .cmp(&Clock::Rep::from(0)) + .cmp(&(::max_value() / 2.into())) + .reverse() } } @@ -127,22 +196,41 @@ where /// Add a duration to an instant resulting in a new, later instance /// /// # Panics - /// If [`Duration::into_ticks()`] returns [`None`]. In this case, `i32::MAX` of seconds - /// cannot be converted to the clock precision of milliseconds with i32 storage. + /// If [`Duration::into_ticks()`] returns [`None`]. In this case, `u32::MAX` of seconds + /// cannot be converted to the clock precision of milliseconds with u32 storage. /// ```rust,should_panic /// # use embedded_time::{Period, units::*, Instant}; /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// - /// Instant::::new(1) + Seconds(i32::MAX); + /// Instant::::new(1) + Seconds(u32::MAX); /// ``` - /// See also: [`impl Add for Duration`](duration/units/index.html#addsub) + /// + /// If the the [`Duration`] is greater than the signed Clock::Rep max value which would cause + /// the result to (logically) overflow + /// ```rust,should_panic + /// # use embedded_time::{Period, units::*, Instant}; + /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + /// struct Clock; + /// impl embedded_time::Clock for Clock { + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); + /// // ... + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} + /// } + /// + /// let _ = Instant::::new(0) + Milliseconds(i32::MAX as u32 + 1); + /// ``` + /// + /// See also [`impl Sub for Duration`](duration/units/index.html#addsub) /// /// # Examples /// ```rust @@ -150,19 +238,24 @@ where /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// - /// assert_eq!(Instant::::new(1) + Seconds(3), Instant::::new(3_001)); - /// assert_eq!(Instant::::new(-1) + Milliseconds(5_123), Instant::::new(5_122)); - /// assert_eq!(Instant::::new(1) + Milliseconds(700), Instant::::new(701)); - /// assert_eq!(Instant::::new(1_i32) + Milliseconds(700_i64), Instant::::new(701_i32)); + /// assert_eq!(Instant::::new(1) + Seconds(3_u32), Instant::::new(3_001)); + /// assert_eq!(Instant::::new(1) + Milliseconds(700_u32), Instant::::new(701)); + /// assert_eq!(Instant::::new(1) + Milliseconds(700_u64), Instant::::new(701)); + /// + /// // maximum duration allowed + /// assert_eq!(Instant::::new(0) + Milliseconds(i32::MAX as u32), + /// Instant::::new(u32::MAX/2)); /// ``` fn add(self, rhs: Dur) -> Self::Output { let add_ticks: Clock::Rep = rhs.into_ticks(Clock::PERIOD).unwrap(); + debug_assert!(add_ticks < ::max_value() / 2.into() + 1.into()); Self { ticks: self.ticks.wrapping_add(&add_ticks), @@ -179,21 +272,40 @@ where /// Subtract a duration from an instant resulting in a new, earlier instance /// /// # Panics - /// If [`Duration::into_ticks()`] returns [`None`]. In this case, `i32::MAX` of seconds - /// cannot be converted to the clock precision of milliseconds with i32 storage. + /// If [`Duration::into_ticks()`] returns [`None`]. In this case, `u32::MAX` of seconds + /// cannot be converted to the clock precision of milliseconds with u32 storage. + /// ```rust,should_panic + /// # use embedded_time::{Period, units::*, Instant}; + /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + /// struct Clock; + /// impl embedded_time::Clock for Clock { + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); + /// // ... + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} + /// } + /// + /// Instant::::new(1) - Seconds(u32::MAX); + /// ``` + /// + /// If the the [`Duration`] is greater than the signed Clock::Rep max value which would cause + /// the result to (logically) underflow /// ```rust,should_panic /// # use embedded_time::{Period, units::*, Instant}; /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period = ::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// - /// Instant::::new(1) - Seconds(i32::MAX); + /// let _ = Instant::::new(u32::MAX) - Milliseconds(i32::MAX as u32 + 1); /// ``` + /// /// See also [`impl Sub for Duration`](duration/units/index.html#addsub) /// /// # Examples @@ -202,22 +314,63 @@ where /// # #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] /// struct Clock; /// impl embedded_time::Clock for Clock { - /// type Rep = i32; - /// const PERIOD: Period = Period::new(1, 1_000); + /// type Rep = u32; + /// const PERIOD: Period =::new(1, 1_000); /// // ... - /// # fn now() -> Instant {unimplemented!()} + /// # type ImplError = (); + /// # fn now(&self) -> Result, embedded_time::clock::Error> {unimplemented!()} /// } /// - /// assert_eq!(Instant::::new(1) - Seconds(3), Instant::::new(-2_999)); - /// assert_eq!(Instant::::new(-1) - Milliseconds(5_123), Instant::::new(-5_124)); - /// assert_eq!(Instant::::new(800) - Milliseconds(700), Instant::::new(100)); - /// assert_eq!(Instant::::new(5_000_i32) - Milliseconds(700_i64), Instant::::new(4_300_i32)); + /// assert_eq!(Instant::::new(800) - Milliseconds(700_u32), Instant::::new(100)); + /// assert_eq!(Instant::::new(5_000) - Milliseconds(700_u64), Instant::::new(4_300)); + /// + /// // maximum duration allowed + /// assert_eq!(Instant::::new(u32::MAX) - Milliseconds(i32::MAX as u32), + /// Instant::::new(u32::MAX/2 + 1)); /// ``` fn sub(self, rhs: Dur) -> Self::Output { let sub_ticks: Clock::Rep = rhs.into_ticks(Clock::PERIOD).unwrap(); + debug_assert!(sub_ticks < ::max_value() / 2.into() + 1.into()); Self { ticks: self.ticks.wrapping_sub(&sub_ticks), } } } + +#[cfg(test)] +mod tests { + use crate::{self as time, units::*, Instant, Period}; + + #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] + struct Clock; + + impl time::Clock for Clock { + type Rep = u32; + const PERIOD: Period = ::new(1, 1_000); + type ImplError = (); + + fn now(&self) -> Result, time::clock::Error> { + unimplemented!() + } + } + + #[test] + fn duration_since() { + let diff: Result, _> = + Instant::::new(5).duration_since(&Instant::::new(3)); + assert_eq!(diff, Ok(Milliseconds(2_u32))); + + let diff: Result, _> = + Instant::::new(5).duration_since(&Instant::::new(3)); + assert_eq!(diff, Ok(Microseconds(2_000_u64))); + + let diff: Result, _> = + Instant::::new(u32::MIN).duration_since(&Instant::::new(u32::MAX)); + assert_eq!(diff, Ok(Microseconds(1_000_u64))); + + let diff: Result, _> = + Instant::::new(5).duration_since(&Instant::::new(6)); + assert_eq!(diff, Err(())); + } +} diff --git a/src/lib.rs b/src/lib.rs index 38b3d9a..f93db9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,33 @@ //! `embedded-time` provides a comprehensive library for implementing [`Clock`] abstractions over //! hardware to generate [`Instant`]s and using [`Duration`]s ([`Seconds`], [`Milliseconds`], etc) //! in embedded systems. The approach is similar to the C++ `chrono` library. A [`Duration`] -//! consists of an integer (whose type is chosen by the user to be either [`i32`] or [`i64`]) as -//! well as a `const` ratio where the integer value multiplied by the ratio is the [`Duration`] in -//! seconds. Put another way, the ratio is the precision of the LSbit of the integer. This structure -//! avoids unnecessary arithmetic. For example, if the [`Duration`] type is [`Milliseconds`], a call -//! to the [`Duration::count()`] method simply returns the stored integer value directly which is -//! the number of milliseconds being represented. Conversion arithmetic is only performed when -//! explicitly converting between time units. +//! consists of an integer (whose type is chosen by the user to be either [`u32`] or [`u64`]) as +//! well as a `const` fraction ([`Period`]) where the integer value multiplied by the fraction is +//! the [`Duration`] in seconds. Put another way, the ratio is the precision of the LSbit of the +//! integer. This structure avoids unnecessary arithmetic. For example, if the [`Duration`] type is +//! [`Milliseconds`], a call to the [`Duration::count()`] method simply returns the stored integer +//! value directly which is the number of milliseconds being represented. Conversion arithmetic is +//! only performed when explicitly converting between time units. +//! +//! In addition frequency-type types are available including [`Hertz`] ([`u32`]) and it's reciprocal +//! [`Period`] ([`u32`]/[`u32`] seconds). //! //! [`Seconds`]: units::Seconds //! [`Milliseconds`]: units::Milliseconds +//! [`Hertz`]: units::Hertz +//! [`Duration`]: duration/trait.Duration.html +//! [`Duration::count()`]: duration/trait.Duration.html#tymethod.count //! //! ## Definitions -//! **Clock**: Any entity that periodically counts (ie a hardware timer peripheral). Generally, -//! this needs to be monotonic. A wrapping clock is considered monotonic in this context as long as -//! it fulfills the other requirements. +//! **Clock**: Any entity that periodically counts (ie an external or peripheral hardware +//! timer/counter). Generally, this needs to be monotonic. A wrapping clock is considered monotonic +//! in this context as long as it fulfills the other requirements. //! //! **Wrapping Clock**: A clock that when at its maximum value, the next count is the minimum //! value. //! +//! **Timer**: An entity that counts toward an expiration. +//! //! **Instant**: A specific instant in time ("time-point") read from a clock. //! //! **Duration**: The difference of two instances. The time that has elapsed since an instant. A @@ -33,28 +41,30 @@ //! //! # Example Usage //! ```rust,no_run -//! # use embedded_time::{prelude::*, units::*, Instant, Period}; +//! # use embedded_time::{traits::*, units::*, Instant, Period}; //! # #[derive(Debug)] //! struct SomeClock; //! impl embedded_time::Clock for SomeClock { -//! type Rep = i64; -//! const PERIOD: Period = Period::new(1, 16_000_000); -//! -//! fn now() -> Instant { +//! type Rep = u64; +//! const PERIOD: Period = ::new(1, 16_000_000); +//! type ImplError = (); +//! +//! fn now(&self) -> Result, embedded_time::clock::Error> { //! // ... //! # unimplemented!() //! } //! } //! -//! let instant1 = SomeClock::now(); +//! let mut clock = SomeClock; +//! let instant1 = clock.now().unwrap(); //! // ... -//! let instant2 = SomeClock::now(); +//! let instant2 = clock.now().unwrap(); //! assert!(instant1 < instant2); // instant1 is *before* instant2 //! //! // duration is the difference between the instances -//! let duration: Option> = instant2.duration_since(&instant1); +//! let duration: Result, _> = instant2.duration_since(&instant1); //! -//! assert!(duration.is_some()); +//! assert!(duration.is_ok()); //! assert_eq!(instant1 + duration.unwrap(), instant2); //! ``` @@ -63,91 +73,139 @@ #![warn(missing_docs)] #![deny(intra_doc_link_resolution_failure)] -mod clock; -mod duration; +pub mod clock; +pub mod duration; mod frequency; mod instant; +mod numeric_constructor; mod period; mod time_int; +mod timer; pub use clock::Clock; -pub use duration::Duration; +use core::{convert::Infallible, fmt}; pub use instant::Instant; pub use period::Period; -pub use time_int::TimeInt; +pub(crate) use time_int::TimeInt; +pub use timer::Timer; /// Public _traits_ /// /// ```rust,no_run -/// use embedded_time::prelude::*; +/// use embedded_time::traits::*; /// ``` -pub mod prelude { +#[doc(hidden)] +pub mod traits { // Rename traits to `_` to avoid any potential name conflicts. + pub use crate::clock::Clock as _; pub use crate::duration::Duration as _; pub use crate::duration::TryConvertFrom as _; pub use crate::duration::TryConvertInto as _; + pub use crate::numeric_constructor::NumericConstructor as _; pub use crate::time_int::TimeInt as _; - pub use crate::Clock as _; } pub mod units { //! Time-based units of measure ([`Milliseconds`], [`Hertz`], etc) + #[doc(inline)] pub use crate::duration::units::*; + #[doc(inline)] pub use crate::frequency::units::*; } +/// General error-type trait implemented for all error types in this crate +pub trait Error: fmt::Debug {} +impl Error for () {} +impl Error for Infallible {} + #[cfg(test)] #[allow(unused_imports)] mod tests { - use crate as time; - use crate::prelude::*; - use crate::units::*; - use core::fmt::{self, Formatter}; + use crate::{self as time, clock, traits::*, units::*}; + use core::{ + convert::{Infallible, TryFrom, TryInto}, + fmt::{self, Formatter}, + }; - #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] struct MockClock64; - impl time::Clock for MockClock64 { - type Rep = i64; - const PERIOD: time::Period = time::Period::new(1, 64_000_000); + type Rep = u64; + const PERIOD: time::Period = ::new(1, 64_000_000); + type ImplError = Infallible; - fn now() -> time::Instant { - time::Instant::new(128_000_000) + fn now(&self) -> Result, time::clock::Error> { + Ok(time::Instant::new(128_000_000)) } } - #[derive(Debug, Ord, PartialOrd, Eq, PartialEq)] + #[derive(Debug)] struct MockClock32; impl time::Clock for MockClock32 { - type Rep = i32; + type Rep = u32; + const PERIOD: time::Period = ::new(1, 16_000_000); + type ImplError = Infallible; + + fn now(&self) -> Result, time::clock::Error> { + Ok(time::Instant::new(32_000_000)) + } + } + + #[non_exhaustive] + #[derive(Debug, Eq, PartialEq)] + pub enum ClockImplError { + NotStarted, + } + impl crate::Error for ClockImplError {} + + #[derive(Debug)] + struct BadClock; + + impl time::Clock for BadClock { + type Rep = u32; const PERIOD: time::Period = time::Period::new(1, 16_000_000); + type ImplError = ClockImplError; - fn now() -> time::Instant { - time::Instant::new(32_000_000) + fn now(&self) -> Result, time::clock::Error> { + Err(time::clock::Error::Other(ClockImplError::NotStarted)) } } - fn get_time() + fn get_time(clock: &Clock) where - M: time::Clock, + u32: TryFrom, + Clock::Rep: TryFrom, { - assert_eq!(M::now().duration_since_epoch(), Some(Seconds(2))); + assert_eq!( + clock.now().unwrap().duration_since_epoch(), + Ok(Seconds(2_u32)) + ); } #[test] fn common_types() { - let then = MockClock32::now(); - let now = MockClock32::now(); + let then = MockClock32.now().unwrap(); + let now = MockClock32.now().unwrap(); - get_time::(); - get_time::(); + let clock64 = MockClock64 {}; + let clock32 = MockClock32 {}; - let then = then - Seconds(1); + get_time(&clock64); + get_time(&clock32); + + let then = then - Seconds(1_u32); assert_ne!(then, now); assert!(then < now); } + #[test] + fn clock_error() { + assert_eq!( + BadClock.now(), + Err(time::clock::Error::Other(ClockImplError::NotStarted)) + ); + } + struct Timestamp(time::Instant) where Clock: time::Clock; @@ -168,16 +226,17 @@ mod tests { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let duration = self .0 - .duration_since_epoch::>() - .ok_or(fmt::Error {})?; + .duration_since_epoch::>() + .map_err(|_| fmt::Error {})?; - let hours = Hours::::try_convert_from(duration).ok_or(fmt::Error {})?; + let hours = Hours::::try_convert_from(duration).ok_or(fmt::Error {})?; let minutes = - Minutes::::try_convert_from(duration).ok_or(fmt::Error {})? % Hours(1); + Minutes::::try_convert_from(duration).ok_or(fmt::Error {})? % Hours(1_u32); let seconds = - Seconds::::try_convert_from(duration).ok_or(fmt::Error {})? % Minutes(1); - let milliseconds = - Milliseconds::::try_convert_from(duration).ok_or(fmt::Error {})? % Seconds(1); + Seconds::::try_convert_from(duration).ok_or(fmt::Error {})? % Minutes(1_u32); + let milliseconds = Milliseconds::::try_convert_from(duration) + .ok_or(fmt::Error {})? + % Seconds(1_u32); f.write_fmt(format_args!( "{}:{:02}:{:02}.{:03}", diff --git a/src/numeric_constructor.rs b/src/numeric_constructor.rs new file mode 100644 index 0000000..dfea9ed --- /dev/null +++ b/src/numeric_constructor.rs @@ -0,0 +1,83 @@ +//! Construction of time-based types from integers + +use crate::{duration::units::*, frequency::units::*, time_int::TimeInt}; + +/// Create time-based values from primitive and core numeric types. +/// +/// This trait is anonomously re-exported in [`traits`](crate::traits) +/// +/// # Examples +/// Basic construction of time-based values. +/// ```rust +/// # use embedded_time::{traits::*, units::*}; +/// assert_eq!(5_u32.nanoseconds(), Nanoseconds(5_u32)); +/// assert_eq!(5_u32.microseconds(), Microseconds(5_u32)); +/// assert_eq!(5_u32.milliseconds(), Milliseconds(5_u32)); +/// assert_eq!(5_u32.seconds(), Seconds(5_u32)); +/// assert_eq!(5_u32.minutes(), Minutes(5_u32)); +/// assert_eq!(5_u32.hours(), Hours(5_u32)); +/// +/// assert_eq!(5_u32.hertz(), Hertz(5_u32)); +/// ``` +pub trait NumericConstructor: TimeInt { + /// Construct the duration implementation + fn nanoseconds(self) -> Nanoseconds; + /// Construct the duration implementation + fn microseconds(self) -> Microseconds; + /// Construct the duration implementation + fn milliseconds(self) -> Milliseconds; + /// Construct the duration implementation + fn seconds(self) -> Seconds; + /// Construct the duration implementation + fn minutes(self) -> Minutes; + /// Construct the duration implementation + fn hours(self) -> Hours; + + /// Construct the frequency type + fn hertz(self) -> Hertz; +} + +macro_rules! impl_numeric_constructors { + ($($type:ty),* $(,)?) => { + $( + impl NumericConstructor for $type { + #[inline(always)] + fn nanoseconds(self) -> Nanoseconds<$type> { + Nanoseconds(self) + } + + #[inline(always)] + fn microseconds(self) -> Microseconds<$type> { + Microseconds(self) + } + + #[inline(always)] + fn milliseconds(self) -> Milliseconds<$type> { + Milliseconds(self) + } + + #[inline(always)] + fn seconds(self) -> Seconds<$type> { + Seconds(self) + } + + #[inline(always)] + fn minutes(self) -> Minutes<$type> { + Minutes(self) + } + + #[inline(always)] + fn hours(self) -> Hours<$type> { + Hours(self) + } + + #[inline(always)] + fn hertz(self) -> Hertz<$type> { + Hertz(self) + } + } + )* + }; + } + +impl_numeric_constructors![u32, u64]; diff --git a/src/period.rs b/src/period.rs index 2b68977..442b718 100644 --- a/src/period.rs +++ b/src/period.rs @@ -5,9 +5,14 @@ use num::{rational::Ratio, CheckedDiv, CheckedMul}; /// A fractional time period /// -/// Used primarily to define the period of one count of a [`crate::Duration`] type +/// Used primarily to define the period of one count of a [`Duration`], [`Instant`] and [`Clock`] +/// impl types but also convertable to/from [`Hertz`]. +/// +/// [`Duration`]: duration/trait.Duration.html +/// [`Clock`]: clock/trait.Clock.html +/// [`Instant`]: instant/struct.Instant.html #[derive(Debug)] -pub struct Period(Ratio); +pub struct Period(Ratio); impl Period { /// Construct a new fractional `Period`. @@ -46,9 +51,7 @@ impl Period { Self(Ratio::from_integer(freq.0).recip()) } - /// Returns an integer approximation of the Period - /// - /// Any remainder is truncated. + /// Returns the value truncated to an integer pub fn to_integer(&self) -> T { self.0.to_integer() } @@ -62,9 +65,9 @@ impl Period { /// ```rust /// # use embedded_time::Period; - /// assert_eq!(Period::new(1000, 1).checked_mul_integer(5), Some(Period::new(5_000, 1))); + /// assert_eq!(Period::new(1000, 1).checked_mul_integer(5_u32), Some(Period::new(5_000, 1))); /// - /// assert_eq!(Period::new(i32::MAX, 1).checked_mul_integer(2), None); + /// assert_eq!(Period::new(u32::MAX, 1).checked_mul_integer(2_u32), None); /// ``` pub fn checked_mul_integer(&self, multiplier: T) -> Option { Some(Self(Ratio::checked_mul( @@ -75,10 +78,10 @@ impl Period { /// ```rust /// # use embedded_time::Period; - /// assert_eq!(Period::new(1000, 1).checked_div_integer(5), Some(Period::new(200, 1))); - /// assert_eq!(Period::new(1, 1000).checked_div_integer(5), Some(Period::new(1, 5000))); + /// assert_eq!(Period::new(1000, 1).checked_div_integer(5_u32), Some(Period::new(200, 1))); + /// assert_eq!(Period::new(1, 1000).checked_div_integer(5_u32), Some(Period::new(1, 5000))); /// - /// assert_eq!(Period::new(1, i32::MAX).checked_div_integer(2), None); + /// assert_eq!(Period::new(1, u32::MAX).checked_div_integer(2_u32), None); /// ``` pub fn checked_div_integer(&self, divisor: T) -> Option { Some(Self(Ratio::checked_div( @@ -96,8 +99,8 @@ where /// ```rust /// # use embedded_time::Period; - /// assert_eq!(Period::new(1000, 1) * Period::new(5,5), - /// Period::new(5_000, 5)); + /// assert_eq!(Period::::new(1000, 1) * ::new(5,5), + /// ::new(5_000, 5)); /// ``` fn mul(self, rhs: Self) -> Self::Output { Self(self.0 * rhs.0) @@ -113,7 +116,7 @@ where /// assert_eq!(::checked_mul(&Period::new(1000, 1), /// &Period::new(5,5)), Some(Period::new(5_000, 5))); /// - /// assert_eq!(::checked_mul(&Period::new(i32::MAX, 1), + /// assert_eq!(::checked_mul(&Period::new(u32::MAX, 1), /// &Period::new(2,1)), None); /// ``` fn checked_mul(&self, v: &Self) -> Option { @@ -129,8 +132,8 @@ where /// ```rust /// # use embedded_time::Period; - /// assert_eq!(Period::new(1000, 1) / Period::new(10, 1_000), - /// Period::new(1_000_000, 10)); + /// assert_eq!(Period::::new(1000, 1) / ::new(10, 1_000), + /// ::new(1_000_000, 10)); /// ``` fn div(self, rhs: Self) -> Self::Output { Self(self.0 / rhs.0) @@ -146,7 +149,7 @@ where /// assert_eq!(::checked_div(&Period::new(1000, 1), /// &Period::new(10, 1000)), Some(Period::new(1_000_000, 10))); /// - /// assert_eq!(::checked_div(&Period::new(1, i32::MAX), + /// assert_eq!(::checked_div(&Period::new(1, u32::MAX), /// &Period::new(2,1)), None); /// ``` fn checked_div(&self, v: &Self) -> Option { diff --git a/src/time_int.rs b/src/time_int.rs index 62e5ef3..6cdb089 100644 --- a/src/time_int.rs +++ b/src/time_int.rs @@ -1,34 +1,7 @@ -use crate::frequency::units::Hertz; -use crate::{duration::units::*, Period}; +use crate::Period; use core::{convert::TryFrom, convert::TryInto, fmt}; -/// Create time-based values from primitive and core numeric types. -/// -/// This trait can be imported with `use embedded-time::prelude::*`. -/// -/// # Examples -/// Basic construction of time-based values. -/// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!(5.nanoseconds(), Nanoseconds(5)); -/// assert_eq!(5.microseconds(), Microseconds(5)); -/// assert_eq!(5.milliseconds(), Milliseconds(5)); -/// assert_eq!(5.seconds(), Seconds(5)); -/// assert_eq!(5.minutes(), Minutes(5)); -/// assert_eq!(5.hours(), Hours(5)); -/// assert_eq!(5.hertz(), Hertz(5)); -/// ``` -/// -/// Signed integers work as well! -/// ```rust -/// # use embedded_time::{prelude::*, units::*}; -/// assert_eq!((-5).nanoseconds(), Nanoseconds(-5)); -/// assert_eq!((-5).microseconds(), Microseconds(-5)); -/// assert_eq!((-5).milliseconds(), Milliseconds(-5)); -/// assert_eq!((-5).seconds(), Seconds(-5)); -/// assert_eq!((-5).minutes(), Minutes(-5)); -/// assert_eq!((-5).hours(), Hours(-5)); -/// ``` +/// The core inner-type trait for time-related types pub trait TimeInt: Copy + num::Integer @@ -37,37 +10,26 @@ pub trait TimeInt: + num::traits::WrappingSub + num::CheckedMul + num::CheckedDiv - + From - + TryInto - + TryFrom + + From + + TryFrom + + TryInto + + TryFrom + TryInto - + Into + + Into + TryFrom + fmt::Display + fmt::Debug { - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn nanoseconds(self) -> Nanoseconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn microseconds(self) -> Microseconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn milliseconds(self) -> Milliseconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn seconds(self) -> Seconds; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn minutes(self) -> Minutes; - /// Construct the [`Duration`](crate::duration::Duration) implementation - fn hours(self) -> Hours; - - /// Construct the frequency type - fn hertz(self) -> Hertz; - + /// A checked multiplication with a [`Period`] + /// + /// # Examples + /// /// ```rust - /// # use embedded_time::{Period, prelude::*}; - /// assert_eq!(8_i32.checked_mul_period(&Period::new(1,2)), Some(4_i32)); + /// # use embedded_time::{Period, traits::*}; + /// assert_eq!(8_u32.checked_mul_period(&Period::new(1,2)), Some(4_u32)); /// /// // the result is not rounded, but truncated - /// assert_eq!(8_i32.checked_mul_period(&Period::new(1,3)), Some(2_i32)); + /// assert_eq!(8_u32.checked_mul_period(&Period::new(1,3)), Some(2_u32)); /// ``` fn checked_mul_period(&self, period: &Period) -> Option { Some(::checked_div( @@ -76,10 +38,14 @@ pub trait TimeInt: )?) } + /// A checked division with a [`Period`] + /// + /// # Examples + /// /// ```rust - /// # use embedded_time::{Period, prelude::*}; - /// assert_eq!(8_i32.checked_div_period(&Period::new(1,2)), Some(16_i32)); - /// assert_eq!(8_i32.checked_div_period(&Period::new(3,2)), Some(5_i32)); + /// # use embedded_time::{Period, traits::*}; + /// assert_eq!(8_u32.checked_div_period(&Period::new(1,2)), Some(16_u32)); + /// assert_eq!(8_u32.checked_div_period(&Period::new(3,2)), Some(5_u32)); /// ``` fn checked_div_period(&self, period: &Period) -> Option { Some(::checked_div( @@ -89,47 +55,6 @@ pub trait TimeInt: } } -macro_rules! impl_time_ints { - ($($type:ty),* $(,)?) => { - $( - impl TimeInt for $type { - #[inline(always)] - fn nanoseconds(self) -> Nanoseconds<$type> { - Nanoseconds(self) - } - - #[inline(always)] - fn microseconds(self) -> Microseconds<$type> { - Microseconds(self) - } - - #[inline(always)] - fn milliseconds(self) -> Milliseconds<$type> { - Milliseconds(self) - } - - #[inline(always)] - fn seconds(self) -> Seconds<$type> { - Seconds(self) - } - - #[inline(always)] - fn minutes(self) -> Minutes<$type> { - Minutes(self) - } - - #[inline(always)] - fn hours(self) -> Hours<$type> { - Hours(self) - } - - #[inline(always)] - fn hertz(self) -> Hertz<$type> { - Hertz(self) - } - } - )* - }; -} +impl TimeInt for u32 {} -impl_time_ints![i32, i64]; +impl TimeInt for u64 {} diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..47bc613 --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,319 @@ +use crate::{ + duration::Duration, duration::TryConvertFrom, timer::param::*, traits::*, units::*, Instant, +}; +use core::{convert::TryFrom, marker::PhantomData, ops::Add, prelude::v1::*}; + +pub(crate) mod param { + #[derive(Debug)] + pub struct None; + + #[derive(Debug)] + pub struct Armed; + + #[derive(Debug)] + pub struct Running; + + #[derive(Debug)] + pub struct Periodic; + + #[derive(Debug)] + pub struct OneShot; +} + +/// A `Timer` counts toward an expiration, can be polled for elapsed and remaining time, and can be +/// one-shot or continuous/periodic. +#[derive(Debug)] +pub struct Timer<'a, Type, State, Clock: crate::Clock, Dur: Duration> { + clock: &'a Clock, + duration: Dur, + expiration: Option>, + _type: PhantomData, + _state: PhantomData, +} + +impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'_, param::None, param::None, Clock, Dur> { + /// Construct a new, `OneShot` `Timer` + #[allow(clippy::new_ret_no_self)] + pub fn new(clock: &Clock, duration: Dur) -> Timer { + Timer:: { + clock, + duration, + expiration: Option::None, + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl<'a, Type, State, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, State, Clock, Dur> { + /// Change timer type to one-shot + pub fn into_oneshot(self) -> Timer<'a, OneShot, State, Clock, Dur> { + Timer:: { + clock: self.clock, + duration: self.duration, + expiration: self.expiration, + _type: PhantomData, + _state: PhantomData, + } + } + + /// Change timer type into periodic + pub fn into_periodic(self) -> Timer<'a, Periodic, State, Clock, Dur> { + Timer:: { + clock: self.clock, + duration: self.duration, + expiration: self.expiration, + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl<'a, Type, Clock: crate::Clock, Dur: Duration> Timer<'a, Type, Armed, Clock, Dur> { + /// Start the timer from this instant + pub fn start(self) -> Timer<'a, Type, Running, Clock, Dur> + where + Instant: Add>, + { + Timer:: { + clock: self.clock, + duration: self.duration, + expiration: Some(self.clock.now().unwrap() + self.duration), + _type: PhantomData, + _state: PhantomData, + } + } +} + +impl Timer<'_, Type, Running, Clock, Dur> { + fn _is_expired(&self) -> bool { + self.clock.now().unwrap() >= self.expiration.unwrap() + } + + /// Returns the [`Duration`] of time elapsed since it was started + /// + /// **The duration is truncated, not rounded**. + /// + /// The units of the [`Duration`] are the same as that used to construct the `Timer`. + pub fn elapsed(&self) -> Dur + where + Dur::Rep: TryFrom, + Clock::Rep: TryFrom, + { + self.clock + .now() + .unwrap() + .duration_since(&(self.expiration.unwrap() - self.duration)) + .unwrap() + } + + /// Returns the [`Duration`] until the expiration of the timer + /// + /// **The duration is truncated, not rounded**. + /// + /// The units of the [`Duration`] are the same as that used to construct the `Timer`. + pub fn remaining(&self) -> Dur + where + Dur::Rep: TryFrom, + Clock::Rep: TryFrom, + Dur: TryConvertFrom>, + { + if let Ok(duration) = self + .expiration + .unwrap() + .duration_since(&self.clock.now().unwrap()) + { + duration + } else { + 0.seconds().try_convert_into().unwrap() + } + } +} + +impl<'a, Clock: crate::Clock, Dur: Duration> Timer<'a, OneShot, Running, Clock, Dur> { + /// Block until the timer has expired + pub fn wait(self) -> Timer<'a, OneShot, Armed, Clock, Dur> { + // since the timer is running, _is_expired() will return a value + while !self._is_expired() {} + + Timer::::new(self.clock, self.duration) + } + + /// Check whether the timer has expired + /// + /// The timer is not restarted + pub fn is_expired(&self) -> bool { + self._is_expired() + } +} + +impl Timer<'_, Periodic, Running, Clock, Dur> { + /// Block until the timer has expired + /// + /// The timer is restarted + pub fn wait(self) -> Self + where + Instant: Add>, + { + // since the timer is running, _is_expired() will return a value + while !self._is_expired() {} + + Self { + clock: self.clock, + duration: self.duration, + expiration: self.expiration.map(|expiration| expiration + self.duration), + _type: PhantomData, + _state: PhantomData, + } + } + + /// Check whether a _periodic_ timer has elapsed + /// + /// The timer is restarted if it has elapsed. + pub fn period_complete(&mut self) -> bool + where + Instant: Add>, + { + // since the timer is running, _is_expired() will return a value + if self._is_expired() { + self.expiration = Some(self.expiration.unwrap() + self.duration); + + true + } else { + false + } + } +} + +#[cfg(test)] +#[allow(unused_imports)] +#[allow(unsafe_code)] +mod test { + use crate::{duration::Duration, traits::*, units::*, Error, Instant, Period}; + use core::convert::{Infallible, TryFrom}; + use crossbeam_utils::thread; + use std::sync::atomic::{AtomicU64, Ordering}; + + static TICKS: AtomicU64 = AtomicU64::new(0); + + #[derive(Debug)] + struct Clock; + impl crate::Clock for Clock { + type Rep = u64; + const PERIOD: Period = ::new(1, 1_000); + type ImplError = Infallible; + + fn now(&self) -> Result, crate::clock::Error> { + Ok(Instant::new(TICKS.load(Ordering::Acquire))) + } + } + + #[test] + fn oneshot_wait() { + init_ticks(); + let clock = Clock; + + let timer = clock.new_timer(1_u32.seconds()).start(); + + thread::scope(|s| { + let timer_handle = s.spawn(|_| timer.wait()); + + add_to_ticks(1_u32.seconds()); + + let result = timer_handle.join(); + + assert!(result.is_ok()); + + add_to_ticks(1_u32.seconds()); + + let timer = result.unwrap().start(); + assert!(!timer.is_expired()); + + let timer_handle = s.spawn(|_| timer.wait()); + add_to_ticks(1_u32.seconds()); + + assert!(timer_handle.join().is_ok()); + }) + .unwrap(); + } + + #[test] + fn periodic_wait() { + init_ticks(); + let clock = Clock; + + let timer = clock.new_timer(1_u32.seconds()).into_periodic().start(); + + thread::scope(|s| { + let timer_handle = s.spawn(|_| timer.wait()); + + add_to_ticks(1_u32.seconds()); + + let result = timer_handle.join(); + + assert!(result.is_ok()); + + let timer = result.unwrap(); + + // WHEN blocking on a timer + let timer_handle = s.spawn(|_| timer.wait()); + + add_to_ticks(1_u32.seconds()); + + assert!(timer_handle.join().is_ok()); + }) + .unwrap(); + } + + #[test] + fn periodic_expiration() { + init_ticks(); + let clock = Clock; + + let mut timer = clock.new_timer(1_u32.seconds()).into_periodic().start(); + + add_to_ticks(2_u32.seconds()); + + assert!(timer.period_complete()); + assert!(timer.period_complete()); + } + + #[test] + fn read_timer() { + init_ticks(); + let clock = Clock; + + let timer = clock.new_timer(2_u32.seconds()).start(); + + add_to_ticks(1_u32.milliseconds()); + + assert_eq!(timer.elapsed(), 0_u32.seconds()); + assert_eq!(timer.remaining(), 1_u32.seconds()); + + add_to_ticks(1_u32.seconds()); + + assert_eq!(timer.elapsed(), 1_u32.seconds()); + assert_eq!(timer.remaining(), 0_u32.seconds()); + + add_to_ticks(1_u32.seconds()); + + assert_eq!(timer.elapsed(), 2_u32.seconds()); + assert_eq!(timer.remaining(), 0_u32.seconds()); + + add_to_ticks(1_u32.seconds()); + + assert_eq!(timer.elapsed(), 3_u32.seconds()); + assert_eq!(timer.remaining(), 0_u32.seconds()); + } + + fn init_ticks() {} + + fn add_to_ticks(duration: Dur) { + let ticks = TICKS.load(Ordering::Acquire); + let ticks = ticks + + duration + .into_ticks::<::Rep>(Clock::PERIOD) + .unwrap(); + TICKS.store(ticks, Ordering::Release); + } +}