diff --git a/.github/workflows/actions.yml b/.github/workflows/actions.yml index e752843..184a2ca 100644 --- a/.github/workflows/actions.yml +++ b/.github/workflows/actions.yml @@ -14,7 +14,7 @@ jobs: os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build run: cargo build --verbose env: @@ -28,7 +28,7 @@ jobs: os: ['ubuntu-latest', 'macos-latest'] runs-on: ${{ matrix.os }} steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Build run: cargo build --verbose --no-default-features env: @@ -40,7 +40,7 @@ jobs: name: Check rustfmt style && run clippy runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -48,7 +48,7 @@ jobs: components: clippy, rustfmt override: true - name: Cache cargo registry - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.cargo/registry key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 673fb8b..c06e1a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this library adheres to Rust's notion of [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 0.12.0 + +### Added +- `bincode` feature flag to enable the use of `bincode-2.0.0`. + +### Changed +- `NonEmpty::capacity` returns `NonZeroUsize` +- Fixed the `nonempty!` macro to use the `vec!` macro internally, ensuring that + it compiles with `std` and `no_std`. + ## 0.11.0 ### Added diff --git a/Cargo.toml b/Cargo.toml index 6c6ad4c..a8b581a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "nonempty" -version = "0.11.0" +version = "0.12.0" description = "Correct by construction non-empty vector" authors = ["Alexis Sellier "] edition = "2021" @@ -10,14 +10,14 @@ repository = "https://github.com/cloudhead/nonempty" [dependencies] serde = { features = ["derive", "alloc"], default-features = false, optional = true, version = "1" } arbitrary = { features = ["derive"], optional = true, version = "1" } -bincode = { optional = true, version = "2.0.0-rc.3" } +bincode = { optional = true, version = "2.0.1" } [features] default = ["std"] std = [] serialize = ["dep:serde"] arbitrary = ["dep:arbitrary"] -bincode2 = ["dep:bincode"] +bincode = ["dep:bincode"] [dev-dependencies] serde_json = "1" diff --git a/src/lib.rs b/src/lib.rs index 15815b0..07465bb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,8 +71,8 @@ //! # Caveats //! //! Since `NonEmpty` must have a least one element, it is not possible to -//! implement the `FromIterator` trait for it. We can't know, in general, if -//! any given `Iterator` actually contains something. +//! implement the [`FromIterator`] trait for it. We can't know, in general, if +//! any given [`Iterator`] actually contains something. #![no_std] @@ -80,10 +80,10 @@ //! //! * `serialize`: `serde` support. //! * `arbitrary`: `arbitrary` support. -//! * `bincode2`" `bincode@2.0.0-rc.3` support. +//! * `bincode`" `bincode` support. #[cfg(feature = "arbitrary")] use arbitrary::Arbitrary; -#[cfg(feature = "bincode2")] +#[cfg(feature = "bincode")] use bincode::{Decode, Encode}; #[cfg(feature = "serialize")] use serde::{ @@ -104,6 +104,11 @@ use alloc::vec::{self, Vec}; pub mod nonzero; +#[doc(hidden)] +pub mod __macro_support { + pub use alloc::vec; +} + /// Like the `vec!` macro, but enforces at least one argument. A nice short-hand /// for constructing [`NonEmpty`] values. /// @@ -128,13 +133,13 @@ pub mod nonzero; #[macro_export] macro_rules! nonempty { ($h:expr, $( $x:expr ),* $(,)?) => {{ - let tail = vec![$($x),*]; + let tail = $crate::__macro_support::vec![$($x),*]; $crate::NonEmpty { head: $h, tail } }}; ($h:expr) => { $crate::NonEmpty { head: $h, - tail: alloc::vec::Vec::new(), + tail: $crate::__macro_support::vec![], } }; } @@ -142,15 +147,8 @@ macro_rules! nonempty { /// Non-empty vector. #[cfg_attr(feature = "serialize", derive(Deserialize))] #[cfg_attr(feature = "arbitrary", derive(Arbitrary))] -#[cfg_attr(feature = "bincode2", derive(Encode, Decode))] #[cfg_attr(feature = "serialize", serde(try_from = "Vec"))] -#[cfg_attr( - feature = "bincode2", - bincode( - encode_bounds = "T: Encode + 'static", - decode_bounds = "T: Decode + 'static", - ) -)] +#[cfg_attr(feature = "bincode", derive(Encode, Decode))] #[derive(Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct NonEmpty { pub head: T, @@ -351,8 +349,8 @@ impl NonEmpty { } /// Get the capacity of the list. - pub fn capacity(&self) -> usize { - self.tail.capacity() + 1 + pub fn capacity(&self) -> NonZeroUsize { + NonZeroUsize::MIN.saturating_add(self.tail.capacity()) } /// Get the last element. Never fails. @@ -1264,6 +1262,34 @@ mod tests { } } + #[cfg(feature = "bincode")] + mod bincode { + use crate::NonEmpty; + use alloc::boxed::Box; + + #[derive(Clone, Debug, Eq, PartialEq, bincode::Encode, bincode::Decode)] + pub struct SimpleSerializable(pub i32); + + #[test] + fn test_simple_round_trip() -> Result<(), Box> { + // Given + let mut non_empty = NonEmpty::new(SimpleSerializable(42)); + non_empty.push(SimpleSerializable(777)); + + // When + let config = bincode::config::standard(); + let (res, _) = bincode::decode_from_slice::, _>( + &bincode::encode_to_vec(non_empty.clone(), config)?, + config, + )?; + + // Then + assert_eq!(res, non_empty); + + Ok(()) + } + } + #[cfg(feature = "arbitrary")] mod arbitrary { use crate::NonEmpty;