From 2c8048718e4a71c0bdcfc03353105722811b8761 Mon Sep 17 00:00:00 2001 From: tsk Date: Mon, 27 Jul 2026 12:20:44 +0000 Subject: [PATCH] fix: support Rust 1.85 as the MSRV Replace APIs that are not stable in Rust 1.85 while preserving the public const encoder accessors by caching the fragment count. Declare the minimum toolchain in Cargo.toml and add CI checks for the default and no-std library configurations. --- .github/workflows/rust.yml | 15 +++++++++++++++ Cargo.toml | 1 + src/bytewords.rs | 2 +- src/fountain.rs | 20 ++++++++++++++------ src/ur.rs | 6 +++++- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 9025e2e..2ff1c03 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -10,6 +10,21 @@ env: CARGO_TERM_COLOR: always jobs: + msrv: + name: MSRV (1.85) + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v7 + - name: Install Rust 1.85 + uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.85.0 + - name: Check library + run: | + cargo check --package ur --lib + cargo check --package ur --lib --no-default-features + build: runs-on: ubuntu-latest diff --git a/Cargo.toml b/Cargo.toml index a82ed8e..772587d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ license = "MIT" version = "0.5.0" authors = ["Dominik Spicher "] edition = "2024" +rust-version = "1.85" repository = "https://github.com/dspicher/ur-rs/" [workspace] members = ["examples/wasm", "fuzz"] diff --git a/src/bytewords.rs b/src/bytewords.rs index fde9e33..2bacc14 100644 --- a/src/bytewords.rs +++ b/src/bytewords.rs @@ -115,7 +115,7 @@ pub fn decode(encoded: &str, style: Style) -> Result, Error> { } fn decode_minimal(encoded: &str) -> Result, Error> { - if !encoded.len().is_multiple_of(2) { + if encoded.len() % 2 != 0 { return Err(Error::InvalidLength); } diff --git a/src/fountain.rs b/src/fountain.rs index 6952126..7b8913d 100644 --- a/src/fountain.rs +++ b/src/fountain.rs @@ -140,6 +140,7 @@ impl From> for Error { #[derive(Debug)] pub struct Encoder { parts: Vec>, + fragment_count: usize, message_length: usize, checksum: u32, current_sequence: usize, @@ -177,8 +178,10 @@ impl Encoder { } let fragment_length = fragment_length(message.len(), max_fragment_length); let fragments = partition(message.to_vec(), fragment_length); + let fragment_count = fragments.len(); Ok(Self { parts: fragments, + fragment_count, message_length: message.len(), checksum: crate::crc32().checksum(message), current_sequence: 0, @@ -211,7 +214,7 @@ impl Encoder { /// See the [`crate::fountain`] module documentation for an example. pub fn next_part(&mut self) -> Part { self.current_sequence += 1; - let indexes = choose_fragments(self.current_sequence, self.parts.len(), self.checksum); + let indexes = choose_fragments(self.current_sequence, self.fragment_count, self.checksum); let mut mixed = alloc::vec![0; self.parts[0].len()]; for item in indexes { @@ -220,7 +223,7 @@ impl Encoder { Part { sequence: self.current_sequence, - sequence_count: self.parts.len(), + sequence_count: self.fragment_count, message_length: self.message_length, checksum: self.checksum, data: mixed, @@ -238,7 +241,7 @@ impl Encoder { /// ``` #[must_use] pub const fn fragment_count(&self) -> usize { - self.parts.len() + self.fragment_count } /// Returns whether all original segments have been emitted at least once. @@ -262,7 +265,7 @@ impl Encoder { /// [`current_sequence`]: Encoder::current_sequence #[must_use] pub const fn complete(&self) -> bool { - self.current_sequence >= self.parts.len() + self.current_sequence >= self.fragment_count } } @@ -901,12 +904,17 @@ mod tests { #[test] fn test_fountain_encoder_is_complete() { + const fn progress(encoder: &Encoder) -> (usize, bool) { + (encoder.fragment_count(), encoder.complete()) + } + let message = crate::xoshiro::test_utils::make_message("Wolf", 256); let mut encoder = Encoder::new(&message, 30).unwrap(); - for _ in 0..encoder.parts.len() { + assert_eq!(progress(&encoder), (9, false)); + for _ in 0..encoder.fragment_count() { encoder.next_part(); } - assert!(encoder.complete()); + assert_eq!(progress(&encoder), (9, true)); } #[test] diff --git a/src/ur.rs b/src/ur.rs index d352c20..174b38b 100644 --- a/src/ur.rs +++ b/src/ur.rs @@ -430,6 +430,10 @@ mod tests { #[test] fn test_ur_encoder() { + const fn fragment_count(encoder: &Encoder<'_>) -> usize { + encoder.fragment_count() + } + let ur = make_message_ur(256, "Wolf"); let mut encoder = Encoder::bytes(&ur, 30).unwrap(); let expected = vec![ @@ -454,7 +458,7 @@ mod tests { "ur:bytes/19-9/lpbwascfadaxcywenbpljkhdcadekicpaajootjzpsdrbalpeywllbdsnbinaerkurspbncxgslgftvtsrjtksplcpeo", "ur:bytes/20-9/lpbbascfadaxcywenbpljkhdcayapmrleeleaxpasfrtrdkncffwjyjzgyetdmlewtkpktgllepfrltataztksmhkbot", ]; - assert_eq!(encoder.fragment_count(), 9); + assert_eq!(fragment_count(&encoder), 9); for (index, e) in expected.into_iter().enumerate() { assert_eq!(encoder.current_index(), index); assert_eq!(encoder.next_part().unwrap(), e);