diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ffffc6e5..995e0f76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,7 +23,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - rust: [stable, beta, nightly, 1.75.0] + rust: [stable, beta, nightly, 1.85.0] steps: - uses: actions/checkout@v6 diff --git a/Cargo.lock b/Cargo.lock index f1bb3c09..415abbc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "anstream" @@ -174,9 +174,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" dependencies = [ "foldhash", ] diff --git a/Cargo.toml b/Cargo.toml index 7833ce94..e0d116c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,8 +9,8 @@ repository = "https://github.com/bmwill/diffy" readme = "README.md" keywords = ["diff", "patch", "merge"] categories = ["text-processing"] -rust-version = "1.75.0" -edition = "2021" +rust-version = "1.85.0" +edition = "2024" [package.metadata.docs.rs] # To build locally: @@ -35,7 +35,7 @@ binary = ["dep:zlib-rs"] color = ["dep:anstyle"] [dependencies] -hashbrown = { version = "0.16.1", default-features = false, features = ["default-hasher"] } +hashbrown = { version = "0.17.0", default-features = false, features = ["default-hasher"] } anstyle = { version = "1.0.13", default-features = false, optional = true } zlib-rs = { version = "0.6.3", optional = true, default-features = false, features = ["c-allocator"] } diff --git a/examples/patch_formatter.rs b/examples/patch_formatter.rs index 4a1a90cf..76d3dc7a 100644 --- a/examples/patch_formatter.rs +++ b/examples/patch_formatter.rs @@ -1,5 +1,5 @@ -use diffy::create_patch; use diffy::PatchFormatter; +use diffy::create_patch; fn main() { let original = "first line\nlast line"; diff --git a/src/apply.rs b/src/apply.rs index 779cb2be..ad8adafb 100644 --- a/src/apply.rs +++ b/src/apply.rs @@ -20,9 +20,7 @@ impl fmt::Display for ApplyError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for ApplyError {} +impl core::error::Error for ApplyError {} #[derive(Debug)] enum ImageLine<'a, T: ?Sized> { @@ -60,8 +58,8 @@ impl Clone for ImageLine<'_, T> { /// Apply a `Patch` to a base image /// /// ``` -/// use diffy::apply; /// use diffy::Patch; +/// use diffy::apply; /// /// let s = "\ /// --- a/ideals @@ -112,8 +110,8 @@ pub fn apply(base_image: &str, patch: &Patch<'_, str>) -> Result) -> Result<(), Bas /// Callers encoding data where the byte count isn't a multiple of 4 /// must handle padding at a higher level. /// For example, via a length indicator in Git binary patch format. -#[allow(dead_code)] // will be used for patch formatting +// will be used for patch formatting +#[cfg_attr(not(test), expect(dead_code))] pub fn encode_into(input: &[u8], output: &mut impl Extend) -> Result<(), Base85Error> { if input.len() % 4 != 0 { return Err(Base85Error::InvalidLength); diff --git a/src/binary/delta.rs b/src/binary/delta.rs index fb6f2fee..48e44529 100644 --- a/src/binary/delta.rs +++ b/src/binary/delta.rs @@ -221,9 +221,7 @@ impl fmt::Display for DeltaError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for DeltaError {} +impl core::error::Error for DeltaError {} #[cfg(test)] mod tests { diff --git a/src/binary/mod.rs b/src/binary/mod.rs index 07b9f4f5..49f084e9 100644 --- a/src/binary/mod.rs +++ b/src/binary/mod.rs @@ -291,9 +291,7 @@ impl fmt::Display for BinaryPatchParseError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for BinaryPatchParseError {} +impl core::error::Error for BinaryPatchParseError {} #[cfg(feature = "binary")] impl From for BinaryPatchParseError { @@ -320,8 +318,6 @@ impl From for BinaryPatchParseError { #[non_exhaustive] pub(crate) enum BinaryPatchParseErrorKind { /// Missing or invalid "GIT binary patch" header. - // TODO: Switch to #[expect(dead_code)] when MSRV >= 1.81 - #[cfg_attr(not(feature = "binary"), allow(dead_code))] InvalidHeader, /// First binary block (forward) not found. @@ -331,13 +327,11 @@ pub(crate) enum BinaryPatchParseErrorKind { MissingReverseBlock, /// No binary data available (marker-only patch). - // TODO: Switch to #[expect(dead_code)] when MSRV >= 1.81 - #[cfg_attr(not(feature = "binary"), allow(dead_code))] + #[cfg_attr(not(feature = "binary"), expect(dead_code))] NoBinaryData, /// Invalid line length indicator in Base85 data. - // TODO: Switch to #[expect(dead_code)] when MSRV >= 1.81 - #[cfg_attr(not(feature = "binary"), allow(dead_code))] + #[cfg_attr(not(feature = "binary"), expect(dead_code))] InvalidLineLengthIndicator, /// Base85 decoding failed. diff --git a/src/diff/cleanup.rs b/src/diff/cleanup.rs index 3b1b6dca..f323dcc3 100644 --- a/src/diff/cleanup.rs +++ b/src/diff/cleanup.rs @@ -4,7 +4,6 @@ use alloc::vec::Vec; // Walks through all edits and shifts them up and then down, trying to see if they run into similar // edits which can be merged -#[allow(clippy::needless_lifetimes)] pub fn compact<'a, 'b, T: ?Sized + SliceLike>(diffs: &mut Vec>) { // First attempt to compact all Deletions let mut pointer = 0; @@ -29,7 +28,6 @@ pub fn compact<'a, 'b, T: ?Sized + SliceLike>(diffs: &mut Vec( diffs: &mut Vec>, mut pointer: usize, @@ -131,7 +129,6 @@ fn shift_diff_up<'a, 'b, T: ?Sized + SliceLike>( } // Attempts to shift the Insertion or Deletion at location `pointer` as far downwards as possible. -#[allow(clippy::needless_lifetimes)] fn shift_diff_down<'a, 'b, T: ?Sized + SliceLike>( diffs: &mut Vec>, mut pointer: usize, diff --git a/src/diff/mod.rs b/src/diff/mod.rs index 861f638e..b62e4406 100644 --- a/src/diff/mod.rs +++ b/src/diff/mod.rs @@ -17,7 +17,6 @@ mod myers; mod tests; // TODO determine if this should be exposed in the public API -#[allow(dead_code)] #[derive(Debug, PartialEq, Eq)] enum Diff<'a, T: ?Sized> { Equal(&'a T), @@ -117,7 +116,7 @@ impl DiffOptions { /// produce a prettier diff by reducing the number of edited blocks by shifting and merging /// edit blocks. // TODO determine if this should be exposed in the public API - #[allow(dead_code)] + #[expect(dead_code)] fn set_compact(&mut self, compact: bool) -> &mut Self { self.compact = compact; self @@ -146,7 +145,6 @@ impl DiffOptions { } // TODO determine if this should be exposed in the public API - #[allow(dead_code)] fn diff<'a>(&self, original: &'a str, modified: &'a str) -> Vec> { let solution = myers::diff(original.as_bytes(), modified.as_bytes()); @@ -229,7 +227,7 @@ impl Default for DiffOptions { } // TODO determine if this should be exposed in the public API -#[allow(dead_code)] +#[cfg_attr(not(test), expect(dead_code))] fn diff<'a>(original: &'a str, modified: &'a str) -> Vec> { DiffOptions::default().diff(original, modified) } diff --git a/src/diff/tests.rs b/src/diff/tests.rs index 6255ff2c..16a38fe5 100644 --- a/src/diff/tests.rs +++ b/src/diff/tests.rs @@ -1,10 +1,10 @@ use super::*; +use crate::PatchFormatter; use crate::apply::apply; use crate::diff::Diff; use crate::diff::DiffRange; use crate::patch::Patch; use crate::range::Range; -use crate::PatchFormatter; use alloc::format; use alloc::string::ToString; use alloc::vec; @@ -59,7 +59,7 @@ macro_rules! assert_diff_range { expected, $solution, ); }; - ([$($kind:ident($text:literal)),* $(,)?], $solution:ident, $msg:expr $(,)?) => { + ([$($kind:ident($text:literal)),* $(,)?], $solution:ident, $msg:expr_2021 $(,)?) => { let expected = &[$(Diff::$kind($text)),*]; assert!( same_diffs(expected, &$solution), @@ -91,7 +91,7 @@ macro_rules! assert_diff { expected, $solution, ); }; - ([$($kind:ident($text:literal)),* $(,)?], $solution:ident, $msg:expr $(,)?) => { + ([$($kind:ident($text:literal)),* $(,)?], $solution:ident, $msg:expr_2021 $(,)?) => { let expected: &[_] = &[$(Diff::$kind($text)),*]; assert_eq!( expected, @@ -328,7 +328,7 @@ fn test_compact() { } macro_rules! assert_patch { - ($diff_options:expr, $old:ident, $new:ident, $expected:ident $(,)?) => { + ($diff_options:expr_2021, $old:ident, $new:ident, $expected:ident $(,)?) => { let patch = $diff_options.create_patch($old, $new); let bpatch = $diff_options.create_patch_bytes($old.as_bytes(), $new.as_bytes()); let patch_str = patch.to_string(); diff --git a/src/lib.rs b/src/lib.rs index 6504856c..162484a6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,7 +22,7 @@ //! This crate is `no_std` by default. //! Enable [Cargo features] as needed: //! -//! - `std` for writer-based formatting and `std::error::Error` impls +//! - `std` for std::io::Write-based formatting impls //! - `color` for ANSI-colored patch formatting //! - `binary` for applying parsed git binary patches //! @@ -112,8 +112,8 @@ //! image. //! //! ``` -//! use diffy::apply; //! use diffy::Patch; +//! use diffy::apply; //! //! let s = "\ //! --- a/skybreaker-ideals @@ -323,16 +323,16 @@ pub mod patch_set; mod range; mod utils; +pub use apply::ApplyError; pub use apply::apply; pub use apply::apply_bytes; -pub use apply::ApplyError; +pub use diff::DiffOptions; pub use diff::create_patch; pub use diff::create_patch_bytes; -pub use diff::DiffOptions; -pub use merge::merge; -pub use merge::merge_bytes; pub use merge::ConflictStyle; pub use merge::MergeOptions; +pub use merge::merge; +pub use merge::merge_bytes; pub use patch::Hunk; pub use patch::HunkRange; pub use patch::Line; diff --git a/src/merge/mod.rs b/src/merge/mod.rs index 15744df4..21f9197a 100644 --- a/src/merge/mod.rs +++ b/src/merge/mod.rs @@ -424,10 +424,10 @@ fn merge_solutions<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>( solution.push(merge_range); - if ours.map_or(true, |range| range.is_empty()) { + if ours.is_none_or(|range| range.is_empty()) { ours = our_solution.next(); } - if theirs.map_or(true, |range| range.is_empty()) { + if theirs.is_none_or(|range| range.is_empty()) { theirs = their_solution.next(); } } @@ -524,7 +524,6 @@ fn create_merge_range<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike>( } } -#[allow(clippy::needless_lifetimes)] fn cleanup_conflicts<'ancestor, 'ours, 'theirs, T: ?Sized + SliceLike + PartialEq>( solution: &mut [MergeRange<'ancestor, 'ours, 'theirs, T>], ) { diff --git a/src/merge/tests.rs b/src/merge/tests.rs index ff408605..99188df2 100644 --- a/src/merge/tests.rs +++ b/src/merge/tests.rs @@ -1,14 +1,14 @@ use super::*; macro_rules! assert_merge { - ($original:ident, $ours:ident, $theirs:ident, $kind:ident($expected:expr), $msg:literal $(,)?) => { + ($original:ident, $ours:ident, $theirs:ident, $kind:ident($expected:expr_2021), $msg:literal $(,)?) => { let solution = merge($original, $ours, $theirs); macro_rules! result { - (Ok, $s:expr) => { + (Ok, $s:expr_2021) => { Result::<&str, &str>::Ok($s) }; - (Err, $s:expr) => { + (Err, $s:expr_2021) => { Result::<&str, &str>::Err($s) }; } @@ -23,10 +23,10 @@ macro_rules! assert_merge { merge_bytes($original.as_bytes(), $ours.as_bytes(), $theirs.as_bytes()); macro_rules! result_bytes { - (Ok, $s:expr) => { + (Ok, $s:expr_2021) => { Result::<&[u8], &[u8]>::Ok($s.as_bytes()) }; - (Err, $s:expr) => { + (Err, $s:expr_2021) => { Result::<&[u8], &[u8]>::Err($s.as_bytes()) }; } diff --git a/src/patch/error.rs b/src/patch/error.rs index be3ffa9e..33767ec5 100644 --- a/src/patch/error.rs +++ b/src/patch/error.rs @@ -36,9 +36,7 @@ impl fmt::Display for ParsePatchError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for ParsePatchError {} +impl core::error::Error for ParsePatchError {} impl From for ParsePatchError { fn from(kind: ParsePatchErrorKind) -> Self { diff --git a/src/patch/format.rs b/src/patch/format.rs index 4cb8e43d..8f172d32 100644 --- a/src/patch/format.rs +++ b/src/patch/format.rs @@ -6,20 +6,20 @@ use core::fmt::Result; #[cfg(feature = "std")] use std::io; -#[cfg(feature = "color")] -use super::style; use super::Hunk; use super::Line; -use super::Patch; use super::NO_NEWLINE_AT_EOF; +use super::Patch; +#[cfg(feature = "color")] +use super::style; /// Formats patches for display or writing into byte streams. /// /// # Examples /// /// ``` -/// use diffy::create_patch; /// use diffy::PatchFormatter; +/// use diffy::create_patch; /// /// let patch = create_patch("alpha\nbeta\n", "ALPHA\nbeta\n"); /// let formatter = PatchFormatter::new().missing_newline_message(false); diff --git a/src/patch/mod.rs b/src/patch/mod.rs index c17634a8..f75f6e97 100644 --- a/src/patch/mod.rs +++ b/src/patch/mod.rs @@ -146,7 +146,7 @@ impl<'a> Patch<'a, str> { /// /// let patch = Patch::from_str(s).unwrap(); /// ``` - #[allow(clippy::should_implement_trait)] + #[expect(clippy::should_implement_trait)] pub fn from_str(s: &'a str) -> Result, ParsePatchError> { parse::parse(s) } diff --git a/src/patch/parse.rs b/src/patch/parse.rs index c730da07..849e9125 100644 --- a/src/patch/parse.rs +++ b/src/patch/parse.rs @@ -1,15 +1,15 @@ //! Parse a Patch -use super::error::ParsePatchError; -use super::error::ParsePatchErrorKind; use super::Hunk; use super::HunkRange; use super::Line; use super::NO_NEWLINE_AT_EOF; +use super::error::ParsePatchError; +use super::error::ParsePatchErrorKind; use crate::patch::Patch; -use crate::utils::escaped_filename; use crate::utils::LineIter; use crate::utils::Text; +use crate::utils::escaped_filename; use alloc::borrow::Cow; use alloc::vec::Vec; @@ -38,7 +38,6 @@ impl ParseOpts { /// /// Useful when the caller has already positioned the input /// at the start of the patch content. - #[allow(dead_code)] // will be used by patch_set parser pub(crate) fn no_skip_preamble(mut self) -> Self { self.skip_preamble = false; self @@ -145,7 +144,7 @@ pub(crate) fn parse_one( (Ok(Patch::new(header.0, header.1, hunks)), parser.offset()) } -#[allow(clippy::type_complexity)] +#[expect(clippy::type_complexity)] fn patch_header<'a, T: Text + ?Sized>( parser: &mut Parser<'a, T>, opts: &ParseOpts, diff --git a/src/patch/tests.rs b/src/patch/tests.rs index c61c37c6..6fe864d0 100644 --- a/src/patch/tests.rs +++ b/src/patch/tests.rs @@ -644,8 +644,8 @@ fn non_utf8_escaped_filename_returns_error_on_str_parse() { mod error_display { use alloc::string::ToString; - use crate::patch::error::ParsePatchErrorKind; use crate::Patch; + use crate::patch::error::ParsePatchErrorKind; use snapbox::assert_data_eq; use snapbox::str; diff --git a/src/patch_set/error.rs b/src/patch_set/error.rs index 6a169445..798c3d7c 100644 --- a/src/patch_set/error.rs +++ b/src/patch_set/error.rs @@ -43,9 +43,7 @@ impl fmt::Display for PatchSetParseError { } } -#[cfg(feature = "std")] -#[cfg_attr(docsrs, doc(cfg(feature = "std")))] -impl std::error::Error for PatchSetParseError {} +impl core::error::Error for PatchSetParseError {} impl From for PatchSetParseError { fn from(kind: PatchSetParseErrorKind) -> Self { diff --git a/src/patch_set/mod.rs b/src/patch_set/mod.rs index 2b341c29..15f94d5a 100644 --- a/src/patch_set/mod.rs +++ b/src/patch_set/mod.rs @@ -12,9 +12,9 @@ use alloc::borrow::Cow; use alloc::borrow::ToOwned; use core::fmt; +use crate::Patch; use crate::binary::BinaryPatch; use crate::utils::Text; -use crate::Patch; pub use error::PatchSetParseError; use error::PatchSetParseErrorKind; diff --git a/src/patch_set/parse.rs b/src/patch_set/parse.rs index 14409556..8facc78c 100644 --- a/src/patch_set/parse.rs +++ b/src/patch_set/parse.rs @@ -1,18 +1,18 @@ //! Parse multiple file patches from a unified diff. -use super::error::PatchSetParseErrorKind; use super::FileMode; use super::FileOperation; use super::FilePatch; use super::Format; use super::ParseOptions; use super::PatchSetParseError; -use crate::binary::parse_binary_patch; +use super::error::PatchSetParseErrorKind; +use crate::Patch; use crate::binary::BinaryPatch; +use crate::binary::parse_binary_patch; use crate::patch::parse::parse_one; -use crate::utils::escaped_filename; use crate::utils::Text; -use crate::Patch; +use crate::utils::escaped_filename; use alloc::borrow::Cow; use alloc::string::String; @@ -610,7 +610,7 @@ fn parse_unquoted_diff_git_path<'a, T: Text + ?Sized>( let mut best_match = None; let mut longest_path_len = 0; - for (i, _) in bytes.iter().enumerate().filter(|(_, &b)| b == b' ') { + for (i, _) in bytes.iter().enumerate().filter(|&(_, &b)| b == b' ') { let (left, right_with_space) = line.split_at(i); // skip the space let (_, right) = right_with_space.split_at(1); diff --git a/src/patch_set/tests.rs b/src/patch_set/tests.rs index 0ab01fea..8d35809c 100644 --- a/src/patch_set/tests.rs +++ b/src/patch_set/tests.rs @@ -4,10 +4,10 @@ use alloc::borrow::ToOwned; use alloc::string::ToString; use alloc::vec::Vec; -use super::error::PatchSetParseErrorKind; use super::FileOperation; use super::ParseOptions; use super::PatchSet; +use super::error::PatchSetParseErrorKind; mod file_operation { use super::*; diff --git a/src/range.rs b/src/range.rs index 5e454c90..cb9c22c5 100644 --- a/src/range.rs +++ b/src/range.rs @@ -35,7 +35,6 @@ impl<'a, T: ?Sized> Range<'a, T> { self.offset } - #[allow(dead_code)] pub fn range(&self) -> ops::Range { self.offset..self.offset + self.len } @@ -98,7 +97,6 @@ where Range { inner, offset, len } } - #[allow(dead_code)] pub fn empty() -> Range<'a, T> { Range { inner: T::empty(), @@ -119,17 +117,17 @@ where self.as_slice().common_suffix_len(other.as_slice()) } - #[allow(dead_code)] + #[cfg_attr(not(test), expect(dead_code))] pub fn common_overlap_len(&self, other: Range<'_, T>) -> usize { self.as_slice().common_overlap_len(other.as_slice()) } - #[allow(dead_code)] + #[expect(dead_code)] pub fn starts_with(&self, prefix: Range<'_, T>) -> bool { self.as_slice().starts_with(prefix.as_slice()) } - #[allow(dead_code)] + #[expect(dead_code)] pub fn ends_with(&self, suffix: Range<'_, T>) -> bool { self.as_slice().ends_with(suffix.as_slice()) } diff --git a/src/utils.rs b/src/utils.rs index bfbd16e0..d94d56b7 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -5,11 +5,11 @@ use alloc::borrow::ToOwned; use alloc::string::String; use alloc::vec::Vec; use core::hash::Hash; -use hashbrown::hash_map::Entry; use hashbrown::HashMap; +use hashbrown::hash_map::Entry; -use crate::patch::error::ParsePatchErrorKind; use crate::ParsePatchError; +use crate::patch::error::ParsePatchErrorKind; /// Returns `true` if a byte must be quoted in a diff filename. /// @@ -140,7 +140,6 @@ pub trait Text: Eq + Hash + ToOwned { fn is_empty(&self) -> bool; fn len(&self) -> usize; fn starts_with(&self, prefix: &str) -> bool; - #[allow(unused)] fn ends_with(&self, suffix: &str) -> bool; fn strip_prefix(&self, prefix: &str) -> Option<&Self>; fn strip_suffix(&self, suffix: &str) -> Option<&Self>; @@ -149,7 +148,6 @@ pub trait Text: Eq + Hash + ToOwned { fn split_at(&self, mid: usize) -> (&Self, &Self); fn as_str(&self) -> Option<&str>; fn as_bytes(&self) -> &[u8]; - #[allow(unused)] fn lines(&self) -> LineIter<'_, Self>; /// Converts raw bytes into `Self::Owned`.