feat: add display UI with splash screen, dashboard, and RSSI sparkline
Implement full SSD1306 OLED display support: - Splash screen on boot (1.5s) with project name, version, board - Dashboard showing radio state, config, counters, RSSI/SNR - RSSI history sparkline (64-sample ring buffer, 2px-wide bars) - DisplayOn/DisplayOff host commands routed via dedicated channel - Pure render functions against generic DrawTarget (no hardware coupling) - Display on/off control bypasses radio task entirely Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
17624 files changed
+4564928
-38
No files matched your search
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"files":{".cargo_vcs_info.json":"4cd5709c7ad83084386c773b6b9f606e4964cf222d981ef965cec2af67c3222a",".github/dependabot.yml":"6f8df103a7f29b06ee7a7443f7f9bd1d410ed1085d62e4815d39b5d936cbeaef",".github/workflows/build.yml":"33713112e7f03f07fbd074799bbcb80786fbdd354e3cb16a7b231c3204fdf10e",".github/workflows/changelog.yml":"d7ae186823f61513ff8094d08dd83e6f4fb1ef0721b51e7e6190393838aa4503","CHANGELOG.md":"2e08b11e0654f0f15426259c5fac37d1d17fbc58b717bf12b9f7493b9df3b190","Cargo.lock":"30c10f6569278e8c70a160b62c4572cfed3d7f67229d4dd31313c7f38600d4e6","Cargo.toml":"f18c1f68f40d4cefd8b06a60d55a6992c216292a705d8b372ac4e276c4dbeb1e","Cargo.toml.orig":"8db1549e57de76903cb9b04716cec5f3816b4408c17008022c50c898ec5cc07b","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"035e70219855119df4273b3c5b97543ae82e0dd60c520416e759107c602f651b","README.md":"61a397deb7dd584d760fd2e44984f90a71a1d30e961a4260cad436a90f4e942a","src/aliases.rs":"a885b9167ba44ad7937bfb785308fcf511aef9bfb8d267e788163ae1462e9f83","src/duration.rs":"e115e998bc9e2319c4e6bc0fb65ba00c5dae6e5b70626579b9e79e84b9ed5d45","src/helpers.rs":"80e9c9ddb207df24ca9f3b6465336506155a2e0563f96bb0573e3ba616fd5406","src/instant.rs":"afdea8af45f87b53d09d5735eb58bbe25650f0d02c6e1507583e28a80896093d","src/lib.rs":"1a7ed17b17122ca274b26051447579ff6def990578c16b8513511933cd5613e4","src/rate.rs":"112a7833139c6979d46b2711bc508a3ecb329063d60e8f065533a5698bf75b92"},"package":"4e639847d312d9a82d2e75b0edcc1e934efcc64e6cb7aa94f0b1fbec0bc231d6"}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"git": {
|
||||
"sha1": "ed53bcb806370c8d82a3bbfa893d69e743ef941b"
|
||||
},
|
||||
"path_in_vcs": ""
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
- package-ecosystem: cargo
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 10
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
name: Build
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- staging
|
||||
- trying
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
# Run cargo fmt --check, includes macros/
|
||||
style:
|
||||
name: style
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
components: rustfmt
|
||||
|
||||
- name: cargo fmt --check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: fmt
|
||||
args: --all -- --check
|
||||
|
||||
# Compilation check
|
||||
check:
|
||||
name: check
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
toolchain:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache cargo dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
- ~/.cargo/bin/
|
||||
- ~/.cargo/registry/index/
|
||||
- ~/.cargo/registry/cache/
|
||||
- ~/.cargo/git/db/
|
||||
key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
${{ runner.OS }}-cargo-
|
||||
|
||||
- name: Cache build output dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
|
||||
${{ runner.OS }}-build-
|
||||
|
||||
- name: Install Rust ${{ matrix.toolchain }} with target (${{ matrix.target }})
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
|
||||
- name: cargo check
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: false
|
||||
command: check
|
||||
args: --target=${{ matrix.target }}
|
||||
|
||||
tests:
|
||||
name: tests
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
target:
|
||||
- x86_64-unknown-linux-gnu
|
||||
toolchain:
|
||||
- stable
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Cache cargo dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
- ~/.cargo/bin/
|
||||
- ~/.cargo/registry/index/
|
||||
- ~/.cargo/registry/cache/
|
||||
- ~/.cargo/git/db/
|
||||
key: ${{ runner.OS }}-cargo-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-cargo-
|
||||
|
||||
- name: Cache build output dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: target
|
||||
key: ${{ runner.OS }}-build-${{ hashFiles('**/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.OS }}-build-
|
||||
|
||||
- name: Install Rust
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: ${{ matrix.toolchain }}
|
||||
target: ${{ matrix.target }}
|
||||
override: true
|
||||
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
use-cross: false
|
||||
command: test
|
||||
args: --target=${{ matrix.target }}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
# Check that the changelog is updated for all changes.
|
||||
#
|
||||
# This is only run for PRs.
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
# opened, reopened, synchronize are the default types for pull_request.
|
||||
# labeled, unlabeled ensure this check is also run if a label is added or removed.
|
||||
types: [opened, reopened, labeled, unlabeled, synchronize]
|
||||
|
||||
name: Changelog
|
||||
|
||||
jobs:
|
||||
changelog:
|
||||
name: Changelog
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout sources
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Check that changelog updated
|
||||
uses: dangoslen/changelog-enforcer@v3
|
||||
with:
|
||||
changeLogPath: CHANGELOG.md
|
||||
skipLabels: 'needs-changelog, skip-changelog'
|
||||
missingUpdateErrorMessage: 'Please add a changelog entry in the CHANGELOG.md file.'
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](http://keepachangelog.com/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
|
||||
### Changed
|
||||
|
||||
## [v0.3.9]
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
|
||||
- No default feature from `serde`
|
||||
|
||||
### Changed
|
||||
|
||||
## [v0.3.8] - YANKED
|
||||
|
||||
### Added
|
||||
|
||||
- Optional `serde` support
|
||||
- Optional `postcard` max size support
|
||||
|
||||
### Fixed
|
||||
|
||||
### Changed
|
||||
|
||||
## [v0.3.7]
|
||||
|
||||
### Added
|
||||
|
||||
- `Hz`, `kHz`, `MHz` shorthands for `Duration` and `nanos`, `micros`, `millis` for `Rate`
|
||||
- `from_rate`, `from_duration` in addition to `into_duration`, `into_rate`;
|
||||
- `const_try_from` in addition to `const_try_into`
|
||||
- `<unit>_at_least` for ceil-rounded `Duration` convert
|
||||
- Add `is_zero()` methods to `Duration<u32, NOM, DENOM>` and `Duration<u64, NOM, DENOM>` types to compare with zero without type conversion
|
||||
|
||||
### Fixed
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated documentation of `const_cmp`
|
||||
|
||||
## [v0.3.6]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed error in conversion between large durations.
|
||||
- Fixed `TimerRateU32` and `TimerRateU64` to use `Rate` instead of `Duration`
|
||||
|
||||
## [v0.3.5]
|
||||
|
||||
### Added
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed `to_X` rates.
|
||||
|
||||
### Changed
|
||||
|
||||
## [v0.3.4] -- YANKED
|
||||
|
||||
### Added
|
||||
|
||||
- Add `Div` implementation for `Duration` and `Rate`
|
||||
- Add `nanos()` methods and `NanosDuration` aliases alongside other units.
|
||||
- Implement AddAssign and SubAssign for Instant and Duration, and
|
||||
MulAssign and DivAssign for Duration.
|
||||
- Add `to_nanos()`, `to_micros()`, `to_millis()`, `to_secs()`, `to_minutes()`,
|
||||
and `to_hours()` methods to `Duration<u32, NOM, DENOM>` and
|
||||
`Duration<u64, NOM, DENOM>` types to easily convert to integer time units.
|
||||
- Support for `Rate` (Hertz, Kilohertz, ...)
|
||||
|
||||
### Fixed
|
||||
|
||||
### Changed
|
||||
|
||||
## [v0.3.3]
|
||||
|
||||
### Changed
|
||||
|
||||
- Underlying const gcd implementation switched to the `gcd` crate.
|
||||
- `Duration::convert` now `const`.
|
||||
|
||||
## [v0.3.2]
|
||||
|
||||
### Fixed
|
||||
|
||||
- `Duration::convert` did not do the right thing when getting close to maximum supported values.
|
||||
|
||||
## [v0.3.1]
|
||||
|
||||
### Added
|
||||
|
||||
- Added `CHANGELOG.md`
|
||||
|
||||
### Fixed
|
||||
|
||||
- Now supports a `defmt` version span (0.2 and 0.3 is supported)
|
||||
|
||||
[Unreleased]: https://github.com/korken89/fugit/compare/v0.3.9...HEAD
|
||||
[v0.3.9]: https://github.com/korken89/fugit/compare/v0.3.8...v0.3.9
|
||||
[v0.3.8]: https://github.com/korken89/fugit/compare/v0.3.7...v0.3.8
|
||||
[v0.3.7]: https://github.com/korken89/fugit/compare/v0.3.6...v0.3.7
|
||||
[v0.3.6]: https://github.com/korken89/fugit/compare/v0.3.5...v0.3.6
|
||||
[v0.3.5]: https://github.com/korken89/fugit/compare/v0.3.4...v0.3.5
|
||||
[v0.3.4]: https://github.com/korken89/fugit/compare/v0.3.3...v0.3.4
|
||||
[v0.3.3]: https://github.com/korken89/fugit/compare/v0.3.2...v0.3.3
|
||||
[v0.3.2]: https://github.com/korken89/fugit/compare/v0.3.1...v0.3.2
|
||||
kv0.3.1]: https://github.com/korken89/fugit/compare/v0.3.0...v0.3.1
|
||||
+294
@@ -0,0 +1,294 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "atomic-polyfill"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "0.3.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad"
|
||||
dependencies = [
|
||||
"defmt 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"defmt-macros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-macros"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e"
|
||||
dependencies = [
|
||||
"defmt-parser",
|
||||
"proc-macro-error2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "defmt-parser"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fugit"
|
||||
version = "0.3.9"
|
||||
dependencies = [
|
||||
"defmt 0.3.100",
|
||||
"gcd",
|
||||
"postcard",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gcd"
|
||||
version = "2.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a"
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"hash32",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"spin",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"heapless",
|
||||
"postcard-derive",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard-derive"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0232bd009a197ceec9cc881ba46f727fcd8060a2d8d6a9dde7a69030a6fe2bb"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error-attr2"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-error2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||
dependencies = [
|
||||
"proc-macro-error-attr2",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "semver"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.110"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
Vendored
+69
@@ -0,0 +1,69 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2021"
|
||||
name = "fugit"
|
||||
version = "0.3.9"
|
||||
authors = ["Emil Fresk <emil.fresk@gmail.com>"]
|
||||
build = false
|
||||
autolib = false
|
||||
autobins = false
|
||||
autoexamples = false
|
||||
autotests = false
|
||||
autobenches = false
|
||||
description = "Time library for embedded targets with ease-of-use and performance first."
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"Time",
|
||||
"Duration",
|
||||
"Instant",
|
||||
"const",
|
||||
]
|
||||
categories = [
|
||||
"date-and-time",
|
||||
"embedded",
|
||||
"no-std",
|
||||
]
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/korken89/fugit"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
postcard_max_size = [
|
||||
"dep:postcard",
|
||||
"serde",
|
||||
]
|
||||
serde = ["dep:serde"]
|
||||
|
||||
[lib]
|
||||
name = "fugit"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies.defmt]
|
||||
version = ">=0.2.0,<0.4"
|
||||
optional = true
|
||||
|
||||
[dependencies.gcd]
|
||||
version = ">=2.1,<3.0"
|
||||
|
||||
[dependencies.postcard]
|
||||
version = "1.1"
|
||||
features = ["experimental-derive"]
|
||||
optional = true
|
||||
|
||||
[dependencies.serde]
|
||||
version = "1"
|
||||
optional = true
|
||||
default-features = false
|
||||
Vendored
+201
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
Copyright (c) 2017 Jorge Aparicio
|
||||
|
||||
Permission is hereby granted, free of charge, to any
|
||||
person obtaining a copy of this software and associated
|
||||
documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without
|
||||
limitation the rights to use, copy, modify, merge,
|
||||
publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software
|
||||
is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice
|
||||
shall be included in all copies or substantial portions
|
||||
of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
||||
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
|
||||
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
|
||||
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
|
||||
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
Vendored
+19
@@ -0,0 +1,19 @@
|
||||
# `fugit`
|
||||
|
||||
> `fugit` provides a comprehensive library of `Duration` and `Instant` for the handling of time in embedded systems, doing all it can at compile time.
|
||||
|
||||
This library is a heavily inspired of `std::chrono`'s `Duration` from C++ which does all it can at compile time.
|
||||
|
||||
## Aims
|
||||
|
||||
* `no_std` library with goals of user-friendliness and performance first
|
||||
* All methods are `const fn` that can be (i.e. non-trait methods)
|
||||
* Use no traits, concrete types all the way for maximum `const`-ification
|
||||
* Operations are supported between different bases and backing storages instead of implementing custom traits
|
||||
* All constants needed for comparing or changing timebase are guaranteed compile time generated
|
||||
* Support for both `u32` and `u64` backing storage with efficient instruction lowering on MCUs
|
||||
* On Cortex-M3 and up: no soft-impls pulled in for both `u32` and `u64` except when changing base on `u64`
|
||||
* Comparisons on `u32` and `u64` do not use division, only changing base with all constants calculated at compile time
|
||||
* Selection of base happens at compile time
|
||||
* A common problem is that run time changing of base robs us of a lot of optimization opportunities, but since there are no traits and short-hands select the correct base at compile time.
|
||||
|
||||
Vendored
+117
@@ -0,0 +1,117 @@
|
||||
//! Type aliases for common uses
|
||||
|
||||
use crate::Duration;
|
||||
use crate::Instant;
|
||||
use crate::Rate;
|
||||
|
||||
/// Alias for nanosecond duration
|
||||
pub type NanosDuration<T> = Duration<T, 1, 1_000_000_000>;
|
||||
|
||||
/// Alias for nanosecond duration (`u32` backing storage)
|
||||
pub type NanosDurationU32 = Duration<u32, 1, 1_000_000_000>;
|
||||
|
||||
/// Alias for nanosecond duration (`u64` backing storage)
|
||||
pub type NanosDurationU64 = Duration<u64, 1, 1_000_000_000>;
|
||||
|
||||
/// Alias for microsecond duration
|
||||
pub type MicrosDuration<T> = Duration<T, 1, 1_000_000>;
|
||||
|
||||
/// Alias for microsecond duration (`u32` backing storage)
|
||||
pub type MicrosDurationU32 = Duration<u32, 1, 1_000_000>;
|
||||
|
||||
/// Alias for microsecond duration (`u64` backing storage)
|
||||
pub type MicrosDurationU64 = Duration<u64, 1, 1_000_000>;
|
||||
|
||||
/// Alias for millisecond duration
|
||||
pub type MillisDuration<T> = Duration<T, 1, 1_000>;
|
||||
|
||||
/// Alias for millisecond duration (`u32` backing storage)
|
||||
pub type MillisDurationU32 = Duration<u32, 1, 1_000>;
|
||||
|
||||
/// Alias for millisecond duration (`u64` backing storage)
|
||||
pub type MillisDurationU64 = Duration<u64, 1, 1_000>;
|
||||
|
||||
/// Alias for second duration
|
||||
pub type SecsDuration<T> = Duration<T, 1, 1>;
|
||||
|
||||
/// Alias for second duration (`u32` backing storage)
|
||||
pub type SecsDurationU32 = Duration<u32, 1, 1>;
|
||||
|
||||
/// Alias for second duration (`u64` backing storage)
|
||||
pub type SecsDurationU64 = Duration<u64, 1, 1>;
|
||||
|
||||
/// Alias for minutes duration
|
||||
pub type MinutesDuration<T> = Duration<T, 60, 1>;
|
||||
|
||||
/// Alias for minutes duration (`u32` backing storage)
|
||||
pub type MinutesDurationU32 = Duration<u32, 60, 1>;
|
||||
|
||||
/// Alias for minutes duration (`u64` backing storage)
|
||||
pub type MinutesDurationU64 = Duration<u64, 60, 1>;
|
||||
|
||||
/// Alias for hours duration
|
||||
pub type HoursDuration<T> = Duration<T, 3_600, 1>;
|
||||
|
||||
/// Alias for hours duration (`u32` backing storage)
|
||||
pub type HoursDurationU32 = Duration<u32, 3_600, 1>;
|
||||
|
||||
/// Alias for hours duration (`u64` backing storage)
|
||||
pub type HoursDurationU64 = Duration<u64, 3_600, 1>;
|
||||
|
||||
/// Alias for durations that come from timers with a specific frequency
|
||||
pub type TimerDuration<T, const FREQ_HZ: u32> = Duration<T, 1, FREQ_HZ>;
|
||||
|
||||
/// Alias for durations that come from timers with a specific frequency (`u32` backing storage)
|
||||
pub type TimerDurationU32<const FREQ_HZ: u32> = Duration<u32, 1, FREQ_HZ>;
|
||||
|
||||
/// Alias for durations that come from timers with a specific frequency (`u64` backing storage)
|
||||
pub type TimerDurationU64<const FREQ_HZ: u32> = Duration<u64, 1, FREQ_HZ>;
|
||||
|
||||
// -------------------------------
|
||||
|
||||
/// Alias for instants that come from timers with a specific frequency
|
||||
pub type TimerInstant<T, const FREQ_HZ: u32> = Instant<T, 1, FREQ_HZ>;
|
||||
|
||||
/// Alias for instants that come from timers with a specific frequency (`u32` backing storage)
|
||||
pub type TimerInstantU32<const FREQ_HZ: u32> = Instant<u32, 1, FREQ_HZ>;
|
||||
|
||||
/// Alias for instants that come from timers with a specific frequency (`u64` backing storage)
|
||||
pub type TimerInstantU64<const FREQ_HZ: u32> = Instant<u64, 1, FREQ_HZ>;
|
||||
|
||||
// -------------------------------
|
||||
|
||||
/// Alias for hertz rate
|
||||
pub type Hertz<T> = Rate<T, 1, 1>;
|
||||
|
||||
/// Alias for hertz rate (`u32` backing storage)
|
||||
pub type HertzU32 = Rate<u32, 1, 1>;
|
||||
|
||||
/// Alias for hertz rate (`u64` backing storage)
|
||||
pub type HertzU64 = Rate<u64, 1, 1>;
|
||||
|
||||
/// Alias for kilohertz rate
|
||||
pub type Kilohertz<T> = Rate<T, 1_000, 1>;
|
||||
|
||||
/// Alias for kilohertz rate (`u32` backing storage)
|
||||
pub type KilohertzU32 = Rate<u32, 1_000, 1>;
|
||||
|
||||
/// Alias for kilohertz rate (`u64` backing storage)
|
||||
pub type KilohertzU64 = Rate<u64, 1_000, 1>;
|
||||
|
||||
/// Alias for megahertz rate
|
||||
pub type Megahertz<T> = Rate<T, 1_000_000, 1>;
|
||||
|
||||
/// Alias for megahertz rate (`u32` backing storage)
|
||||
pub type MegahertzU32 = Rate<u32, 1_000_000, 1>;
|
||||
|
||||
/// Alias for megahertz rate (`u64` backing storage)
|
||||
pub type MegahertzU64 = Rate<u64, 1_000_000, 1>;
|
||||
|
||||
/// Alias for rate that come from timers with a specific frequency
|
||||
pub type TimerRate<T, const FREQ_HZ: u32> = Rate<T, FREQ_HZ, 1>;
|
||||
|
||||
/// Alias for rate that come from timers with a specific frequency (`u32` backing storage)
|
||||
pub type TimerRateU32<const FREQ_HZ: u32> = Rate<u32, FREQ_HZ, 1>;
|
||||
|
||||
/// Alias for rate that come from timers with a specific frequency (`u64` backing storage)
|
||||
pub type TimerRateU64<const FREQ_HZ: u32> = Rate<u64, FREQ_HZ, 1>;
|
||||
Vendored
+965
@@ -0,0 +1,965 @@
|
||||
use crate::helpers::{self, Helpers};
|
||||
use crate::Rate;
|
||||
use core::cmp::Ordering;
|
||||
use core::convert;
|
||||
use core::ops;
|
||||
|
||||
/// Represents a duration of time.
|
||||
///
|
||||
/// The generic `T` can either be `u32` or `u64`, and the const generics represent the ratio of the
|
||||
/// ticks contained within the duration: `duration in seconds = NOM / DENOM * ticks`
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(
|
||||
feature = "postcard_max_size",
|
||||
derive(postcard::experimental::max_size::MaxSize)
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Duration<T, const NOM: u32, const DENOM: u32> {
|
||||
pub(crate) ticks: T,
|
||||
}
|
||||
|
||||
macro_rules! shorthand {
|
||||
($i:ty, $nom:literal, $denum:literal, $unit:ident, $to_unit:ident, $unital:ident, $unitstr:literal) => {
|
||||
#[doc = concat!("Convert the Duration to an integer number of ", $unitstr, ".")]
|
||||
#[inline]
|
||||
pub const fn $to_unit(&self) -> $i {
|
||||
(Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i * self.ticks)
|
||||
/ Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i
|
||||
}
|
||||
|
||||
#[doc = concat!("Shorthand for creating a duration which represents ", $unitstr, ".")]
|
||||
#[inline]
|
||||
pub const fn $unit(val: $i) -> Self {
|
||||
Self::from_ticks(
|
||||
(Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i * val)
|
||||
/ Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i
|
||||
)
|
||||
}
|
||||
|
||||
#[doc = concat!("Shorthand for creating a duration which represents ", $unitstr, " (ceil rounded).")]
|
||||
#[inline]
|
||||
pub const fn $unital(val: $i) -> Self {
|
||||
let mul = Helpers::<$nom, $denum, NOM, DENOM>::RD_TIMES_LN as $i * val;
|
||||
let ld_times_rn = Helpers::<$nom, $denum, NOM, DENOM>::LD_TIMES_RN as $i;
|
||||
Self::from_ticks(if mul % ld_times_rn == 0 {
|
||||
mul / ld_times_rn
|
||||
} else {
|
||||
mul / ld_times_rn + 1
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
macro_rules! impl_duration_for_integer {
|
||||
($i:ty) => {
|
||||
impl<const NOM: u32, const DENOM: u32> Duration<$i, NOM, DENOM> {
|
||||
/// Create a `Duration` from a ticks value.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let _d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn from_ticks(ticks: $i) -> Self {
|
||||
helpers::greater_than_0::<NOM>();
|
||||
helpers::greater_than_0::<DENOM>();
|
||||
|
||||
Duration { ticks }
|
||||
}
|
||||
|
||||
/// Extract the ticks from a `Duration`.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
|
||||
///
|
||||
/// assert_eq!(d.ticks(), 234);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn ticks(&self) -> $i {
|
||||
self.ticks
|
||||
}
|
||||
|
||||
/// Returns true if this `Duration` spans no time
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let zero = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(0);")]
|
||||
#[doc = concat!("let one = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(zero.is_zero(), true);
|
||||
/// assert_eq!(one.is_zero(), false);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn is_zero(&self) -> bool {
|
||||
self.ticks == 0
|
||||
}
|
||||
|
||||
/// Add two durations while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
#[doc = concat!("let d3 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
|
||||
///
|
||||
/// assert_eq!(d1.checked_add(d2).unwrap().ticks(), 3);
|
||||
/// assert_eq!(d1.checked_add(d3), None);
|
||||
/// ```
|
||||
pub const fn checked_add<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
if let Some(ticks) = self.ticks.checked_add(other.ticks) {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
if let Some(ticks) = self.ticks.checked_add(ticks) {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract two durations while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
#[doc = concat!("let d3 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i), "::MAX);")]
|
||||
///
|
||||
/// assert_eq!(d2.checked_sub(d1).unwrap().ticks(), 1);
|
||||
/// assert_eq!(d1.checked_sub(d3), None);
|
||||
/// ```
|
||||
pub const fn checked_sub<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
if let Some(ticks) = self.ticks.checked_sub(other.ticks) {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
if let Some(ticks) = self.ticks.checked_sub(ticks) {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(ticks))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = concat!("Const `cmp` for ", stringify!($i))]
|
||||
#[inline(always)]
|
||||
const fn _const_cmp(a: $i, b: $i) -> Ordering {
|
||||
if a < b {
|
||||
Ordering::Less
|
||||
} else if a > b {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
/// Const partial comparison.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(d1.const_partial_cmp(d2), Some(core::cmp::Ordering::Greater));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_partial_cmp<const R_NOM: u32, const R_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, R_NOM, R_DENOM>
|
||||
) -> Option<Ordering> {
|
||||
//
|
||||
// We want to check:
|
||||
//
|
||||
// n_lh / d_lh * lh_ticks {cmp} n_rh / d_rh * rh_ticks
|
||||
//
|
||||
// simplify to
|
||||
//
|
||||
// n_lh * d_rh * lh_ticks {cmp} n_rh * d_lh * rh_ticks
|
||||
//
|
||||
// find gdc(n_lh * d_rh, n_rh * d_lh) and use that to make the constants minimal (done
|
||||
// with the `helpers::Helpers` struct)
|
||||
//
|
||||
// then perform the comparison in a comparable basis
|
||||
//
|
||||
|
||||
if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
|
||||
// If we are in the same base, comparison in trivial
|
||||
Some(Self::_const_cmp(self.ticks, other.ticks))
|
||||
} else {
|
||||
let lh = self
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
|
||||
let rh = other
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
|
||||
|
||||
if let (Some(lh), Some(rh)) = (lh, rh) {
|
||||
Some(Self::_const_cmp(lh, rh))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const equality check.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(10);")]
|
||||
///
|
||||
/// assert!(d1.const_eq(d2));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_eq<const R_NOM: u32, const R_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, R_NOM, R_DENOM>
|
||||
) -> bool {
|
||||
if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
|
||||
// If we are in the same base, comparison in trivial
|
||||
self.ticks == other.ticks
|
||||
} else {
|
||||
let lh = self
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
|
||||
let rh = other
|
||||
.ticks
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
|
||||
|
||||
if let (Some(lh), Some(rh)) = (lh, rh) {
|
||||
lh == rh
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try from, checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2 = Duration::<", stringify!($i), ", 1, 1_000>::const_try_from(d1);")]
|
||||
///
|
||||
/// assert_eq!(d2.unwrap().ticks(), 10);
|
||||
/// ```
|
||||
pub const fn const_try_from<const I_NOM: u32, const I_DENOM: u32>(
|
||||
duration: Duration<$i, I_NOM, I_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<I_NOM, I_DENOM, NOM, DENOM>::SAME_BASE {
|
||||
Some(Self::from_ticks(duration.ticks))
|
||||
} else {
|
||||
if let Some(lh) = (duration.ticks as u64)
|
||||
.checked_mul(Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RD_TIMES_LN as u64)
|
||||
{
|
||||
let ticks = lh / Helpers::<I_NOM, I_DENOM, NOM, DENOM>::LD_TIMES_RN as u64;
|
||||
|
||||
if ticks <= <$i>::MAX as u64 {
|
||||
Some(Self::from_ticks(ticks as $i))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try into, checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_00>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2: Option<Duration::<", stringify!($i), ", 1, 1_000>> = d1.const_try_into();")]
|
||||
///
|
||||
/// assert_eq!(d2.unwrap().ticks(), 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_try_into<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Option<Duration<$i, O_NOM, O_DENOM>> {
|
||||
Duration::<$i, O_NOM, O_DENOM>::const_try_from(self)
|
||||
}
|
||||
|
||||
/// Const try into rate, checking for divide-by-zero.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
#[doc = concat!("let r1: Option<Rate::<", stringify!($i), ", 1, 1>> = d1.try_into_rate();")]
|
||||
///
|
||||
/// assert_eq!(r1.unwrap().raw(), 500);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn try_into_rate<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Option<Rate<$i, O_NOM, O_DENOM>> {
|
||||
Rate::<$i, O_NOM, O_DENOM>::try_from_duration(self)
|
||||
}
|
||||
|
||||
/// Convert from duration to rate.
|
||||
#[inline]
|
||||
pub const fn into_rate<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Rate<$i, O_NOM, O_DENOM> {
|
||||
if let Some(v) = self.try_into_rate() {
|
||||
v
|
||||
} else {
|
||||
panic!("Into rate failed, divide-by-zero!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try from rate, checking for divide-by-zero.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1>::from_raw(1);")]
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::try_from_rate(r1);")]
|
||||
///
|
||||
/// assert_eq!(d1.unwrap().ticks(), 1_000);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn try_from_rate<const I_NOM: u32, const I_DENOM: u32>(
|
||||
rate: Rate<$i, I_NOM, I_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if rate.raw > 0 {
|
||||
Some(Self::from_ticks(
|
||||
Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RATE_TO_DURATION_NUMERATOR as $i
|
||||
/ rate.raw
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from rate to duration.
|
||||
#[inline]
|
||||
pub const fn from_rate<const I_NOM: u32, const I_DENOM: u32>(
|
||||
rate: Rate<$i, I_NOM, I_DENOM>,
|
||||
) -> Self {
|
||||
if let Some(v) = Self::try_from_rate(rate) {
|
||||
v
|
||||
} else {
|
||||
panic!("From rate failed, divide-by-zero!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert between bases for a duration.
|
||||
///
|
||||
/// Unfortunately not a `From` impl due to collision with the std lib.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 100>::from_ticks(1);")]
|
||||
#[doc = concat!("let d2: Duration::<", stringify!($i), ", 1, 1_000> = d1.convert();")]
|
||||
///
|
||||
/// assert_eq!(d2.ticks(), 10);
|
||||
/// ```
|
||||
/// Can be used in const contexts. Compilation will fail if the conversion causes overflow
|
||||
/// ```compile_fail
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("const TICKS: ", stringify!($i), "= ", stringify!($i), "::MAX - 10;")]
|
||||
#[doc = concat!("const D1: Duration::<", stringify!($i), ", 1, 100> = Duration::<", stringify!($i), ", 1, 100>::from_ticks(TICKS);")]
|
||||
/// // Fails conversion due to tick overflow
|
||||
#[doc = concat!("const D2: Duration::<", stringify!($i), ", 1, 200> = D1.convert();")]
|
||||
#[inline]
|
||||
pub const fn convert<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Duration<$i, O_NOM, O_DENOM> {
|
||||
if let Some(v) = self.const_try_into() {
|
||||
v
|
||||
} else {
|
||||
panic!("Convert failed!");
|
||||
}
|
||||
}
|
||||
|
||||
shorthand!($i, 1, 1_000_000_000, nanos, to_nanos, nanos_at_least, "nanoseconds");
|
||||
shorthand!($i, 1, 1_000_000, micros, to_micros, micros_at_least, "microseconds");
|
||||
shorthand!($i, 1, 1_000, millis, to_millis, millis_at_least, "milliseconds");
|
||||
shorthand!($i, 1, 1, secs, to_secs, secs_at_least, "seconds");
|
||||
shorthand!($i, 60, 1, minutes, to_minutes, minutes_at_least, "minutes");
|
||||
shorthand!($i, 3600, 1, hours, to_hours, hours_at_least, "hours");
|
||||
|
||||
/// Shorthand for creating a duration which represents hertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn Hz(val: $i) -> Self {
|
||||
Self::from_rate(crate::Hertz::<$i>::from_raw(val))
|
||||
}
|
||||
|
||||
/// Shorthand for creating a duration which represents kilohertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn kHz(val: $i) -> Self {
|
||||
Self::from_rate(crate::Kilohertz::<$i>::from_raw(val))
|
||||
}
|
||||
|
||||
/// Shorthand for creating a duration which represents megahertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn MHz(val: $i) -> Self {
|
||||
Self::from_rate(crate::Megahertz::<$i>::from_raw(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Duration<$i, R_NOM, R_DENOM>> for Duration<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Duration<$i, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
self.const_partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Ord for Duration<$i, NOM, DENOM> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
Self::_const_cmp(self.ticks, other.ticks)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Duration<$i, R_NOM, R_DENOM>> for Duration<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Duration<$i, R_NOM, R_DENOM>) -> bool {
|
||||
self.const_eq(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Eq for Duration<$i, NOM, DENOM> {}
|
||||
|
||||
// Duration - Duration = Duration (only same base until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<$i, NOM, DENOM>>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Duration -= Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<$i, NOM, DENOM>>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Self) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Duration + Duration = Duration (only same base until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<$i, NOM, DENOM>>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Duration += Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<$i, NOM, DENOM>>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
// integer * Duration = Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Mul<Duration<$i, NOM, DENOM>> for u32 {
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, mut other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
other.ticks *= self as $i;
|
||||
other
|
||||
}
|
||||
}
|
||||
|
||||
// Duration * integer = Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Mul<u32> for Duration<$i, NOM, DENOM> {
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn mul(mut self, other: u32) -> Self::Output {
|
||||
self.ticks *= other as $i;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Duration *= integer
|
||||
impl<const NOM: u32, const DENOM: u32> ops::MulAssign<u32>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: u32) {
|
||||
*self = *self * other;
|
||||
}
|
||||
}
|
||||
|
||||
// Duration / integer = Duration
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Div<u32> for Duration<$i, NOM, DENOM> {
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn div(mut self, other: u32) -> Self::Output {
|
||||
self.ticks /= other as $i;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Duration /= integer
|
||||
impl<const NOM: u32, const DENOM: u32> ops::DivAssign<u32>
|
||||
for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: u32) {
|
||||
*self = *self / other;
|
||||
}
|
||||
}
|
||||
|
||||
// Duration / Duration = integer
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32> ops::Div<Duration<$i, R_NOM, R_DENOM>>
|
||||
for Duration<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
type Output = $i;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Duration<$i, R_NOM, R_DENOM>) -> Self::Output {
|
||||
let conv: Duration<$i, R_NOM, R_DENOM> = self.convert();
|
||||
conv.ticks / other.ticks
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl<const NOM: u32, const DENOM: u32> defmt::Format for Duration<$i, NOM, DENOM>
|
||||
{
|
||||
fn format(&self, f: defmt::Formatter) {
|
||||
if NOM == 3_600 && DENOM == 1 {
|
||||
defmt::write!(f, "{} h", self.ticks)
|
||||
} else if NOM == 60 && DENOM == 1 {
|
||||
defmt::write!(f, "{} min", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1 {
|
||||
defmt::write!(f, "{} s", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000 {
|
||||
defmt::write!(f, "{} ms", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000 {
|
||||
defmt::write!(f, "{} us", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000_000 {
|
||||
defmt::write!(f, "{} ns", self.ticks)
|
||||
} else {
|
||||
defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> core::fmt::Display for Duration<$i, NOM, DENOM> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
if NOM == 3_600 && DENOM == 1 {
|
||||
write!(f, "{} h", self.ticks)
|
||||
} else if NOM == 60 && DENOM == 1 {
|
||||
write!(f, "{} min", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1 {
|
||||
write!(f, "{} s", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000 {
|
||||
write!(f, "{} ms", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000 {
|
||||
write!(f, "{} us", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000_000 {
|
||||
write!(f, "{} ns", self.ticks)
|
||||
} else {
|
||||
write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_duration_for_integer!(u32);
|
||||
impl_duration_for_integer!(u64);
|
||||
|
||||
//
|
||||
// Operations between u32 and u64 Durations
|
||||
//
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> From<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn from(val: Duration<u32, NOM, DENOM>) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::from_ticks(val.ticks() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> convert::TryFrom<Duration<u64, NOM, DENOM>>
|
||||
for Duration<u32, NOM, DENOM>
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(val: Duration<u64, NOM, DENOM>) -> Result<Duration<u32, NOM, DENOM>, ()> {
|
||||
Ok(Duration::<u32, NOM, DENOM>::from_ticks(
|
||||
val.ticks().try_into().map_err(|_| ())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Duration - Duration = Duration (to make shorthands work, until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) =
|
||||
self.checked_sub(Duration::<u64, NOM, DENOM>::from_ticks(other.ticks() as u64))
|
||||
{
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Duration -= Duration (to make shorthands work, until const_generics_defaults is stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Duration + Duration = Duration (to make shorthands work, until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) =
|
||||
self.checked_add(Duration::<u64, NOM, DENOM>::from_ticks(other.ticks() as u64))
|
||||
{
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Duration += Duration (to make shorthands work, until const_generics_defaults is stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<u32, NOM, DENOM>>
|
||||
for Duration<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Duration<u32, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
self.partial_cmp(&Duration::<u64, R_NOM, R_DENOM>::from_ticks(
|
||||
other.ticks() as u64
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Duration<u32, R_NOM, R_DENOM>) -> bool {
|
||||
self.eq(&Duration::<u64, R_NOM, R_DENOM>::from_ticks(
|
||||
other.ticks() as u64
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Duration<u64, R_NOM, R_DENOM>> for Duration<u32, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Duration<u64, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
Duration::<u64, L_NOM, L_DENOM>::from_ticks(self.ticks as u64).partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Duration<u64, R_NOM, R_DENOM>> for Duration<u32, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Duration<u64, R_NOM, R_DENOM>) -> bool {
|
||||
Duration::<u64, L_NOM, L_DENOM>::from_ticks(self.ticks as u64).eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u32 Durations
|
||||
pub trait ExtU32 {
|
||||
/// Shorthand for creating a duration which represents nanoseconds.
|
||||
fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents microseconds.
|
||||
fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents milliseconds.
|
||||
fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents seconds.
|
||||
fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents minutes.
|
||||
fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents hours.
|
||||
fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU32 for u32 {
|
||||
#[inline]
|
||||
fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::nanos(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::micros(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::millis(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::secs(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::minutes(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::hours(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u32 Durations (ceil rounded)
|
||||
pub trait ExtU32Ceil {
|
||||
/// Shorthand for creating a duration which represents nanoseconds.
|
||||
fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents microseconds.
|
||||
fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents milliseconds.
|
||||
fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents seconds.
|
||||
fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents minutes.
|
||||
fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents hours.
|
||||
fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU32Ceil for u32 {
|
||||
#[inline]
|
||||
fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::nanos_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::micros_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::millis_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::secs_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::minutes_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u32, NOM, DENOM> {
|
||||
Duration::<u32, NOM, DENOM>::hours_at_least(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u64 Durations
|
||||
pub trait ExtU64 {
|
||||
/// Shorthand for creating a duration which represents nanoseconds.
|
||||
fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents microseconds.
|
||||
fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents milliseconds.
|
||||
fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents seconds.
|
||||
fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents minutes.
|
||||
fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents hours.
|
||||
fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU64 for u64 {
|
||||
#[inline]
|
||||
fn nanos<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::nanos(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn micros<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::micros(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn millis<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::millis(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn secs<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::secs(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn minutes<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::minutes(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hours<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::hours(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u64 Durations (ceil rounded)
|
||||
pub trait ExtU64Ceil {
|
||||
/// Shorthand for creating a duration which represents nanoseconds.
|
||||
fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents microseconds.
|
||||
fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents milliseconds.
|
||||
fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents seconds.
|
||||
fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents minutes.
|
||||
fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a duration which represents hours.
|
||||
fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU64Ceil for u64 {
|
||||
#[inline]
|
||||
fn nanos_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::nanos_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn micros_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::micros_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn millis_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::millis_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn secs_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::secs_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn minutes_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::minutes_at_least(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn hours_at_least<const NOM: u32, const DENOM: u32>(self) -> Duration<u64, NOM, DENOM> {
|
||||
Duration::<u64, NOM, DENOM>::hours_at_least(self)
|
||||
}
|
||||
}
|
||||
Vendored
+80
@@ -0,0 +1,80 @@
|
||||
/// Needed due to not being allowed to call const-fn in `PartialEq` fo some reasion
|
||||
/// get the error:
|
||||
///
|
||||
/// ```console
|
||||
/// error[E0401]: can't use generic parameters from outer function
|
||||
/// --> src/main.rs:25:47
|
||||
/// |
|
||||
/// 21 | impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
/// | ------- const parameter from outer function
|
||||
/// ...
|
||||
/// 25 | const TEST: u32 = gcd_binary_u32(L_DENOM, R_DENOM);
|
||||
/// | ^^^^^^^ use of generic parameter from outer function
|
||||
///
|
||||
/// For more information about this error, try `rustc --explain E0401`
|
||||
/// ```
|
||||
pub struct Helpers<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>;
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
Helpers<L_NOM, L_DENOM, R_NOM, R_DENOM>
|
||||
{
|
||||
/// Helper constants generated at compile time
|
||||
pub const DIVISOR: u64 =
|
||||
gcd::binary_u64(L_DENOM as u64 * R_NOM as u64, R_DENOM as u64 * L_NOM as u64);
|
||||
|
||||
/// Helper constants generated at compile time
|
||||
pub const DIVISOR_2: u64 =
|
||||
gcd::binary_u64(L_NOM as u64 * R_NOM as u64, R_DENOM as u64 * L_DENOM as u64);
|
||||
|
||||
/// Helper constants generated at compile time for Durations
|
||||
pub const RD_TIMES_LN: u64 = (R_DENOM as u64 * L_NOM as u64) / Self::DIVISOR;
|
||||
|
||||
/// Helper constants generated at compile time
|
||||
pub const LD_TIMES_RN: u64 = (L_DENOM as u64 * R_NOM as u64) / Self::DIVISOR;
|
||||
|
||||
/// Helper constants generated at compile time for Rates
|
||||
pub const LN_TIMES_RN: u64 = (L_NOM as u64 * R_NOM as u64) / Self::DIVISOR_2;
|
||||
|
||||
/// Helper constants generated at compile time for Rates
|
||||
pub const RD_TIMES_LD: u64 = (R_DENOM as u64 * L_DENOM as u64) / Self::DIVISOR_2;
|
||||
|
||||
/// Helper constants generated at compile time for Rates
|
||||
pub const RATE_TO_DURATION_NUMERATOR: u64 = Self::RD_TIMES_LD / Self::LN_TIMES_RN;
|
||||
|
||||
/// Helper constants generated at compile time
|
||||
pub const SAME_BASE: bool = Self::LD_TIMES_RN == Self::RD_TIMES_LN;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[allow(path_statements)]
|
||||
pub(crate) const fn greater_than_0<const N: u32>() {
|
||||
Assert::<N, 0>::GREATER;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// Const assert hack
|
||||
pub struct Assert<const L: u32, const R: u32>;
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl<const L: u32, const R: u32> Assert<L, R> {
|
||||
/// Const assert hack
|
||||
pub const GREATER_EQ: () = assert!(L >= R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const LESS_EQ: () = assert!(L <= R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const NOT_EQ: () = assert!(L != R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const EQ: () = assert!(L == R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const GREATER: () = assert!(L > R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const LESS: () = assert!(L < R);
|
||||
|
||||
/// Const assert hack
|
||||
pub const POWER_OF_TWO: () = assert!(L.is_power_of_two());
|
||||
}
|
||||
Vendored
+442
@@ -0,0 +1,442 @@
|
||||
use crate::duration::Duration;
|
||||
use crate::helpers::{self, Helpers};
|
||||
use core::cmp::Ordering;
|
||||
use core::ops;
|
||||
|
||||
/// Represents an instant in time.
|
||||
///
|
||||
/// The generic `T` can either be `u32` or `u64`, and the const generics represent the ratio of the
|
||||
/// ticks contained within the instant: `instant in seconds = NOM / DENOM * ticks`
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(
|
||||
feature = "postcard_max_size",
|
||||
derive(postcard::experimental::max_size::MaxSize)
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Instant<T, const NOM: u32, const DENOM: u32> {
|
||||
ticks: T,
|
||||
}
|
||||
|
||||
macro_rules! impl_instant_for_integer {
|
||||
($i:ty) => {
|
||||
impl<const NOM: u32, const DENOM: u32> Instant<$i, NOM, DENOM> {
|
||||
/// Create an `Instant` from a ticks value.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let _i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn from_ticks(ticks: $i) -> Self {
|
||||
helpers::greater_than_0::<NOM>();
|
||||
helpers::greater_than_0::<DENOM>();
|
||||
|
||||
Instant { ticks }
|
||||
}
|
||||
|
||||
/// Extract the ticks from an `Instant`.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(234);")]
|
||||
///
|
||||
/// assert_eq!(i.ticks(), 234);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn ticks(&self) -> $i {
|
||||
self.ticks
|
||||
}
|
||||
|
||||
/// Const comparison of `Instant`s.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
///
|
||||
/// assert_eq!(i1.const_cmp(i2), core::cmp::Ordering::Less);
|
||||
/// ```
|
||||
///
|
||||
/// This function takes into account that ticks might wrap around. If the absolute
|
||||
/// values of `self` and `other` differ by more than half the possible range, it is
|
||||
/// assumed that an overflow occured and the result is reversed:
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(", stringify!($i),"::MAX);")]
|
||||
#[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(i1.const_cmp(i2), core::cmp::Ordering::Less);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_cmp(self, other: Self) -> Ordering {
|
||||
if self.ticks == other.ticks {
|
||||
Ordering::Equal
|
||||
} else {
|
||||
let v = self.ticks.wrapping_sub(other.ticks);
|
||||
|
||||
// not using `v.cmp(<$i>::MAX / 2).reverse()` due to `cmp` being non-const
|
||||
if v > <$i>::MAX / 2 {
|
||||
Ordering::Less
|
||||
} else if v < <$i>::MAX / 2 {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Duration between since the start of the `Instant`. This assumes an instant which
|
||||
/// won't wrap within the execution of the program.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(11);")]
|
||||
///
|
||||
/// assert_eq!(i.duration_since_epoch().ticks(), 11);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn duration_since_epoch(self) -> Duration<$i, NOM, DENOM> {
|
||||
Duration::<$i, NOM, DENOM>::from_ticks(self.ticks())
|
||||
}
|
||||
|
||||
/// Duration between `Instant`s.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i1 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let i2 = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
///
|
||||
/// assert_eq!(i1.checked_duration_since(i2), None);
|
||||
/// assert_eq!(i2.checked_duration_since(i1).unwrap().ticks(), 1);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn checked_duration_since(
|
||||
self,
|
||||
other: Self,
|
||||
) -> Option<Duration<$i, NOM, DENOM>> {
|
||||
match self.const_cmp(other) {
|
||||
Ordering::Greater | Ordering::Equal => {
|
||||
Some(Duration::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_sub(other.ticks),
|
||||
))
|
||||
}
|
||||
Ordering::Less => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract a `Duration` from an `Instant` while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(i.checked_sub_duration(d).unwrap().ticks(), 0);
|
||||
/// ```
|
||||
pub const fn checked_sub_duration<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
Some(Instant::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_sub(other.ticks()),
|
||||
))
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.ticks()
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
Some(Instant::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_sub(ticks),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a `Duration` to an `Instant` while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let i = Instant::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
#[doc = concat!("let d = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(1);")]
|
||||
///
|
||||
/// assert_eq!(i.checked_add_duration(d).unwrap().ticks(), 2);
|
||||
/// ```
|
||||
pub const fn checked_add_duration<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Duration<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
Some(Instant::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_add(other.ticks()),
|
||||
))
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.ticks()
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let ticks = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
Some(Instant::<$i, NOM, DENOM>::from_ticks(
|
||||
self.ticks.wrapping_add(ticks),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> PartialOrd for Instant<$i, NOM, DENOM> {
|
||||
/// This implementation deviates from the definition of
|
||||
/// [PartialOrd::partial_cmp](core::cmp::PartialOrd::partial_cmp):
|
||||
///
|
||||
/// It takes into account that ticks might wrap around. If the absolute
|
||||
/// values of `self` and `other` differ by more than half the possible range, it is
|
||||
/// assumed that an overflow occured and the result is reversed.
|
||||
///
|
||||
/// That breaks the transitivity invariant: a < b and b < c no longer implies a < c.
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
||||
Some(self.const_cmp(*other))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Ord for Instant<$i, NOM, DENOM> {
|
||||
/// This implementation deviates from the definition of
|
||||
/// [Ord::cmp](core::cmp::Ord::cmp):
|
||||
///
|
||||
/// It takes into account that ticks might wrap around. If the absolute
|
||||
/// values of `self` and `other` differ by more than half the possible range, it is
|
||||
/// assumed that an overflow occured and the result is reversed.
|
||||
///
|
||||
/// That breaks the transitivity invariant: a < b and b < c no longer implies a < c.
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
self.const_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> PartialEq for Instant<$i, NOM, DENOM> {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.ticks.eq(&other.ticks)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Eq for Instant<$i, NOM, DENOM> {}
|
||||
|
||||
// Instant - Instant = Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_duration_since`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Instant<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Duration<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Self) -> Self::Output {
|
||||
if let Some(v) = self.checked_duration_since(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed! Other > self");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant - Duration = Instant
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_sub_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Instant<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub_duration(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed! Overflow");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant -= Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_sub_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Instant + Duration = Instant
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_add_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Instant<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Duration<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add_duration(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed! Overflow");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant += Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_add_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<$i, NOM, DENOM>>
|
||||
for Instant<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Duration<$i, NOM, DENOM>) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl<const NOM: u32, const DENOM: u32> defmt::Format for Instant<$i, NOM, DENOM> {
|
||||
fn format(&self, f: defmt::Formatter) {
|
||||
if NOM == 3_600 && DENOM == 1 {
|
||||
defmt::write!(f, "{} h", self.ticks)
|
||||
} else if NOM == 60 && DENOM == 1 {
|
||||
defmt::write!(f, "{} min", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1 {
|
||||
defmt::write!(f, "{} s", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000 {
|
||||
defmt::write!(f, "{} ms", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000 {
|
||||
defmt::write!(f, "{} us", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000_000 {
|
||||
defmt::write!(f, "{} ns", self.ticks)
|
||||
} else {
|
||||
defmt::write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> core::fmt::Display for Instant<$i, NOM, DENOM> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
if NOM == 3_600 && DENOM == 1 {
|
||||
write!(f, "{} h", self.ticks)
|
||||
} else if NOM == 60 && DENOM == 1 {
|
||||
write!(f, "{} min", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1 {
|
||||
write!(f, "{} s", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000 {
|
||||
write!(f, "{} ms", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000 {
|
||||
write!(f, "{} us", self.ticks)
|
||||
} else if NOM == 1 && DENOM == 1_000_000_000 {
|
||||
write!(f, "{} ns", self.ticks)
|
||||
} else {
|
||||
write!(f, "{} ticks @ ({}/{})", self.ticks, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_instant_for_integer!(u32);
|
||||
impl_instant_for_integer!(u64);
|
||||
|
||||
//
|
||||
// Operations between u32 Duration and u64 Instant
|
||||
//
|
||||
|
||||
// Instant - Duration = Instant
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_sub_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Duration<u32, NOM, DENOM>>
|
||||
for Instant<u64, NOM, DENOM>
|
||||
{
|
||||
type Output = Instant<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub_duration(other.into()) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed! Overflow");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant -= Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_sub_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Duration<u32, NOM, DENOM>>
|
||||
for Instant<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Instant + Duration = Instant
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_add_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Duration<u32, NOM, DENOM>>
|
||||
for Instant<u64, NOM, DENOM>
|
||||
{
|
||||
type Output = Instant<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Duration<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add_duration(other.into()) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed! Overflow");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Instant += Duration
|
||||
// We have limited this to use same numerator and denominator in both left and right hand sides,
|
||||
// this allows for the extension traits to work. For usage with different fraction, use
|
||||
// `checked_add_duration`.
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Duration<u32, NOM, DENOM>>
|
||||
for Instant<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Duration<u32, NOM, DENOM>) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
// impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
// ops::Add<Duration<u32, R_NOM, R_DENOM>> for Duration<u64, L_NOM, L_DENOM>
|
||||
// {
|
||||
// type Output = Duration<u64, L_NOM, L_DENOM>;
|
||||
//
|
||||
// #[inline]
|
||||
// fn add(self, other: Duration<u32, R_NOM, R_DENOM>) -> Self::Output {
|
||||
// self.add(Duration::<u64, L_NOM, L_DENOM>::from_ticks(
|
||||
// other.ticks() as u64
|
||||
// ))
|
||||
// }
|
||||
// }
|
||||
Vendored
+1348
File diff suppressed because it is too large.
Load diff
Vendored
+773
@@ -0,0 +1,773 @@
|
||||
use crate::helpers::{self, Helpers};
|
||||
use crate::Duration;
|
||||
use core::cmp::Ordering;
|
||||
use core::convert;
|
||||
use core::ops;
|
||||
|
||||
/// Represents a frequency.
|
||||
///
|
||||
/// The generic `T` can either be `u32` or `u64`, and the const generics represent the ratio of the
|
||||
/// raw contained within the rate: `rate in Hz = NOM / DENOM * raw`
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
#[cfg_attr(
|
||||
feature = "postcard_max_size",
|
||||
derive(postcard::experimental::max_size::MaxSize)
|
||||
)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Rate<T, const NOM: u32, const DENOM: u32> {
|
||||
pub(crate) raw: T,
|
||||
}
|
||||
|
||||
macro_rules! impl_rate_for_integer {
|
||||
($i:ty) => {
|
||||
impl<const NOM: u32, const DENOM: u32> Rate<$i, NOM, DENOM> {
|
||||
/// Create a `Rate` from a raw value.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let _d = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(1);")]
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn from_raw(raw: $i) -> Self {
|
||||
helpers::greater_than_0::<NOM>();
|
||||
helpers::greater_than_0::<DENOM>();
|
||||
|
||||
Rate { raw }
|
||||
}
|
||||
|
||||
/// Extract the raw value from a `Rate`.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(234);")]
|
||||
///
|
||||
/// assert_eq!(d.raw(), 234);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn raw(&self) -> $i {
|
||||
self.raw
|
||||
}
|
||||
|
||||
/// Add two rates while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(2);")]
|
||||
#[doc = concat!("let r3 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(", stringify!($i), "::MAX);")]
|
||||
///
|
||||
/// assert_eq!(r1.checked_add(r2).unwrap().raw(), 3);
|
||||
/// assert_eq!(r1.checked_add(r3), None);
|
||||
/// ```
|
||||
pub const fn checked_add<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Rate<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
if let Some(raw) = self.raw.checked_add(other.raw) {
|
||||
Some(Rate::<$i, NOM, DENOM>::from_raw(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let raw = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
if let Some(raw) = self.raw.checked_add(raw) {
|
||||
Some(Rate::<$i, NOM, DENOM>::from_raw(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Subtract two rates while checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(2);")]
|
||||
#[doc = concat!("let r3 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(", stringify!($i), "::MAX);")]
|
||||
///
|
||||
/// assert_eq!(r2.checked_sub(r1).unwrap().raw(), 1);
|
||||
/// assert_eq!(r1.checked_sub(r3), None);
|
||||
/// ```
|
||||
pub const fn checked_sub<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
other: Rate<$i, O_NOM, O_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<NOM, DENOM, O_NOM, O_DENOM>::SAME_BASE {
|
||||
if let Some(raw) = self.raw.checked_sub(other.raw) {
|
||||
Some(Rate::<$i, NOM, DENOM>::from_raw(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
if let Some(lh) = other
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, O_NOM, O_DENOM>::LD_TIMES_RN as $i)
|
||||
{
|
||||
let raw = lh / Helpers::<NOM, DENOM, O_NOM, O_DENOM>::RD_TIMES_LN as $i;
|
||||
|
||||
if let Some(raw) = self.raw.checked_sub(raw) {
|
||||
Some(Rate::<$i, NOM, DENOM>::from_raw(raw))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc = concat!("Const `cmp` for ", stringify!($i))]
|
||||
#[inline(always)]
|
||||
const fn _const_cmp(a: $i, b: $i) -> Ordering {
|
||||
if a < b {
|
||||
Ordering::Less
|
||||
} else if a > b {
|
||||
Ordering::Greater
|
||||
} else {
|
||||
Ordering::Equal
|
||||
}
|
||||
}
|
||||
|
||||
/// Const partial comparison.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_00>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(1);")]
|
||||
///
|
||||
/// assert_eq!(r1.const_partial_cmp(r2), Some(core::cmp::Ordering::Greater));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_partial_cmp<const R_NOM: u32, const R_DENOM: u32>(
|
||||
self,
|
||||
other: Rate<$i, R_NOM, R_DENOM>
|
||||
) -> Option<Ordering> {
|
||||
if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
|
||||
// If we are in the same base, comparison in trivial
|
||||
Some(Self::_const_cmp(self.raw, other.raw))
|
||||
} else {
|
||||
let lh = self
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
|
||||
let rh = other
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
|
||||
|
||||
if let (Some(lh), Some(rh)) = (lh, rh) {
|
||||
Some(Self::_const_cmp(lh, rh))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const equality check.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_00>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::from_raw(10);")]
|
||||
///
|
||||
/// assert!(r1.const_eq(r2));
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_eq<const R_NOM: u32, const R_DENOM: u32>(
|
||||
self,
|
||||
other: Rate<$i, R_NOM, R_DENOM>
|
||||
) -> bool {
|
||||
if Helpers::<NOM, DENOM, R_NOM, R_DENOM>::SAME_BASE {
|
||||
// If we are in the same base, comparison in trivial
|
||||
self.raw == other.raw
|
||||
} else {
|
||||
let lh = self
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::RD_TIMES_LN as $i);
|
||||
let rh = other
|
||||
.raw
|
||||
.checked_mul(Helpers::<NOM, DENOM, R_NOM, R_DENOM>::LD_TIMES_RN as $i);
|
||||
|
||||
if let (Some(lh), Some(rh)) = (lh, rh) {
|
||||
lh == rh
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try from, checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_00>::from_raw(1);")]
|
||||
#[doc = concat!("let r2 = Rate::<", stringify!($i), ", 1, 1_000>::const_try_from(r1);")]
|
||||
///
|
||||
/// assert_eq!(r2.unwrap().raw(), 10);
|
||||
/// ```
|
||||
pub const fn const_try_from<const I_NOM: u32, const I_DENOM: u32>(
|
||||
rate: Rate<$i, I_NOM, I_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if Helpers::<I_NOM, I_DENOM, NOM, DENOM>::SAME_BASE {
|
||||
Some(Self::from_raw(rate.raw))
|
||||
} else {
|
||||
if let Some(lh) = (rate.raw as u64)
|
||||
.checked_mul(Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RD_TIMES_LN)
|
||||
{
|
||||
let raw = lh / Helpers::<I_NOM, I_DENOM, NOM, DENOM>::LD_TIMES_RN;
|
||||
|
||||
if raw <= <$i>::MAX as u64 {
|
||||
Some(Self::from_raw(raw as $i))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try into, checking for overflow.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1_00>::from_raw(1);")]
|
||||
#[doc = concat!("let r2: Option<Rate::<", stringify!($i), ", 1, 1_000>> = r1.const_try_into();")]
|
||||
///
|
||||
/// assert_eq!(r2.unwrap().raw(), 10);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn const_try_into<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Option<Rate<$i, O_NOM, O_DENOM>> {
|
||||
Rate::<$i, O_NOM, O_DENOM>::const_try_from(self)
|
||||
}
|
||||
|
||||
/// Const try into duration, checking for divide-by-zero.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1>::from_raw(1);")]
|
||||
#[doc = concat!("let d1: Option<Duration::<", stringify!($i), ", 1, 1_000>> = r1.try_into_duration();")]
|
||||
///
|
||||
/// assert_eq!(d1.unwrap().ticks(), 1_000);
|
||||
/// ```
|
||||
pub const fn try_into_duration<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Option<Duration<$i, O_NOM, O_DENOM>> {
|
||||
Duration::<$i, O_NOM, O_DENOM>::try_from_rate(self)
|
||||
}
|
||||
|
||||
/// Convert from rate to duration.
|
||||
pub const fn into_duration<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Duration<$i, O_NOM, O_DENOM> {
|
||||
if let Some(v) = self.try_into_duration() {
|
||||
v
|
||||
} else {
|
||||
panic!("Into duration failed, divide-by-zero!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Const try from duration, checking for divide-by-zero.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let d1 = Duration::<", stringify!($i), ", 1, 1_000>::from_ticks(2);")]
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 1>::try_from_duration(d1);")]
|
||||
///
|
||||
/// assert_eq!(r1.unwrap().raw(), 500);
|
||||
/// ```
|
||||
#[inline]
|
||||
pub const fn try_from_duration<const I_NOM: u32, const I_DENOM: u32>(
|
||||
duration: Duration<$i, I_NOM, I_DENOM>,
|
||||
) -> Option<Self> {
|
||||
if duration.ticks > 0 {
|
||||
Some(Self::from_raw(
|
||||
Helpers::<I_NOM, I_DENOM, NOM, DENOM>::RATE_TO_DURATION_NUMERATOR as $i
|
||||
/ duration.ticks
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from duration to rate.
|
||||
#[inline]
|
||||
pub const fn from_duration<const I_NOM: u32, const I_DENOM: u32>(
|
||||
duration: Duration<$i, I_NOM, I_DENOM>,
|
||||
) -> Self {
|
||||
if let Some(v) = Self::try_from_duration(duration) {
|
||||
v
|
||||
} else {
|
||||
panic!("From duration failed, divide-by-zero!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert between bases for a rate.
|
||||
///
|
||||
/// Unfortunately not a `From` impl due to collision with the std lib.
|
||||
///
|
||||
/// ```
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("let r1 = Rate::<", stringify!($i), ", 1, 100>::from_raw(1);")]
|
||||
#[doc = concat!("let r2: Rate::<", stringify!($i), ", 1, 1_000> = r1.convert();")]
|
||||
///
|
||||
/// assert_eq!(r2.raw(), 10);
|
||||
/// ```
|
||||
///
|
||||
/// Can be used in const contexts. Compilation will fail if the conversion causes overflow
|
||||
///
|
||||
/// ```compile_fail
|
||||
/// # use fugit::*;
|
||||
#[doc = concat!("const RAW: ", stringify!($i), "= ", stringify!($i), "::MAX - 10;")]
|
||||
#[doc = concat!("const R1: Rate::<", stringify!($i), ", 1, 100> = Rate::<", stringify!($i), ", 1, 100>::from_raw(RAW);")]
|
||||
/// // Fails conversion due to overflow
|
||||
#[doc = concat!("const R2: Rate::<", stringify!($i), ", 1, 200> = R1.convert();")]
|
||||
/// ```
|
||||
pub const fn convert<const O_NOM: u32, const O_DENOM: u32>(
|
||||
self,
|
||||
) -> Rate<$i, O_NOM, O_DENOM> {
|
||||
if let Some(v) = self.const_try_into() {
|
||||
v
|
||||
} else {
|
||||
panic!("Convert failed!");
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the Rate to an interger number of Hz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn to_Hz(&self) -> $i {
|
||||
(Helpers::<1, 1, NOM, DENOM>::LD_TIMES_RN as $i * self.raw)
|
||||
/ Helpers::<1, 1, NOM, DENOM>::RD_TIMES_LN as $i
|
||||
}
|
||||
|
||||
/// Convert the Rate to an interger number of kHz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn to_kHz(&self) -> $i {
|
||||
(Helpers::<1_000, 1, NOM, DENOM>::LD_TIMES_RN as $i * self.raw)
|
||||
/ Helpers::<1_000, 1, NOM, DENOM>::RD_TIMES_LN as $i
|
||||
}
|
||||
|
||||
/// Convert the Rate to an interger number of MHz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn to_MHz(&self) -> $i {
|
||||
(Helpers::<1_000_000, 1, NOM, DENOM>::LD_TIMES_RN as $i * self.raw)
|
||||
/ Helpers::<1_000_000, 1, NOM, DENOM>::RD_TIMES_LN as $i
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents hertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn Hz(val: $i) -> Self {
|
||||
Self::from_raw(
|
||||
(Helpers::<1, 1, NOM, DENOM>::RD_TIMES_LN as $i * val)
|
||||
/ Helpers::<1, 1, NOM, DENOM>::LD_TIMES_RN as $i,
|
||||
)
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents kilohertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn kHz(val: $i) -> Self {
|
||||
Self::from_raw(
|
||||
(Helpers::<1_000, 1, NOM, DENOM>::RD_TIMES_LN as $i * val)
|
||||
/ Helpers::<1_000, 1, NOM, DENOM>::LD_TIMES_RN as $i,
|
||||
)
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents megahertz.
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
pub const fn MHz(val: $i) -> Self {
|
||||
Self::from_raw(
|
||||
(Helpers::<1_000_000, 1, NOM, DENOM>::RD_TIMES_LN as $i * val)
|
||||
/ Helpers::<1_000_000, 1, NOM, DENOM>::LD_TIMES_RN as $i,
|
||||
)
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents nanoseconds.
|
||||
#[inline]
|
||||
pub const fn nanos(val: $i) -> Self {
|
||||
Self::from_duration(crate::Duration::<$i, 1, 1_000_000_000>::from_ticks(val))
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents microseconds.
|
||||
#[inline]
|
||||
pub const fn micros(val: $i) -> Self {
|
||||
Self::from_duration(crate::Duration::<$i, 1, 1_000_000>::from_ticks(val))
|
||||
}
|
||||
|
||||
/// Shorthand for creating a rate which represents milliseconds.
|
||||
#[inline]
|
||||
pub const fn millis(val: $i) -> Self {
|
||||
Self::from_duration(crate::Duration::<$i, 1, 1_000>::from_ticks(val))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Rate<$i, R_NOM, R_DENOM>> for Rate<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Rate<$i, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
self.const_partial_cmp(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Ord for Rate<$i, NOM, DENOM> {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> Ordering {
|
||||
Self::_const_cmp(self.raw, other.raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Rate<$i, R_NOM, R_DENOM>> for Rate<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Rate<$i, R_NOM, R_DENOM>) -> bool {
|
||||
self.const_eq(*other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> Eq for Rate<$i, NOM, DENOM> {}
|
||||
|
||||
// Rate - Rate = Rate (only same base until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Rate<$i, NOM, DENOM>>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Rate<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rate + Rate = Rate (only same base until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Rate<$i, NOM, DENOM>>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Rate<$i, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add(other) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rate += Rate
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Rate<$i, NOM, DENOM>>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
// integer * Rate = Rate
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Mul<Rate<$i, NOM, DENOM>> for u32 {
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn mul(self, mut other: Rate<$i, NOM, DENOM>) -> Self::Output {
|
||||
other.raw *= self as $i;
|
||||
other
|
||||
}
|
||||
}
|
||||
|
||||
// Rate * integer = Rate
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Mul<u32> for Rate<$i, NOM, DENOM> {
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn mul(mut self, other: u32) -> Self::Output {
|
||||
self.raw *= other as $i;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Rate *= integer
|
||||
impl<const NOM: u32, const DENOM: u32> ops::MulAssign<u32>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn mul_assign(&mut self, other: u32) {
|
||||
*self = *self * other;
|
||||
}
|
||||
}
|
||||
|
||||
// Rate / integer = Rate
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Div<u32> for Rate<$i, NOM, DENOM> {
|
||||
type Output = Rate<$i, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn div(mut self, other: u32) -> Self::Output {
|
||||
self.raw /= other as $i;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
// Rate / Rate = integer
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32> ops::Div<Rate<$i, R_NOM, R_DENOM>>
|
||||
for Rate<$i, L_NOM, L_DENOM>
|
||||
{
|
||||
type Output = $i;
|
||||
|
||||
#[inline]
|
||||
fn div(self, other: Rate<$i, R_NOM, R_DENOM>) -> Self::Output {
|
||||
let conv: Rate<$i, R_NOM, R_DENOM> = self.convert();
|
||||
conv.raw / other.raw
|
||||
}
|
||||
}
|
||||
|
||||
// Rate /= integer
|
||||
impl<const NOM: u32, const DENOM: u32> ops::DivAssign<u32>
|
||||
for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn div_assign(&mut self, other: u32) {
|
||||
*self = *self / other;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "defmt")]
|
||||
impl<const NOM: u32, const DENOM: u32> defmt::Format for Rate<$i, NOM, DENOM>
|
||||
{
|
||||
fn format(&self, f: defmt::Formatter) {
|
||||
if NOM == 1 && DENOM == 1 {
|
||||
defmt::write!(f, "{} Hz", self.raw)
|
||||
} else if NOM == 1_000 && DENOM == 1 {
|
||||
defmt::write!(f, "{} kHz", self.raw)
|
||||
} else if NOM == 1_000_000 && DENOM == 1 {
|
||||
defmt::write!(f, "{} MHz", self.raw)
|
||||
} else if NOM == 1_000_000_000 && DENOM == 1 {
|
||||
defmt::write!(f, "{} GHz", self.raw)
|
||||
} else {
|
||||
defmt::write!(f, "{} raw @ ({}/{})", self.raw, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> core::fmt::Display for Rate<$i, NOM, DENOM> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
if NOM == 1 && DENOM == 1 {
|
||||
write!(f, "{} Hz", self.raw)
|
||||
} else if NOM == 1_000 && DENOM == 1 {
|
||||
write!(f, "{} kHz", self.raw)
|
||||
} else if NOM == 1_000_000 && DENOM == 1 {
|
||||
write!(f, "{} MHz", self.raw)
|
||||
} else if NOM == 1_000_000_000 && DENOM == 1 {
|
||||
write!(f, "{} GHz", self.raw)
|
||||
} else {
|
||||
write!(f, "{} raw @ ({}/{})", self.raw, NOM, DENOM)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_rate_for_integer!(u32);
|
||||
impl_rate_for_integer!(u64);
|
||||
|
||||
//
|
||||
// Operations between u32 and u64 Rate
|
||||
//
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> From<Rate<u32, NOM, DENOM>> for Rate<u64, NOM, DENOM> {
|
||||
#[inline]
|
||||
fn from(val: Rate<u32, NOM, DENOM>) -> Rate<u64, NOM, DENOM> {
|
||||
Rate::<u64, NOM, DENOM>::from_raw(val.raw() as u64)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const NOM: u32, const DENOM: u32> convert::TryFrom<Rate<u64, NOM, DENOM>>
|
||||
for Rate<u32, NOM, DENOM>
|
||||
{
|
||||
type Error = ();
|
||||
|
||||
#[inline]
|
||||
fn try_from(val: Rate<u64, NOM, DENOM>) -> Result<Rate<u32, NOM, DENOM>, ()> {
|
||||
Ok(Rate::<u32, NOM, DENOM>::from_raw(
|
||||
val.raw().try_into().map_err(|_| ())?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// Rate - Rate = Rate (to make shorthands work, until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Sub<Rate<u32, NOM, DENOM>> for Rate<u64, NOM, DENOM> {
|
||||
type Output = Rate<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn sub(self, other: Rate<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_sub(Rate::<u64, NOM, DENOM>::from_raw(other.raw() as u64)) {
|
||||
v
|
||||
} else {
|
||||
panic!("Sub failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rate -= Rate (to make shorthands work, until const_generics_defaults is stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::SubAssign<Rate<u32, NOM, DENOM>>
|
||||
for Rate<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn sub_assign(&mut self, other: Rate<u32, NOM, DENOM>) {
|
||||
*self = *self - other;
|
||||
}
|
||||
}
|
||||
|
||||
// Rate + Rate = Rate (to make shorthands work, until const_generics_defaults is
|
||||
// stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::Add<Rate<u32, NOM, DENOM>> for Rate<u64, NOM, DENOM> {
|
||||
type Output = Rate<u64, NOM, DENOM>;
|
||||
|
||||
#[inline]
|
||||
fn add(self, other: Rate<u32, NOM, DENOM>) -> Self::Output {
|
||||
if let Some(v) = self.checked_add(Rate::<u64, NOM, DENOM>::from_raw(other.raw() as u64)) {
|
||||
v
|
||||
} else {
|
||||
panic!("Add failed!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rate += Rate (to make shorthands work, until const_generics_defaults is stabilized)
|
||||
impl<const NOM: u32, const DENOM: u32> ops::AddAssign<Rate<u32, NOM, DENOM>>
|
||||
for Rate<u64, NOM, DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn add_assign(&mut self, other: Rate<u32, NOM, DENOM>) {
|
||||
*self = *self + other;
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Rate<u32, R_NOM, R_DENOM>> for Rate<u64, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Rate<u32, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
self.partial_cmp(&Rate::<u64, R_NOM, R_DENOM>::from_raw(other.raw() as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Rate<u32, R_NOM, R_DENOM>> for Rate<u64, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Rate<u32, R_NOM, R_DENOM>) -> bool {
|
||||
self.eq(&Rate::<u64, R_NOM, R_DENOM>::from_raw(other.raw() as u64))
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialOrd<Rate<u64, R_NOM, R_DENOM>> for Rate<u32, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Rate<u64, R_NOM, R_DENOM>) -> Option<Ordering> {
|
||||
Rate::<u64, L_NOM, L_DENOM>::from_raw(self.raw as u64).partial_cmp(other)
|
||||
}
|
||||
}
|
||||
|
||||
impl<const L_NOM: u32, const L_DENOM: u32, const R_NOM: u32, const R_DENOM: u32>
|
||||
PartialEq<Rate<u64, R_NOM, R_DENOM>> for Rate<u32, L_NOM, L_DENOM>
|
||||
{
|
||||
#[inline]
|
||||
fn eq(&self, other: &Rate<u64, R_NOM, R_DENOM>) -> bool {
|
||||
Rate::<u64, L_NOM, L_DENOM>::from_raw(self.raw as u64).eq(other)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u32 Rate
|
||||
pub trait ExtU32 {
|
||||
/// Shorthand for creating a rate which represents hertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn Hz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a rate which represents kilohertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn kHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a rate which represents megahertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn MHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU32 for u32 {
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn Hz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM> {
|
||||
Rate::<u32, NOM, DENOM>::Hz(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn kHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM> {
|
||||
Rate::<u32, NOM, DENOM>::kHz(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn MHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u32, NOM, DENOM> {
|
||||
Rate::<u32, NOM, DENOM>::MHz(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for simple short-hands for u64 Rate
|
||||
pub trait ExtU64 {
|
||||
/// Shorthand for creating a rate which represents hertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn Hz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a rate which represents kilohertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn kHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM>;
|
||||
|
||||
/// Shorthand for creating a rate which represents megahertz.
|
||||
#[allow(non_snake_case)]
|
||||
fn MHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM>;
|
||||
}
|
||||
|
||||
impl ExtU64 for u64 {
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn Hz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM> {
|
||||
Rate::<u64, NOM, DENOM>::Hz(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn kHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM> {
|
||||
Rate::<u64, NOM, DENOM>::kHz(self)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[allow(non_snake_case)]
|
||||
fn MHz<const NOM: u32, const DENOM: u32>(self) -> Rate<u64, NOM, DENOM> {
|
||||
Rate::<u64, NOM, DENOM>::MHz(self)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user