diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 1f96788..43e45c2 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,7 +11,7 @@ jobs: fail-fast: false matrix: rust: [1.62.0, stable] - features: ['', '--all-features'] + features: ['use_alloc', 'use_alloc,defmt', 'use_heapless', 'use_heapless,defmt'] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -21,15 +21,15 @@ jobs: toolchain: ${{ matrix.rust }} components: rustfmt clippy - name: build - run: cargo build ${{ matrix.features }} + run: cargo build --features ${{ matrix.features }} - name: check - run: cargo check ${{ matrix.features }} + run: cargo check --features ${{ matrix.features }} - name: test - run: cargo test ${{ matrix.features }} + run: cargo test --features ${{ matrix.features }} - name: check formatting run: cargo fmt --all -- --check - name: clippy - run: cargo clippy ${{ matrix.features }} + run: cargo clippy --features ${{ matrix.features }} - name: audit run: cargo audit diff --git a/CHANGELOG.md b/CHANGELOG.md index ff36fba..3b2b05a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,9 +6,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - ReleaseDate +### Added +* Add support for using [`alloc::vec::Vec`](https://doc.rust-lang.org/alloc/vec/struct.Vec.html) ### Changed * Due to dependency updates the MSRV has been updated from 1.60 to 1.62. This should only be relevant if you use the `defmt` feature, but we now only test with 1.62 and not older releases, so it's not guaranteed to work otherwise. * Update to `heapless:0.8.0` +### Breaking Changes +* You must now select either the feature `use_alloc` or `use_heapless` for the crate to compile. Select `use_heapless` + to keep the API from the previous release of this crate. ## [0.1.1] - 2023-01-07 ### Changed diff --git a/Cargo.toml b/Cargo.toml index 2ff54a8..e531b8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT OR Apache-2.0" [dependencies] defmt = { version = "0.3", optional = true } -heapless = { version = "0.8" } +heapless = { version = "0.8", optional = true } rgb = { version = "0.8", optional = true } serde = { version = "1.0", features = ["derive"], optional = true } @@ -20,7 +20,10 @@ serde = { version = "1.0", features = ["derive"], optional = true } [features] default = ["accelerometer_event", "button_event", "color_event", "gyro_event", "location_event", "magnetometer_event", "quaternion_event"] -defmt = ["dep:defmt", "heapless/defmt-03"] +use_heapless = ["dep:heapless"] +use_alloc = [] + +defmt = ["dep:defmt", "heapless?/defmt-03"] accelerometer_event = [] button_event = [] diff --git a/README.md b/README.md index 41ba5b0..c1927af 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,13 @@ which is e.g. used by the [Adafruit Bluefruit LE UART Friend](https://learn.adaf Note that this work is not affiliated with Adafruit. -## Optional features +## Mandatory Features +This crate is `no_std` and you can choose whether you want to use +[`heapless::Vec`](https://docs.rs/heapless/0.8.0/heapless/struct.Vec.html) by selecting the feature `use_heapless` or +[`alloc::vec::Vec`](https://doc.rust-lang.org/alloc/vec/struct.Vec.html) by selecting the feature `use_alloc`. +If you select neither or both you'll get a compile error. + +## Optional Features * `defmt`: you can enable the [`defmt`](https://defmt.ferrous-systems.com/) feature to get a `defmt::Format` implementation for all structs & enums and a `defmt::debug!` call for each command being parsed. * `rgb`: if enabled, `From for RGB8` is implemented to support the [RGB crate](https://crates.io/crates/rgb). * `serde`: if enabled, all events implement the [serde](https://serde.rs/) `#[derive(Serialize, Deserialize)]`. diff --git a/examples/stm32f4-event-printer/Cargo.toml b/examples/stm32f4-event-printer/Cargo.toml index dd257a3..e207b11 100644 --- a/examples/stm32f4-event-printer/Cargo.toml +++ b/examples/stm32f4-event-printer/Cargo.toml @@ -17,7 +17,7 @@ defmt = "0.3.5" defmt-rtt = "0.4" # use `adafruit-bluefruit-protocol = "0.1"` in reality; path used here to ensure that the example always compiles against the latest master -adafruit-bluefruit-protocol = { path = "../..", features = ["defmt"] } +adafruit-bluefruit-protocol = { path = "../..", features = ["defmt", "use_heapless"] } [profile.release] codegen-units = 1 diff --git a/src/lib.rs b/src/lib.rs index 2f0a6d9..6a1fcac 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,12 @@ )))] compile_error!("at least one event type must be selected in the features!"); +#[cfg(not(any(feature = "use_alloc", feature = "use_heapless")))] +compile_error!("you must choose either 'use_alloc' or 'use_heapless' as a feature!"); + +#[cfg(all(feature = "use_alloc", feature = "use_heapless"))] +compile_error!("you must choose either 'use_alloc' or 'use_heapless' as a feature but not both!"); + #[cfg(feature = "accelerometer_event")] pub mod accelerometer_event; #[cfg(feature = "button_event")] @@ -53,7 +59,13 @@ use color_event::ColorEvent; use core::cmp::min; #[cfg(feature = "gyro_event")] use gyro_event::GyroEvent; +#[cfg(feature = "use_heapless")] use heapless::Vec; + +#[cfg(feature = "use_alloc")] +extern crate alloc; +#[cfg(feature = "use_alloc")] +use alloc::vec::Vec; #[cfg(feature = "location_event")] use location_event::LocationEvent; #[cfg(feature = "magnetometer_event")] @@ -156,10 +168,19 @@ impl TryFrom for ControllerDataPackageType { } } +#[cfg(feature = "use_heapless")] +type ParseResult = + Vec, MAX_RESULTS>; + +#[cfg(feature = "use_alloc")] +type ParseResult = Vec>; +#[cfg(feature = "use_alloc")] +const MAX_RESULTS: usize = 0; + /// Parse the input string for commands. Unexpected content will be ignored if it's not formatted like a command! -pub fn parse( +pub fn parse<#[cfg(feature = "use_heapless")] const MAX_RESULTS: usize>( input: &[u8], -) -> Vec, MAX_RESULTS> { +) -> ParseResult { /// Simple state machine for the parser, represents whether the parser is seeking a start or has found it. enum ParserState { SeekStart, @@ -167,7 +188,7 @@ pub fn parse( } let mut state = ParserState::SeekStart; - let mut result: Vec, MAX_RESULTS> = Vec::new(); + let mut result = Vec::new(); for pos in 0..input.len() { let byte = input[pos]; @@ -179,7 +200,11 @@ pub fn parse( } ParserState::ParseCommand => { let data_package = extract_and_parse_command(&input[(pos - 1)..]); + #[cfg(feature = "use_alloc")] + result.push(data_package); + #[cfg(feature = "use_heapless")] result.push(data_package).ok(); + #[cfg(feature = "use_heapless")] if result.len() == MAX_RESULTS { return result; } @@ -334,9 +359,11 @@ mod tests { #[test] fn test_parse() { - const MAX_RESULTS: usize = 4; let input = b"\x00!B11:!B10;\x00\x00!\x00\x00\x00\x00"; - let result = parse::(input); + #[cfg(feature = "use_heapless")] + let result = parse::<4>(input); + #[cfg(feature = "use_alloc")] + let result = parse(input); assert_eq!(result.len(), 3); assert_is_button_event(&result[0], Button::Button1, ButtonState::Pressed);