Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release/v0.13.0 #114

Open
wants to merge 24 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
524be16
Generic add/sub
korken89 May 23, 2021
1429b4e
Add/sub between 2 `Generics`
korken89 May 27, 2021
cfbb007
add support for defmt
newAM Aug 9, 2021
acd7891
Enforce minimum defmt v0.2.3
newAM Aug 13, 2021
5994105
skip defmt for non-embedded documentation tests
newAM Aug 15, 2021
3bac663
fix(CHANGELOG): typo
PTaylor-us Aug 28, 2021
c75c040
Merge pull request #90
PTaylor-us Aug 28, 2021
675e4ad
bump version
PTaylor-us Aug 28, 2021
2b4a66c
fix: update new PR with `integer()` return change
PTaylor-us Aug 28, 2021
103e876
fix: html_root_url version
PTaylor-us Aug 28, 2021
91c80c7
refactor(instant): remove unnecessary borrow
PTaylor-us Aug 28, 2021
d1657df
tests(duration): add Generic + Generic and Generic - Generic tests
PTaylor-us Aug 28, 2021
bbf1625
refactor(fraction): `numerator()` and `denominator()` return value ra…
PTaylor-us Aug 29, 2021
240a68b
refactor(duration, rate): bind FixedPoint to traits to simplify _gene…
PTaylor-us Aug 29, 2021
a1a8249
docs(fixed_point): expose Add/Sub impl docs
PTaylor-us Sep 12, 2021
03a99c4
Add scaling_factor method to FixedPoint
Sh3Rm4n Aug 20, 2021
927e0bf
docs(fixed_point): add short example to new `scaling_factor` method
PTaylor-us Sep 12, 2021
614f667
docs(changelog): add implemented changes/additions
PTaylor-us Sep 12, 2021
008ddfd
Merge branch 'release/v0.13.0' into defmt
PTaylor-us Sep 12, 2021
ee0c883
Merge pull request #110 from newAM/defmt
PTaylor-us Sep 12, 2021
3b270c9
Fraction: manual cmp & hash
burrbull Aug 15, 2021
c89639a
Merge pull request #111 from burrbull/fraction
PTaylor-us Oct 2, 2021
2ab2ec6
Fixed generic comparison where the destination integer is larger
korken89 Oct 8, 2021
e313ce9
Merge pull request #122 from korken89/fix-generic-extending-comparison
PTaylor-us Oct 16, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ jobs:
uses: actions-rs/cargo@v1
with:
command: test
args: --doc --all-features
args: --doc --features serde

- name: Clippy
uses: actions-rs/cargo@v1
Expand Down
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

## [Unreleased]

