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
165
src/diff.rs
165
src/diff.rs
|
@ -1,12 +1,12 @@
|
||||||
use std::{
|
use std::fmt::{
|
||||||
fmt::{
|
|
||||||
self,
|
self,
|
||||||
Write as _,
|
Write as _,
|
||||||
},
|
|
||||||
sync,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use itertools::EitherOrBoth;
|
use itertools::{
|
||||||
|
EitherOrBoth,
|
||||||
|
Itertools,
|
||||||
|
};
|
||||||
use ref_cast::RefCast as _;
|
use ref_cast::RefCast as _;
|
||||||
use rustc_hash::{
|
use rustc_hash::{
|
||||||
FxBuildHasher,
|
FxBuildHasher,
|
||||||
|
@ -20,9 +20,7 @@ use crate::{
|
||||||
Version,
|
Version,
|
||||||
};
|
};
|
||||||
|
|
||||||
const HEADER_STYLE: yansi::Style = yansi::Style::new().bold().underline();
|
#[derive(Debug, Default)]
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
struct Diff<T> {
|
struct Diff<T> {
|
||||||
old: T,
|
old: T,
|
||||||
new: T,
|
new: T,
|
||||||
|
@ -110,9 +108,18 @@ pub fn diff<'a>(
|
||||||
for (name, versions, status) in diffs {
|
for (name, versions, status) in diffs {
|
||||||
if last_status != Some(status) {
|
if last_status != Some(status) {
|
||||||
last_status = Some(status);
|
last_status = Some(status);
|
||||||
HEADER_STYLE.fmt_prefix(writer)?;
|
|
||||||
writeln!(writer, "{status:?} packages:")?;
|
writeln!(
|
||||||
HEADER_STYLE.fmt_suffix(writer)?;
|
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!(
|
write!(
|
||||||
|
@ -122,56 +129,112 @@ pub fn diff<'a>(
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut oldacc = String::new();
|
let mut oldacc = String::new();
|
||||||
|
let mut oldwrote = false;
|
||||||
let mut newacc = String::new();
|
let mut newacc = String::new();
|
||||||
|
let mut newwrote = false;
|
||||||
|
|
||||||
for diff in itertools::Itertools::zip_longest(
|
for diff in Itertools::zip_longest(versions.old.iter(), versions.new.iter())
|
||||||
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 {
|
match diff {
|
||||||
// I have no idea why itertools is returning `versions.new` in `Left`.
|
EitherOrBoth::Right(old_comp) => {
|
||||||
EitherOrBoth::Left(new) => {
|
match old_comp {
|
||||||
let new = new.unwrap_or(Version::ref_cast("<none>"));
|
Ok(old_comp) => {
|
||||||
|
write!(oldacc, "{old}", old = old_comp.red())?;
|
||||||
write!(newacc, " {new}", new = new.green())?;
|
},
|
||||||
|
Err(ignored) => {
|
||||||
|
write!(oldacc, "{ignored}")?;
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
EitherOrBoth::Right(old) => {
|
EitherOrBoth::Left(new_comp) => {
|
||||||
let old = old.unwrap_or(Version::ref_cast("<none>"));
|
match new_comp {
|
||||||
|
Ok(new_comp) => {
|
||||||
write!(oldacc, " {old}", old = old.red())?;
|
write!(newacc, "{new}", new = new_comp.green())?;
|
||||||
|
},
|
||||||
|
Err(ignored) => {
|
||||||
|
write!(newacc, "{ignored}")?;
|
||||||
|
},
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
EitherOrBoth::Both(old, new) => {
|
EitherOrBoth::Both(old_comp, new_comp) => {
|
||||||
static NAME_SUFFIX_REGEX: sync::LazyLock<regex::Regex> =
|
if let Err(ignored) = old_comp {
|
||||||
sync::LazyLock::new(|| {
|
write!(oldacc, "{ignored}")?;
|
||||||
regex::Regex::new("(-man|-lib|-doc|-dev|-out|-terminfo)")
|
}
|
||||||
.expect("failed to compile regex for Nix store path versions")
|
|
||||||
});
|
|
||||||
|
|
||||||
let old = old.unwrap_or(Version::ref_cast("<none>"));
|
if let Err(ignored) = new_comp {
|
||||||
let new = new.unwrap_or(Version::ref_cast("<none>"));
|
write!(newacc, "{ignored}")?;
|
||||||
|
}
|
||||||
|
|
||||||
let suffix = NAME_SUFFIX_REGEX.captures(old).map_or("", |matches| {
|
if let (Ok(old_comp), Ok(new_comp)) = (old_comp, new_comp) {
|
||||||
matches.get(0).map_or("", |capture| capture.as_str())
|
if old_comp == new_comp {
|
||||||
});
|
write!(oldacc, "{old}", old = old_comp.yellow())?;
|
||||||
|
write!(newacc, "{new}", new = new_comp.yellow())?;
|
||||||
let old = old.strip_suffix(suffix).unwrap_or(old);
|
} else {
|
||||||
let new = new.strip_suffix(suffix).unwrap_or(new);
|
write!(oldacc, "{old}", old = old_comp.red())?;
|
||||||
|
write!(newacc, "{new}", new = new_comp.green())?;
|
||||||
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();
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
25
src/main.rs
25
src/main.rs
|
@ -111,31 +111,6 @@ fn real_main() -> Result<()> {
|
||||||
paths_old.iter().map(|(_, path)| path),
|
paths_old.iter().map(|(_, path)| path),
|
||||||
paths_new.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!("Difference between the two generations:");
|
||||||
// println!();
|
// println!();
|
||||||
|
|
|
@ -20,15 +20,26 @@ impl PartialOrd for Version {
|
||||||
|
|
||||||
impl cmp::Ord for Version {
|
impl cmp::Ord for Version {
|
||||||
fn cmp(&self, that: &Self) -> cmp::Ordering {
|
fn cmp(&self, that: &Self) -> cmp::Ordering {
|
||||||
let this = VersionComponentIter::from(&**self);
|
let this = VersionComponentIter::from(&**self).filter_map(Result::ok);
|
||||||
let that = VersionComponentIter::from(&**that);
|
let that = VersionComponentIter::from(&**that).filter_map(Result::ok);
|
||||||
|
|
||||||
this.cmp(that)
|
this.cmp(that)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
|
#[expect(clippy::into_iter_without_iter)]
|
||||||
enum VersionComponent<'a> {
|
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),
|
Number(u64),
|
||||||
Text(&'a str),
|
Text(&'a str),
|
||||||
}
|
}
|
||||||
|
@ -47,12 +58,12 @@ impl cmp::Ord for VersionComponent<'_> {
|
||||||
};
|
};
|
||||||
|
|
||||||
match (*self, *other) {
|
match (*self, *other) {
|
||||||
(Number(x), Number(y)) => x.cmp(&y),
|
(Number(this), Number(that)) => this.cmp(&that),
|
||||||
(Text(x), Text(y)) => {
|
(Text(this), Text(that)) => {
|
||||||
match (x, y) {
|
match (this, that) {
|
||||||
("pre", _) => cmp::Ordering::Less,
|
("pre", _) => cmp::Ordering::Less,
|
||||||
(_, "pre") => cmp::Ordering::Greater,
|
(_, "pre") => cmp::Ordering::Greater,
|
||||||
_ => x.cmp(y),
|
_ => this.cmp(that),
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(Text(_), Number(_)) => cmp::Ordering::Less,
|
(Text(_), Number(_)) => cmp::Ordering::Less,
|
||||||
|
@ -63,15 +74,16 @@ impl cmp::Ord for VersionComponent<'_> {
|
||||||
|
|
||||||
/// Yields [`VertionComponent`] from a version string.
|
/// Yields [`VertionComponent`] from a version string.
|
||||||
#[derive(Deref, DerefMut, From)]
|
#[derive(Deref, DerefMut, From)]
|
||||||
struct VersionComponentIter<'a>(&'a str);
|
pub struct VersionComponentIter<'a>(&'a str);
|
||||||
|
|
||||||
impl<'a> Iterator for VersionComponentIter<'a> {
|
impl<'a> Iterator for VersionComponentIter<'a> {
|
||||||
type Item = VersionComponent<'a>;
|
type Item = Result<VersionComponent<'a>, &'a str>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
// Skip all '-' and '.'.
|
if self.starts_with(['.', '-']) {
|
||||||
while self.starts_with(['.', '-']) {
|
let ret = &self[..1];
|
||||||
**self = &self[1..];
|
**self = &self[1..];
|
||||||
|
return Some(Err(ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the next character and decide if it is a digit.
|
// 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());
|
assert!(!component.is_empty());
|
||||||
|
|
||||||
if is_digit {
|
if is_digit {
|
||||||
component.parse::<u64>().ok().map(VersionComponent::Number)
|
component
|
||||||
|
.parse::<u64>()
|
||||||
|
.ok()
|
||||||
|
.map(VersionComponent::Number)
|
||||||
|
.map(Ok)
|
||||||
} else {
|
} else {
|
||||||
Some(VersionComponent::Text(component))
|
Some(Ok(VersionComponent::Text(component)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +129,11 @@ mod tests {
|
||||||
fn version_component_iter() {
|
fn version_component_iter() {
|
||||||
let version = "132.1.2test234-1-man----.--.......---------..---";
|
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(132),
|
||||||
Number(1),
|
Number(1),
|
||||||
Number(2),
|
Number(2),
|
||||||
|
@ -121,6 +141,7 @@ mod tests {
|
||||||
Number(234),
|
Number(234),
|
||||||
Number(1),
|
Number(1),
|
||||||
Text("man")
|
Text("man")
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue