From b6e2894f215654831a1220befa7eb61ead4c437d Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 15 Feb 2021 22:50:12 +0100 Subject: [PATCH] Add support for Rust 1.41.0 (#14) --- .github/workflows/tests.yml | 4 +- CHANGELOG.md | 6 +- clippy.toml | 2 +- src/iter.rs | 198 ++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/text/abstraction.rs | 6 +- src/text/mod.rs | 13 ++- src/types.rs | 83 +-------------- src/udiff.rs | 13 +-- 9 files changed, 230 insertions(+), 96 deletions(-) create mode 100644 src/iter.rs diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 8f9c5cd..88963e0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,14 +18,14 @@ jobs: run: make test build-stable: - name: Test on 1.43.0 + name: Test on 1.41.0 runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.43.0 + toolchain: 1.41.0 profile: minimal override: true - name: Test diff --git a/CHANGELOG.md b/CHANGELOG.md index 69de1af..2fedc45 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,11 @@ All notable changes to similar are documented here. -## 1.3.0 +## 1.2.2 + +* Added support for Rust 1.41.0 for better compatibility. + +## 1.2.1 * Added support for Rust 1.43.0 for better compatibility. diff --git a/clippy.toml b/clippy.toml index f97e544..88924a5 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.42.0" +msrv = "1.41.0" diff --git a/src/iter.rs b/src/iter.rs new file mode 100644 index 0000000..5ff460b --- /dev/null +++ b/src/iter.rs @@ -0,0 +1,198 @@ +//! The various iterators this crate provides. +//! +//! These iterators are not a very stable interface and you really should +//! avoid considering them to be concrete types. A lot of the iterators in +//! this crate use `impl Iterator` for this reason but restrictions in the +//! language don't allow this to be used in all places on the versions of +//! rust this crate wants to compile for. +use std::marker::PhantomData; +use std::ops::{Index, Range}; + +use crate::{Change, ChangeTag, DiffOp, DiffTag}; + +/// Iterator for [`DiffOp::iter_changes`]. +pub struct ChangesIter<'lookup, 'data, Old: ?Sized, New: ?Sized, T: ?Sized> { + old: &'lookup Old, + new: &'lookup New, + old_range: Range, + new_range: Range, + old_index: usize, + new_index: usize, + old_i: usize, + new_i: usize, + tag: DiffTag, + _marker: PhantomData<&'data T>, +} + +impl<'lookup, 'data, Old, New, T> ChangesIter<'lookup, 'data, Old, New, T> +where + Old: Index + ?Sized, + New: Index + ?Sized, + T: 'data + ?Sized, + 'data: 'lookup, +{ + pub(crate) fn new(old: &'lookup Old, new: &'lookup New, op: DiffOp) -> Self { + let (tag, old_range, new_range) = op.as_tag_tuple(); + let old_index = old_range.start; + let new_index = new_range.start; + let old_i = old_range.start; + let new_i = new_range.start; + ChangesIter { + old, + new, + old_range, + new_range, + old_index, + new_index, + old_i, + new_i, + tag, + _marker: PhantomData, + } + } +} + +impl<'lookup, 'data, Old, New, T> Iterator for ChangesIter<'lookup, 'data, Old, New, T> +where + Old: Index + ?Sized, + New: Index + ?Sized, + T: 'data + ?Sized, + 'data: 'lookup, +{ + type Item = Change<'data, T>; + + fn next(&mut self) -> Option { + match self.tag { + DiffTag::Equal => { + if self.old_i < self.old_range.end { + let value = self.old[self.old_i]; + self.old_i += 1; + self.old_index += 1; + self.new_index += 1; + Some(Change { + tag: ChangeTag::Equal, + old_index: Some(self.old_index - 1), + new_index: Some(self.new_index - 1), + value, + }) + } else { + None + } + } + DiffTag::Delete => { + if self.old_i < self.old_range.end { + let value = self.old[self.old_i]; + self.old_i += 1; + self.old_index += 1; + Some(Change { + tag: ChangeTag::Delete, + old_index: Some(self.old_index - 1), + new_index: None, + value, + }) + } else { + None + } + } + DiffTag::Insert => { + if self.new_i < self.new_range.end { + let value = self.new[self.new_i]; + self.new_i += 1; + self.new_index += 1; + Some(Change { + tag: ChangeTag::Insert, + old_index: None, + new_index: Some(self.new_index - 1), + value, + }) + } else { + None + } + } + DiffTag::Replace => { + if self.old_i < self.old_range.end { + let value = self.old[self.old_i]; + self.old_i += 1; + self.old_index += 1; + Some(Change { + tag: ChangeTag::Delete, + old_index: Some(self.old_index - 1), + new_index: None, + value, + }) + } else if self.new_i < self.new_range.end { + let value = self.new[self.new_i]; + self.new_i += 1; + self.new_index += 1; + Some(Change { + tag: ChangeTag::Insert, + old_index: None, + new_index: Some(self.new_index - 1), + value, + }) + } else { + None + } + } + } + } +} + +#[cfg(feature = "text")] +mod text { + use super::*; + + /// Iterator for [`TextDiff::iter_all_changes`](crate::TextDiff::iter_all_changes). + pub struct AllChangesIter<'slf, 'data, T: ?Sized> { + old: &'slf [&'data T], + new: &'slf [&'data T], + ops: &'slf [DiffOp], + current_iter: Option>, + } + + impl<'slf, 'data, T> AllChangesIter<'slf, 'data, T> + where + T: 'data + ?Sized + PartialEq, + { + pub(crate) fn new( + old: &'slf [&'data T], + new: &'slf [&'data T], + ops: &'slf [DiffOp], + ) -> Self { + AllChangesIter { + old, + new, + ops, + current_iter: None, + } + } + } + + impl<'slf, 'data, T> Iterator for AllChangesIter<'slf, 'data, T> + where + T: PartialEq + 'data + ?Sized, + 'data: 'slf, + { + type Item = Change<'data, T>; + + fn next(&mut self) -> Option { + loop { + if let Some(ref mut iter) = self.current_iter { + if let Some(rv) = iter.next() { + return Some(rv); + } + self.current_iter.take(); + } + if let Some((&first, rest)) = self.ops.split_first() { + self.current_iter = Some(ChangesIter::new(self.old, self.new, first)); + self.ops = rest; + } else { + return None; + } + } + } + } +} + +#[cfg(feature = "text")] +pub use self::text::*; diff --git a/src/lib.rs b/src/lib.rs index fc7f9a3..69257dd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,6 +128,7 @@ //! in a line diff. This currently also enables the `unicode` feature. #![warn(missing_docs)] pub mod algorithms; +pub mod iter; #[cfg(feature = "text")] pub mod udiff; #[cfg(feature = "text")] diff --git a/src/text/abstraction.rs b/src/text/abstraction.rs index aff39ee..5b3f4ee 100644 --- a/src/text/abstraction.rs +++ b/src/text/abstraction.rs @@ -318,7 +318,11 @@ mod bytes_support { } fn ends_with_newline(&self) -> bool { - matches!(self.last_byte(), Some(b'\r') | Some(b'\n')) + if let Some(b'\r') | Some(b'\n') = self.last_byte() { + true + } else { + false + } } fn len(&self) -> usize { diff --git a/src/text/mod.rs b/src/text/mod.rs index ef8e525..6ff7c36 100644 --- a/src/text/mod.rs +++ b/src/text/mod.rs @@ -13,8 +13,9 @@ pub use self::abstraction::{DiffableStr, DiffableStrRef}; pub use self::inline::InlineChange; use self::utils::{upper_seq_ratio, QuickSeqRatio}; +use crate::iter::{AllChangesIter, ChangesIter}; use crate::udiff::UnifiedDiff; -use crate::{capture_diff_slices, get_diff_ratio, group_diff_ops, Algorithm, Change, DiffOp}; +use crate::{capture_diff_slices, get_diff_ratio, group_diff_ops, Algorithm, DiffOp}; /// A builder type config for more complex uses of [`TextDiff`]. /// @@ -437,7 +438,7 @@ impl<'old, 'new, 'bufs, T: DiffableStr + ?Sized + 'old + 'new> TextDiff<'old, 'n pub fn iter_changes<'x, 'slf>( &'slf self, op: &DiffOp, - ) -> impl Iterator> + '_ + ) -> ChangesIter<'slf, 'x, [&'x T], [&'x T], T> where 'x: 'slf, 'old: 'x, @@ -462,13 +463,13 @@ impl<'old, 'new, 'bufs, T: DiffableStr + ?Sized + 'old + 'new> TextDiff<'old, 'n /// /// This is a shortcut for combining [`TextDiff::ops`] with /// [`TextDiff::iter_changes`]. - pub fn iter_all_changes<'x, 'slf>(&'slf self) -> impl Iterator> + '_ + pub fn iter_all_changes<'x, 'slf>(&'slf self) -> AllChangesIter<'slf, 'x, T> where 'x: 'slf + 'old + 'new, 'old: 'x, 'new: 'x, { - self.ops().iter().flat_map(move |op| self.iter_changes(&op)) + AllChangesIter::new(&self.old[..], &self.new[..], self.ops()) } /// Utility to return a unified diff formatter. @@ -539,7 +540,7 @@ pub fn get_close_matches<'a, T: DiffableStr + ?Sized>( if ratio >= cutoff { // we're putting the word itself in reverse in so that matches with // the same ratio are ordered lexicographically. - matches.push(((ratio * u32::MAX as f32) as u32, Reverse(possibility))); + matches.push(((ratio * std::u32::MAX as f32) as u32, Reverse(possibility))); } } @@ -668,6 +669,8 @@ fn test_get_close_matches() { #[test] fn test_lifetimes_on_iter() { + use crate::Change; + fn diff_lines<'x, T>(old: &'x T, new: &'x T) -> Vec> where T: DiffableStrRef + ?Sized, diff --git a/src/types.rs b/src/types.rs index 100d09c..8654550 100644 --- a/src/types.rs +++ b/src/types.rs @@ -2,6 +2,7 @@ use std::fmt; use std::ops::{Index, Range}; use crate::algorithms::DiffHook; +use crate::iter::ChangesIter; /// An enum representing a diffing algorithm. #[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, Debug)] @@ -272,92 +273,14 @@ impl DiffOp { &self, old: &'lookup Old, new: &'lookup New, - ) -> impl Iterator> + 'lookup + ) -> ChangesIter<'lookup, 'x, Old, New, T> where Old: Index + ?Sized, New: Index + ?Sized, T: 'x + ?Sized, 'x: 'lookup, { - let (tag, old_range, new_range) = self.as_tag_tuple(); - let mut old_index = old_range.start; - let mut new_index = new_range.start; - let mut old_i = old_range.start; - let mut new_i = new_range.start; - - std::iter::from_fn(move || match tag { - DiffTag::Equal => { - if old_i < old_range.end { - let value = old[old_i]; - old_i += 1; - old_index += 1; - new_index += 1; - Some(Change { - tag: ChangeTag::Equal, - old_index: Some(old_index - 1), - new_index: Some(new_index - 1), - value, - }) - } else { - None - } - } - DiffTag::Delete => { - if old_i < old_range.end { - let value = old[old_i]; - old_i += 1; - old_index += 1; - Some(Change { - tag: ChangeTag::Delete, - old_index: Some(old_index - 1), - new_index: None, - value, - }) - } else { - None - } - } - DiffTag::Insert => { - if new_i < new_range.end { - let value = new[new_i]; - new_i += 1; - new_index += 1; - Some(Change { - tag: ChangeTag::Insert, - old_index: None, - new_index: Some(new_index - 1), - value, - }) - } else { - None - } - } - DiffTag::Replace => { - if old_i < old_range.end { - let value = old[old_i]; - old_i += 1; - old_index += 1; - Some(Change { - tag: ChangeTag::Delete, - old_index: Some(old_index - 1), - new_index: None, - value, - }) - } else if new_i < new_range.end { - let value = new[new_i]; - new_i += 1; - new_index += 1; - Some(Change { - tag: ChangeTag::Insert, - old_index: None, - new_index: Some(new_index - 1), - value, - }) - } else { - None - } - } - }) + ChangesIter::new(old, new, *self) } /// Given a diffop yields the changes it encodes against the given slices. diff --git a/src/udiff.rs b/src/udiff.rs index 9141212..40969f2 100644 --- a/src/udiff.rs +++ b/src/udiff.rs @@ -27,8 +27,9 @@ use std::ops::Range; use std::{fmt, io}; +use crate::iter::AllChangesIter; use crate::text::{DiffableStr, TextDiff}; -use crate::types::{Algorithm, Change, DiffOp}; +use crate::types::{Algorithm, DiffOp}; struct MissingNewlineHint(bool); @@ -240,13 +241,13 @@ impl<'diff, 'old, 'new, 'bufs, T: DiffableStr + ?Sized> } /// Iterates over all changes in a hunk. - pub fn iter_changes(&self) -> impl Iterator> + '_ + pub fn iter_changes<'x, 'slf>(&'slf self) -> AllChangesIter<'slf, 'x, T> where - 'diff: 'old + 'new + 'bufs, + 'x: 'slf + 'old + 'new, + 'old: 'x, + 'new: 'x, { - self.ops() - .iter() - .flat_map(move |op| self.diff.iter_changes(op)) + AllChangesIter::new(self.diff.old_slices(), self.diff.new_slices(), self.ops()) } /// Write the hunk as bytes to the output stream.