mirror of
https://github.com/RGBCube/dix
synced 2025-07-28 12:17:45 +00:00
feat: fix formatting, yield ignored chars in version component iter
This commit is contained in:
parent
4f0ee04e19
commit
a1ccf88d0c
3 changed files with 155 additions and 96 deletions
159
src/diff.rs
159
src/diff.rs
|
@ -1,12 +1,12 @@
|
|||
use std::{
|
||||
fmt::{
|
||||
self,
|
||||
Write as _,
|
||||
},
|
||||
sync,
|
||||
use std::fmt::{
|
||||
self,
|
||||
Write as _,
|
||||
};
|
||||
|
||||
use itertools::EitherOrBoth;
|
||||
use itertools::{
|
||||
EitherOrBoth,
|
||||
Itertools,
|
||||
};
|
||||
use ref_cast::RefCast as _;
|
||||
use rustc_hash::{
|
||||
FxBuildHasher,
|
||||
|
@ -20,9 +20,7 @@ use crate::{
|
|||
Version,
|
||||
};
|
||||
|
||||
const HEADER_STYLE: yansi::Style = yansi::Style::new().bold().underline();
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
struct Diff<T> {
|
||||
old: T,
|
||||
new: T,
|
||||
|
@ -110,9 +108,18 @@ pub fn diff<'a>(
|
|||
for (name, versions, status) in diffs {
|
||||
if last_status != Some(status) {
|
||||
last_status = Some(status);
|
||||
HEADER_STYLE.fmt_prefix(writer)?;
|
||||
writeln!(writer, "{status:?} packages:")?;
|
||||
HEADER_STYLE.fmt_suffix(writer)?;
|
||||
|
||||
writeln!(
|
||||
writer,
|
||||
"{nl}{status}",
|
||||
nl = if last_status.is_some() { "\n" } else { "" },
|
||||
status = match status {
|
||||
DiffStatus::Added => "ADDED",
|
||||
DiffStatus::Removed => "REMOVED",
|
||||
DiffStatus::Changed => "CHANGED",
|
||||
}
|
||||
.bold(),
|
||||
)?;
|
||||
}
|
||||
|
||||
write!(
|
||||
|
@ -122,56 +129,112 @@ pub fn diff<'a>(
|
|||
)?;
|
||||
|
||||
let mut oldacc = String::new();
|
||||
let mut oldwrote = false;
|
||||
let mut newacc = String::new();
|
||||
let mut newwrote = false;
|
||||
|
||||
for diff in itertools::Itertools::zip_longest(
|
||||
versions.old.iter(),
|
||||
versions.new.iter(),
|
||||
) {
|
||||
for diff in Itertools::zip_longest(versions.old.iter(), versions.new.iter())
|
||||
{
|
||||
match diff {
|
||||
EitherOrBoth::Right(old_version) => {
|
||||
if oldwrote {
|
||||
write!(oldacc, ", ")?;
|
||||
} else {
|
||||
write!(oldacc, " ")?;
|
||||
oldwrote = true;
|
||||
}
|
||||
|
||||
for old_comp in old_version.unwrap_or(Version::ref_cast("<none>")) {
|
||||
match old_comp {
|
||||
Ok(old_comp) => write!(oldacc, "{old}", old = old_comp.red())?,
|
||||
Err(ignored) => write!(oldacc, "{ignored}")?,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// I have no idea why itertools is returning `versions.new` in `Left`.
|
||||
EitherOrBoth::Left(new) => {
|
||||
let new = new.unwrap_or(Version::ref_cast("<none>"));
|
||||
EitherOrBoth::Left(new_version) => {
|
||||
if newwrote {
|
||||
write!(newacc, ", ")?;
|
||||
} else {
|
||||
write!(newacc, " ")?;
|
||||
newwrote = true;
|
||||
}
|
||||
|
||||
write!(newacc, " {new}", new = new.green())?;
|
||||
for new_comp in new_version.unwrap_or(Version::ref_cast("<none>")) {
|
||||
match new_comp {
|
||||
Ok(new_comp) => write!(newacc, "{new}", new = new_comp.green())?,
|
||||
Err(ignored) => write!(newacc, "{ignored}")?,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
EitherOrBoth::Right(old) => {
|
||||
let old = old.unwrap_or(Version::ref_cast("<none>"));
|
||||
EitherOrBoth::Both(old_version, new_version) => {
|
||||
if old_version == new_version {
|
||||
continue;
|
||||
}
|
||||
|
||||
write!(oldacc, " {old}", old = old.red())?;
|
||||
},
|
||||
let old_version = old_version.unwrap_or(Version::ref_cast("<none>"));
|
||||
let new_version = new_version.unwrap_or(Version::ref_cast("<none>"));
|
||||
|
||||
EitherOrBoth::Both(old, new) => {
|
||||
static NAME_SUFFIX_REGEX: sync::LazyLock<regex::Regex> =
|
||||
sync::LazyLock::new(|| {
|
||||
regex::Regex::new("(-man|-lib|-doc|-dev|-out|-terminfo)")
|
||||
.expect("failed to compile regex for Nix store path versions")
|
||||
});
|
||||
if oldwrote {
|
||||
write!(oldacc, ", ")?;
|
||||
} else {
|
||||
write!(oldacc, " ")?;
|
||||
oldwrote = true;
|
||||
}
|
||||
if newwrote {
|
||||
write!(newacc, ", ")?;
|
||||
} else {
|
||||
write!(newacc, " ")?;
|
||||
newwrote = true;
|
||||
}
|
||||
|
||||
let old = old.unwrap_or(Version::ref_cast("<none>"));
|
||||
let new = new.unwrap_or(Version::ref_cast("<none>"));
|
||||
|
||||
let suffix = NAME_SUFFIX_REGEX.captures(old).map_or("", |matches| {
|
||||
matches.get(0).map_or("", |capture| capture.as_str())
|
||||
});
|
||||
|
||||
let old = old.strip_suffix(suffix).unwrap_or(old);
|
||||
let new = new.strip_suffix(suffix).unwrap_or(new);
|
||||
|
||||
for diff in diff::chars(old, new) {
|
||||
for diff in Itertools::zip_longest(
|
||||
old_version.into_iter(),
|
||||
new_version.into_iter(),
|
||||
) {
|
||||
match diff {
|
||||
diff::Result::Left(oldc) => {
|
||||
write!(oldacc, "{oldc}", oldc = oldc.red()).unwrap();
|
||||
EitherOrBoth::Right(old_comp) => {
|
||||
match old_comp {
|
||||
Ok(old_comp) => {
|
||||
write!(oldacc, "{old}", old = old_comp.red())?;
|
||||
},
|
||||
Err(ignored) => {
|
||||
write!(oldacc, "{ignored}")?;
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
diff::Result::Right(newc) => {
|
||||
write!(newacc, "{newc}", newc = newc.green()).unwrap();
|
||||
EitherOrBoth::Left(new_comp) => {
|
||||
match new_comp {
|
||||
Ok(new_comp) => {
|
||||
write!(newacc, "{new}", new = new_comp.green())?;
|
||||
},
|
||||
Err(ignored) => {
|
||||
write!(newacc, "{ignored}")?;
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
diff::Result::Both(oldc, newc) => {
|
||||
write!(oldacc, "{oldc}", oldc = oldc.yellow()).unwrap();
|
||||
write!(newacc, "{newc}", newc = newc.yellow()).unwrap();
|
||||
EitherOrBoth::Both(old_comp, new_comp) => {
|
||||
if let Err(ignored) = old_comp {
|
||||
write!(oldacc, "{ignored}")?;
|
||||
}
|
||||
|
||||
if let Err(ignored) = new_comp {
|
||||
write!(newacc, "{ignored}")?;
|
||||
}
|
||||
|
||||
if let (Ok(old_comp), Ok(new_comp)) = (old_comp, new_comp) {
|
||||
if old_comp == new_comp {
|
||||
write!(oldacc, "{old}", old = old_comp.yellow())?;
|
||||
write!(newacc, "{new}", new = new_comp.yellow())?;
|
||||
} else {
|
||||
write!(oldacc, "{old}", old = old_comp.red())?;
|
||||
write!(newacc, "{new}", new = new_comp.green())?;
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -111,31 +111,6 @@ fn real_main() -> Result<()> {
|
|||
paths_old.iter().map(|(_, path)| path),
|
||||
paths_new.iter().map(|(_, path)| path),
|
||||
)?;
|
||||
// let PackageDiff {
|
||||
// pkg_to_versions_pre: pre,
|
||||
// pkg_to_versions_post: post,
|
||||
// pre_keys: _,
|
||||
// post_keys: _,
|
||||
// added,
|
||||
// removed,
|
||||
// changed,
|
||||
// } = PackageDiff::new(&packages_old, &packages_after);
|
||||
|
||||
// log::debug!("Added packages: {}", added.len());
|
||||
// log::debug!("Removed packages: {}", removed.len());
|
||||
// log::debug!(
|
||||
// "Changed packages: {}",
|
||||
// changed
|
||||
// .iter()
|
||||
// .filter(|p| {
|
||||
// !p.is_empty()
|
||||
// && match (pre.get(*p), post.get(*p)) {
|
||||
// (Some(ver_pre), Some(ver_post)) => ver_pre != ver_post,
|
||||
// _ => false,
|
||||
// }
|
||||
// })
|
||||
// .count()
|
||||
// );
|
||||
|
||||
// println!("Difference between the two generations:");
|
||||
// println!();
|
||||
|
|
|
@ -20,15 +20,26 @@ impl PartialOrd for Version {
|
|||
|
||||
impl cmp::Ord for Version {
|
||||
fn cmp(&self, that: &Self) -> cmp::Ordering {
|
||||
let this = VersionComponentIter::from(&**self);
|
||||
let that = VersionComponentIter::from(&**that);
|
||||
let this = VersionComponentIter::from(&**self).filter_map(Result::ok);
|
||||
let that = VersionComponentIter::from(&**that).filter_map(Result::ok);
|
||||
|
||||
this.cmp(that)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
||||
enum VersionComponent<'a> {
|
||||
#[expect(clippy::into_iter_without_iter)]
|
||||
impl<'a> IntoIterator for &'a Version {
|
||||
type Item = Result<VersionComponent<'a>, &'a str>;
|
||||
|
||||
type IntoIter = VersionComponentIter<'a>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
VersionComponentIter::from(&**self)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Display, Debug, Clone, Copy, Eq, PartialEq)]
|
||||
pub enum VersionComponent<'a> {
|
||||
Number(u64),
|
||||
Text(&'a str),
|
||||
}
|
||||
|
@ -47,12 +58,12 @@ impl cmp::Ord for VersionComponent<'_> {
|
|||
};
|
||||
|
||||
match (*self, *other) {
|
||||
(Number(x), Number(y)) => x.cmp(&y),
|
||||
(Text(x), Text(y)) => {
|
||||
match (x, y) {
|
||||
(Number(this), Number(that)) => this.cmp(&that),
|
||||
(Text(this), Text(that)) => {
|
||||
match (this, that) {
|
||||
("pre", _) => cmp::Ordering::Less,
|
||||
(_, "pre") => cmp::Ordering::Greater,
|
||||
_ => x.cmp(y),
|
||||
_ => this.cmp(that),
|
||||
}
|
||||
},
|
||||
(Text(_), Number(_)) => cmp::Ordering::Less,
|
||||
|
@ -63,15 +74,16 @@ impl cmp::Ord for VersionComponent<'_> {
|
|||
|
||||
/// Yields [`VertionComponent`] from a version string.
|
||||
#[derive(Deref, DerefMut, From)]
|
||||
struct VersionComponentIter<'a>(&'a str);
|
||||
pub struct VersionComponentIter<'a>(&'a str);
|
||||
|
||||
impl<'a> Iterator for VersionComponentIter<'a> {
|
||||
type Item = VersionComponent<'a>;
|
||||
type Item = Result<VersionComponent<'a>, &'a str>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
// Skip all '-' and '.'.
|
||||
while self.starts_with(['.', '-']) {
|
||||
if self.starts_with(['.', '-']) {
|
||||
let ret = &self[..1];
|
||||
**self = &self[1..];
|
||||
return Some(Err(ret));
|
||||
}
|
||||
|
||||
// Get the next character and decide if it is a digit.
|
||||
|
@ -92,9 +104,13 @@ impl<'a> Iterator for VersionComponentIter<'a> {
|
|||
assert!(!component.is_empty());
|
||||
|
||||
if is_digit {
|
||||
component.parse::<u64>().ok().map(VersionComponent::Number)
|
||||
component
|
||||
.parse::<u64>()
|
||||
.ok()
|
||||
.map(VersionComponent::Number)
|
||||
.map(Ok)
|
||||
} else {
|
||||
Some(VersionComponent::Text(component))
|
||||
Some(Ok(VersionComponent::Text(component)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -113,14 +129,19 @@ mod tests {
|
|||
fn version_component_iter() {
|
||||
let version = "132.1.2test234-1-man----.--.......---------..---";
|
||||
|
||||
assert_eq!(VersionComponentIter::from(version).collect::<Vec<_>>(), [
|
||||
Number(132),
|
||||
Number(1),
|
||||
Number(2),
|
||||
Text("test"),
|
||||
Number(234),
|
||||
Number(1),
|
||||
Text("man")
|
||||
]);
|
||||
assert_eq!(
|
||||
VersionComponentIter::from(version)
|
||||
.filter_map(Result::ok)
|
||||
.collect::<Vec<_>>(),
|
||||
[
|
||||
Number(132),
|
||||
Number(1),
|
||||
Number(2),
|
||||
Text("test"),
|
||||
Number(234),
|
||||
Number(1),
|
||||
Text("man")
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue