1
Fork 0
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:
RGBCube 2025-05-09 15:52:19 +03:00 committed by bloxx12
parent 4f0ee04e19
commit a1ccf88d0c
3 changed files with 155 additions and 96 deletions

View file

@ -1,12 +1,12 @@
use std::{
fmt::{
use std::fmt::{
self,
Write as _,
},
sync,
};
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_version) => {
if newwrote {
write!(newacc, ", ")?;
} else {
write!(newacc, " ")?;
newwrote = true;
}
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::Both(old_version, new_version) => {
if old_version == new_version {
continue;
}
let old_version = old_version.unwrap_or(Version::ref_cast("<none>"));
let new_version = new_version.unwrap_or(Version::ref_cast("<none>"));
if oldwrote {
write!(oldacc, ", ")?;
} else {
write!(oldacc, " ")?;
oldwrote = true;
}
if newwrote {
write!(newacc, ", ")?;
} else {
write!(newacc, " ")?;
newwrote = true;
}
for diff in Itertools::zip_longest(
old_version.into_iter(),
new_version.into_iter(),
) {
match diff {
// I have no idea why itertools is returning `versions.new` in `Left`.
EitherOrBoth::Left(new) => {
let new = new.unwrap_or(Version::ref_cast("<none>"));
write!(newacc, " {new}", new = new.green())?;
EitherOrBoth::Right(old_comp) => {
match old_comp {
Ok(old_comp) => {
write!(oldacc, "{old}", old = old_comp.red())?;
},
Err(ignored) => {
write!(oldacc, "{ignored}")?;
},
}
},
EitherOrBoth::Right(old) => {
let old = old.unwrap_or(Version::ref_cast("<none>"));
write!(oldacc, " {old}", old = old.red())?;
EitherOrBoth::Left(new_comp) => {
match new_comp {
Ok(new_comp) => {
write!(newacc, "{new}", new = new_comp.green())?;
},
Err(ignored) => {
write!(newacc, "{ignored}")?;
},
}
},
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")
});
EitherOrBoth::Both(old_comp, new_comp) => {
if let Err(ignored) = old_comp {
write!(oldacc, "{ignored}")?;
}
let old = old.unwrap_or(Version::ref_cast("<none>"));
let new = new.unwrap_or(Version::ref_cast("<none>"));
if let Err(ignored) = new_comp {
write!(newacc, "{ignored}")?;
}
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) {
match diff {
diff::Result::Left(oldc) => {
write!(oldacc, "{oldc}", oldc = oldc.red()).unwrap();
},
diff::Result::Right(newc) => {
write!(newacc, "{newc}", newc = newc.green()).unwrap();
},
diff::Result::Both(oldc, newc) => {
write!(oldacc, "{oldc}", oldc = oldc.yellow()).unwrap();
write!(newacc, "{newc}", newc = newc.yellow()).unwrap();
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())?;
}
}
},
}
}

View file

@ -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!();

View file

@ -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,7 +129,11 @@ mod tests {
fn version_component_iter() {
let version = "132.1.2test234-1-man----.--.......---------..---";
assert_eq!(VersionComponentIter::from(version).collect::<Vec<_>>(), [
assert_eq!(
VersionComponentIter::from(version)
.filter_map(Result::ok)
.collect::<Vec<_>>(),
[
Number(132),
Number(1),
Number(2),
@ -121,6 +141,7 @@ mod tests {
Number(234),
Number(1),
Text("man")
]);
]
);
}
}