Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions .github/workflows/actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -40,15 +40,15 @@ 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
profile: minimal
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') }}
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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 <self@cloudhead.io>"]
edition = "2021"
Expand All @@ -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"
58 changes: 42 additions & 16 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,19 @@
//! # 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]

//! # Features
//!
//! * `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::{
Expand All @@ -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.
///
Expand All @@ -128,29 +133,22 @@ 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![],
}
};
}

/// 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<T>"))]
#[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<T> {
pub head: T,
Expand Down Expand Up @@ -351,8 +349,8 @@ impl<T> NonEmpty<T> {
}

/// 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.
Expand Down Expand Up @@ -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<dyn core::error::Error>> {
// 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::<NonEmpty<SimpleSerializable>, _>(
&bincode::encode_to_vec(non_empty.clone(), config)?,
config,
)?;

// Then
assert_eq!(res, non_empty);

Ok(())
}
}

#[cfg(feature = "arbitrary")]
mod arbitrary {
use crate::NonEmpty;
Expand Down
Loading