- added optional support for [defmt](https://github.com/knurling-rs/defmt)
- add `scaling_factor` method to `FixedPoint` type to retrieve the const scaling-factor [`Fraction`] from any fixed-point object
- Duration and Rate are no longer implemented by the `Generic` duration and rate types
- durations and rates can now be taken generically by a function without having to bind to `FixedPoint` (`FixedPoint` is now bound to the `Duration` and `Rate` traits)
- `Fraction`s `numerator()` and `denominator()` getter methods now return a value instead of a reference

[unreleased]: https://github.com/FluenTech/embedded-time/compare/v0.12.0...HEAD

## [0.12.0] - 2021-05-30
Expand All @@ -13,7 +19,7 @@
### Changed

- limit Rate and Duration extension to u32
- removed all use os `unwrap()`
- removed all use of `unwrap()`

[0.12.0]: https://github.com/FluenTech/embedded-time/compare/v0.11.0...v0.12.0

Expand Down
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "embedded-time"
version = "0.12.0"
version = "0.13.0"
authors = ["Peter Taylor <PTaylor@FluenTech.info>"]
edition = "2018"
description = "Fully defined, inter-operable, ergonomic, and fast human-time units (both duration and rate types) with hardware timer abstraction and software timers."
Expand All @@ -26,6 +26,7 @@ members = ["examples"]
[dependencies]
num = { version = "0.3.0", default-features = false }
serde = { version = "1.0.0", default-features = false, features = ["derive"], optional = true }
defmt = { version = "0.2.3", optional = true }

[dev-dependencies]
crossbeam-utils = "0.7.2"
Expand Down
14 changes: 8 additions & 6 deletions src/clock.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! Abstraction for hardware timers/clocks

use crate::{
duration::Duration, fixed_point::FixedPoint, fraction::Fraction, instant::Instant,
time_int::TimeInt, timer::param, timer::Timer,
duration::Duration, fraction::Fraction, instant::Instant, time_int::TimeInt, timer::param,
timer::Timer,
};
use core::hash::Hash;

/// Potential `Clock` errors
#[non_exhaustive]
#[derive(Debug, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum Error {
/// Exact cause of failure is unknown
Unspecified,
Expand All @@ -34,7 +35,11 @@ impl Default for Error {
/// software [`Timer`]s can be spawned from a `Clock` object.
pub trait Clock: Sized {
/// The type to hold the tick count
#[cfg(not(feature = "defmt"))]
type T: TimeInt + Hash;
/// The type to hold the tick count
#[cfg(feature = "defmt")]
type T: TimeInt + Hash + defmt::Format;

/// The duration of one clock tick in seconds, AKA the clock precision.
const SCALING_FACTOR: Fraction;
Expand All @@ -51,10 +56,7 @@ pub trait Clock: Sized {
fn new_timer<Dur: Duration>(
&self,
duration: Dur,
) -> Timer<param::OneShot, param::Armed, Self, Dur>
where
Dur: FixedPoint,
{
) -> Timer<param::OneShot, param::Armed, Self, Dur> {
Timer::<param::None, param::None, Self, Dur>::new(&self, duration)
}
}
161 changes: 146 additions & 15 deletions src/duration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use core::{
convert::TryFrom,
hash::{Hash, Hasher},
mem::size_of,
ops,
prelude::v1::*,
};
#[doc(hidden)]
Expand Down Expand Up @@ -290,7 +291,7 @@ pub use units::*;
///
/// assert_eq!(Minutes(62_u32) % Hours(1_u32), Minutes(2_u32));
/// ```
pub trait Duration: Sized + Copy {
pub trait Duration: FixedPoint + Sized + Copy {
/// Construct a `Generic` `Duration` from a _named_ `Duration` (eg.
/// [`Milliseconds`])
///
Expand Down Expand Up @@ -340,7 +341,6 @@ pub trait Duration: Sized + Copy {
scaling_factor: Fraction,
) -> Result<Generic<DestInt>, ConversionError>
where
Self: FixedPoint,
DestInt: TryFrom<Self::T>,
{
Ok(Generic::<DestInt>::new(
Expand Down Expand Up @@ -395,8 +395,6 @@ pub trait Duration: Sized + Copy {
/// ```
fn to_rate<Rate: rate::Rate>(&self) -> Result<Rate, ConversionError>
where
Rate: FixedPoint,
Self: FixedPoint,
Rate::T: TryFrom<Self::T>,
{
let conversion_factor = Self::SCALING_FACTOR
Expand All @@ -406,23 +404,23 @@ pub trait Duration: Sized + Copy {

if size_of::<Self::T>() >= size_of::<Rate::T>() {
fixed_point::FixedPoint::from_ticks(
Self::T::from(*conversion_factor.numerator())
Self::T::from(conversion_factor.numerator())
.checked_div(
&self
.integer()
.checked_mul(&Self::T::from(*conversion_factor.denominator()))
.checked_mul(&Self::T::from(conversion_factor.denominator()))
.ok_or(ConversionError::Overflow)?,
)
.ok_or(ConversionError::DivByZero)?,
Rate::SCALING_FACTOR,
)
} else {
fixed_point::FixedPoint::from_ticks(
Rate::T::from(*conversion_factor.numerator())
Rate::T::from(conversion_factor.numerator())
.checked_div(
&Rate::T::try_from(self.integer())
.map_err(|_| ConversionError::Overflow)?
.checked_mul(&Rate::T::from(*conversion_factor.denominator()))
.checked_mul(&Rate::T::from(conversion_factor.denominator()))
.ok_or(ConversionError::Overflow)?,
)
.ok_or(ConversionError::DivByZero)?,
Expand All @@ -438,6 +436,7 @@ pub trait Duration: Sized + Copy {
/// The purpose of this type is to allow a simple `Duration` object that can be defined at run-time.
/// It does this by replacing the `const` _scaling factor_ with a struct field.
#[derive(Copy, Clone, Debug, Default)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Generic<T> {
integer: T,
scaling_factor: Fraction,
Expand Down Expand Up @@ -501,7 +500,108 @@ impl<T: TimeInt> Generic<T> {
}
}

impl<T: TimeInt> Duration for Generic<T> {}
impl<T: TimeInt> Generic<T> {
pub(crate) fn into_ticks<T2>(self, fraction: Fraction) -> Result<T2, ConversionError>
where
T2: TimeInt,
T2: TryFrom<T>,
{
if size_of::<T2>() > size_of::<T>() {
let ticks =
T2::try_from(self.integer()).map_err(|_| ConversionError::ConversionFailure)?;

if fraction > Fraction::new(1, 1) {
TimeInt::checked_div_fraction(
&TimeInt::checked_mul_fraction(&ticks, &self.scaling_factor)
.ok_or(ConversionError::Unspecified)?,
&fraction,
)
.ok_or(ConversionError::Unspecified)
} else {
TimeInt::checked_mul_fraction(
&ticks,
&self
.scaling_factor
.checked_div(&fraction)
.ok_or(ConversionError::Unspecified)?,
)
.ok_or(ConversionError::Unspecified)
}
} else {
let ticks = if self.scaling_factor > Fraction::new(1, 1) {
TimeInt::checked_div_fraction(
&TimeInt::checked_mul_fraction(&self.integer(), &self.scaling_factor)
.ok_or(ConversionError::Unspecified)?,
&fraction,
)
.ok_or(ConversionError::Unspecified)?
} else {
TimeInt::checked_mul_fraction(
&self.integer(),
&self
.scaling_factor
.checked_div(&fraction)
.ok_or(ConversionError::Unspecified)?,
)
.ok_or(ConversionError::Unspecified)?
};

T2::try_from(ticks).map_err(|_| ConversionError::ConversionFailure)
}
}

/// Checked addition of two `Generic` durations.
pub fn checked_add_generic<T2: TimeInt>(mut self, duration: Generic<T2>) -> Option<Self>
where
T: TryFrom<T2>,
{
let add_ticks: T = duration.into_ticks(*self.scaling_factor()).ok()?;
self.integer = self.integer.checked_add(&add_ticks)?;

Some(self)
}

/// Checked subtraction of two `Generic` durations.
pub fn checked_sub_generic<T2: TimeInt>(mut self, duration: Generic<T2>) -> Option<Self>
where
T: TryFrom<T2>,
{
let sub_ticks: T = duration.into_ticks(*self.scaling_factor()).ok()?;
self.integer = self.integer.checked_sub(&sub_ticks)?;

Some(self)
}
}

impl<T: TimeInt, T2: TimeInt> ops::Add<Generic<T2>> for Generic<T>
where
T: TryFrom<T2>,
{
type Output = Self;

fn add(self, rhs: Generic<T2>) -> Self::Output {
if let Some(v) = self.checked_add_generic(rhs) {
v
} else {
panic!("Add failed")
}
}
}

impl<T: TimeInt, T2: TimeInt> ops::Sub<Generic<T2>> for Generic<T>
where
T: TryFrom<T2>,
{
type Output = Self;

fn sub(self, rhs: Generic<T2>) -> Self::Output {
if let Some(v) = self.checked_sub_generic(rhs) {
v
} else {
panic!("Sub failed")
}
}
}

/// Duration units
#[doc(hidden)]
Expand Down Expand Up @@ -560,9 +660,15 @@ pub mod units {
}
}

#[cfg(feature = "defmt")]
impl<T: TimeInt + defmt::Format> defmt::Format for $name<T> {
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "{}", self.0)
}
}

impl<T: TimeInt, Rhs: Duration> ops::Add<Rhs> for $name<T>
where
Rhs: FixedPoint,
Self: TryFrom<Rhs>,
{
type Output = Self;
Expand All @@ -576,7 +682,6 @@ pub mod units {
impl<T: TimeInt, Rhs: Duration> ops::Sub<Rhs> for $name<T>
where
Self: TryFrom<Rhs>,
Rhs: FixedPoint,
{
type Output = Self;

Expand Down Expand Up @@ -623,7 +728,6 @@ pub mod units {
impl<T: TimeInt, Rhs: Duration> ops::Rem<Rhs> for $name<T>
where
Self: TryFrom<Rhs>,
Rhs: FixedPoint,
{
type Output = Self;

Expand All @@ -650,11 +754,38 @@ pub mod units {
}
}

impl<T: TimeInt> From<$name<T>> for Generic<T> {
impl<T1, T2> From<$name<T1>> for Generic<T2>
where
T1: TimeInt,
T2: TimeInt + From<T1>,
{
/// See [Converting to a `Generic`
/// `Duration`](trait.Duration.html#converting-to-a-generic-duration)
fn from(duration: $name<T>) -> Self {
Self::new(duration.integer(), $name::<T>::SCALING_FACTOR)
fn from(duration: $name<T1>) -> Self {
Self::new(duration.integer().into(), $name::<T1>::SCALING_FACTOR)
}
}

impl<T1, T2> PartialEq<$name<T1>> for Generic<T2>
where
T1: TimeInt,
T2: TimeInt + From<T1>,
{
fn eq(&self, rhs: &$name<T1>) -> bool {
self.eq(rhs.into())
}
}

impl<T1, T2> PartialOrd<$name<T1>> for Generic<T2>
where
T1: TimeInt,
T2: TimeInt + From<T1>,
{
fn partial_cmp(&self, rhs: &$name<T1>) -> Option<core::cmp::Ordering> {
self.partial_cmp(&Self::new(
rhs.integer().into(),
$name::<T1>::SCALING_FACTOR,
))
}
}
};
Expand Down
13 changes: 11 additions & 2 deletions src/fixed_point.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,17 @@ pub trait FixedPoint: Sized + Copy {
/// ```
fn integer(&self) -> Self::T;

/// Returns the _scaling factor_ [`Fraction`] part
///
/// ```rust
/// # use embedded_time::{ rate::*};
/// #
/// assert_eq!(Kilohertz(45_u32).scaling_factor(), Fraction::new(1_000, 1));
/// ```
fn scaling_factor(&self) -> Fraction {
Self::SCALING_FACTOR
}

/// Constructs a `FixedPoint` value from _integer_ and _scaling-factor_ ([`Fraction`]) parts
///
/// # Errors
Expand Down Expand Up @@ -146,7 +157,6 @@ pub trait FixedPoint: Sized + Copy {
}

/// Panicky addition
#[doc(hidden)]
fn add<Rhs: FixedPoint>(self, rhs: Rhs) -> Self
where
Self: TryFrom<Rhs>,
Expand All @@ -160,7 +170,6 @@ pub trait FixedPoint: Sized + Copy {
}

/// Panicky subtraction
#[doc(hidden)]
fn sub<Rhs: FixedPoint>(self, rhs: Rhs) -> Self
where
Self: TryFrom<Rhs>,
Expand Down
Loading