├── .envrc ├── .gitignore ├── assets └── 2025-05-13-12-30-48.png ├── .github └── workflows │ └── tangled.yml ├── shell.nix ├── package.nix ├── flake.lock ├── flake.nix ├── Cargo.toml ├── README.md ├── LICENSE ├── src ├── versioning.rs ├── main.rs ├── parser.rs ├── package.rs └── diff.rs └── Cargo.lock /.envrc: -------------------------------------------------------------------------------- 1 | use flake 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | 3 | result 4 | 5 | .direnv/ 6 | -------------------------------------------------------------------------------- /assets/2025-05-13-12-30-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tgirlcloud/lix-diff/HEAD/assets/2025-05-13-12-30-48.png -------------------------------------------------------------------------------- /.github/workflows/tangled.yml: -------------------------------------------------------------------------------- 1 | name: Tangle 2 | 3 | on: 4 | workflow_dispatch: {} 5 | push: 6 | branches: 7 | - main 8 | 9 | jobs: 10 | tangle: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - uses: gwennlbh/to-tangled@v0.3 15 | with: 16 | repo: tgirl.cloud/lix-diff 17 | ssh-key: ${{ secrets.TANGLED_KEY }} 18 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | { 2 | mkShell, 3 | callPackage, 4 | rustPlatform, 5 | 6 | # extra tooling 7 | clippy, 8 | rustfmt, 9 | rust-analyzer, 10 | }: 11 | let 12 | defaultPackage = callPackage ./package.nix { }; 13 | in 14 | mkShell { 15 | inputsFrom = [ defaultPackage ]; 16 | 17 | env = { 18 | RUST_SRC_PATH = rustPlatform.rustLibSrc; 19 | }; 20 | 21 | packages = [ 22 | clippy 23 | rustfmt 24 | rust-analyzer 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /package.nix: -------------------------------------------------------------------------------- 1 | { lib, rustPlatform }: 2 | let 3 | toml = (lib.importTOML ./Cargo.toml).package; 4 | in 5 | rustPlatform.buildRustPackage { 6 | pname = "lix-diff"; 7 | inherit (toml) version; 8 | 9 | src = lib.fileset.toSource { 10 | root = ./.; 11 | fileset = lib.fileset.intersection (lib.fileset.fromSource (lib.sources.cleanSource ./.)) ( 12 | lib.fileset.unions [ 13 | ./Cargo.toml 14 | ./Cargo.lock 15 | ./src 16 | ] 17 | ); 18 | }; 19 | 20 | cargoLock.lockFile = ./Cargo.lock; 21 | 22 | meta = { 23 | inherit (toml) homepage description; 24 | license = lib.licenses.mit; 25 | maintainers = with lib.maintainers; [ isabelroses ]; 26 | mainProgram = "lix-diff"; 27 | }; 28 | } 29 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "nixpkgs": { 4 | "locked": { 5 | "lastModified": 1764856222, 6 | "narHash": "sha256-8dNVYhZx136KEKET8T7yr32PT/8y/Dsk1vucG5AmsmE=", 7 | "rev": "ece6e266caf1effab32eceef0403b797b4330373", 8 | "type": "tarball", 9 | "url": "https://releases.nixos.org/nixpkgs/nixpkgs-26.05pre906544.ece6e266caf1/nixexprs.tar.xz?lastModified=1764856222&rev=ece6e266caf1effab32eceef0403b797b4330373" 10 | }, 11 | "original": { 12 | "type": "tarball", 13 | "url": "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz" 14 | } 15 | }, 16 | "root": { 17 | "inputs": { 18 | "nixpkgs": "nixpkgs" 19 | } 20 | } 21 | }, 22 | "root": "root", 23 | "version": 7 24 | } 25 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | inputs.nixpkgs.url = "https://channels.nixos.org/nixpkgs-unstable/nixexprs.tar.xz"; 3 | 4 | outputs = 5 | { self, nixpkgs }: 6 | let 7 | forAllSystems = 8 | function: 9 | nixpkgs.lib.genAttrs nixpkgs.lib.systems.flakeExposed ( 10 | system: function nixpkgs.legacyPackages.${system} 11 | ); 12 | in 13 | { 14 | packages = forAllSystems (pkgs: { 15 | lix-diff = pkgs.callPackage ./package.nix { }; 16 | default = self.packages.${pkgs.stdenv.hostPlatform.system}.lix-diff; 17 | }); 18 | 19 | devShells = forAllSystems (pkgs: { 20 | default = pkgs.callPackage ./shell.nix { }; 21 | }); 22 | 23 | overlays.default = final: _: { lix-diff = final.callPackage ./package.nix { }; }; 24 | }; 25 | } 26 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lix-diff" 3 | version = "1.1.1" 4 | license = "MIT" 5 | description = "A lix plugin for diffing two generations" 6 | homepage = "https://github.com/tgirlcloud/lix-diff" 7 | authors = ["Isabel Roses "] 8 | edition = "2024" 9 | 10 | [dependencies] 11 | clap = { version = "4.5.47", features = ["derive"] } 12 | color-eyre = "0.6.5" 13 | humansize = "2.1.3" 14 | nu-ansi-term = "0.50.1" 15 | serde = { version = "1.0.223", features = ["derive"] } 16 | serde_json = "1.0.145" 17 | strip-ansi-escapes = "0.2.1" 18 | terminal-light = "1.8.0" 19 | 20 | [lints.clippy] 21 | all = "warn" 22 | pedantic = "warn" 23 | unreadable_literal = { level = "allow", priority = 1 } 24 | 25 | [profile.release] 26 | opt-level = "s" 27 | lto = "fat" 28 | codegen-units = 1 29 | panic = "abort" 30 | strip = true 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lix-diff 2 | 3 | 4 | ![example](assets/2025-05-13-12-30-48.png) 5 | 6 | ## Description 7 | 8 | This is a nix plugin built upon the [`lix`](https://lix.systems/) package manager. It 9 | is intended that the experimental feature `lix-custom-sub-commands` which 10 | provides access to the `lix` command which allows for custom sub-commands to be 11 | used. 12 | 13 | ## Installation 14 | 15 | Get it from nixpkgs: 16 | 17 | ```bash 18 | nix profile install nixpkgs#lix-diff 19 | ``` 20 | 21 | Or get it from the flake: 22 | 23 | ```bash 24 | nix profile install github:tgirlcloud/lix-diff 25 | ``` 26 | 27 | ## Usage 28 | 29 | The example below demonstrates the usage of the `lix diff` command. 30 | 31 | ```bash 32 | lix diff /nix/var/nix/profiles/system-95-link/ /run/current-system 33 | ``` 34 | 35 | 36 | Without the experimental feature enabled, the command can be called via 37 | 38 | ```bash 39 | lix-diff /nix/var/nix/profiles/system-95-link/ /run/current-system 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 tgirlcloud 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/versioning.rs: -------------------------------------------------------------------------------- 1 | use nu_ansi_term::Color::{Green, Red, Yellow}; 2 | use std::{cmp::Ordering, fmt::Display}; 3 | 4 | #[derive(Debug)] 5 | pub struct VersionComponent(String, Ordering); 6 | 7 | #[derive(Debug)] 8 | pub struct Version(Vec); 9 | 10 | #[derive(Debug)] 11 | pub struct VersionList(pub Vec); 12 | 13 | impl VersionComponent { 14 | pub fn new(version: String, ordering: Ordering) -> Self { 15 | Self(version, ordering) 16 | } 17 | } 18 | 19 | impl Version { 20 | pub fn new() -> Self { 21 | Self(Vec::new()) 22 | } 23 | 24 | pub fn push(&mut self, version: VersionComponent) { 25 | self.0.push(version); 26 | } 27 | } 28 | 29 | impl Display for Version { 30 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 31 | let mut out = String::new(); 32 | 33 | for component in &self.0 { 34 | let val = &component.0; 35 | let cmp = component.1; 36 | 37 | let text = if cmp == Ordering::Less { 38 | format!("{}", Red.paint(val)) 39 | } else if cmp == Ordering::Greater { 40 | format!("{}", Green.paint(val)) 41 | } else { 42 | format!("{}", Yellow.paint(val)) 43 | }; 44 | 45 | out.push_str(&text); 46 | out.push('.'); 47 | } 48 | 49 | out.pop(); // remove last comma 50 | write!(f, "{out}") 51 | } 52 | } 53 | 54 | impl VersionList { 55 | pub fn new() -> Self { 56 | Self(Vec::new()) 57 | } 58 | 59 | pub fn push(&mut self, version: Version) { 60 | self.0.push(version); 61 | } 62 | } 63 | 64 | impl Display for VersionList { 65 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 66 | let out = self 67 | .0 68 | .iter() 69 | .map(ToString::to_string) 70 | .collect::>() 71 | .join(", "); 72 | write!(f, "{out}") 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use clap::Parser; 4 | use color_eyre::Result; 5 | use diff::PackageListDiff; 6 | use nu_ansi_term::{Color, Style}; 7 | use terminal_light::luma; 8 | 9 | mod diff; 10 | mod package; 11 | mod parser; 12 | mod versioning; 13 | 14 | use self::parser::DiffRoot; 15 | 16 | #[derive(Parser, PartialEq, Debug)] 17 | /// List the package differences between two `NixOS` generations 18 | struct Args { 19 | /// the path to the lix bin directory 20 | #[arg(short, long)] 21 | lix_bin: Option, 22 | 23 | /// the generation we are switching from 24 | before: PathBuf, 25 | 26 | /// the generation we are switching to 27 | #[arg(default_value = "/run/current-system/")] 28 | after: PathBuf, 29 | 30 | /// sort by size difference 31 | #[arg(short, long)] 32 | size: bool, 33 | } 34 | 35 | fn main() -> Result<()> { 36 | let args = Args::parse(); 37 | let before = args.before; 38 | let after = args.after; 39 | let lix_bin = args.lix_bin; 40 | 41 | let mut lix_exe = None; 42 | if let Some(lix_bin) = lix_bin { 43 | lix_exe = if lix_bin.is_dir() { 44 | Some(lix_bin.join("nix")) 45 | } else { 46 | Some(lix_bin) 47 | } 48 | } 49 | 50 | if !before.exists() { 51 | eprintln!("Before generation does not exist: {}", before.display()); 52 | std::process::exit(1); 53 | } 54 | 55 | if !after.exists() { 56 | eprintln!("After generation does not exist: {}", after.display()); 57 | std::process::exit(1); 58 | } 59 | 60 | let packages_diff = DiffRoot::new(lix_exe, &before, &after)?; 61 | let mut packages: PackageListDiff = PackageListDiff::new(); 62 | packages.by_size = args.size; 63 | packages.from_diff_root(packages_diff); 64 | 65 | let text_color = if luma().is_ok_and(|luma| luma > 0.6) { 66 | Color::DarkGray 67 | } else { 68 | Color::LightGray 69 | }; 70 | let arrow_style = Style::new().bold().fg(text_color); 71 | 72 | let before_text = format!("<<< {}", before.display()); 73 | let after_text = format!(">>> {}", after.display()); 74 | 75 | println!("{}", arrow_style.paint(before_text)); 76 | println!("{}\n", arrow_style.paint(after_text)); 77 | 78 | println!("{packages}"); 79 | 80 | Ok(()) 81 | } 82 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use color_eyre::Result; 2 | use serde::de::Deserializer; 3 | use serde::Deserialize; 4 | use std::{ 5 | borrow::Cow, 6 | collections::BTreeMap, 7 | path::{Path, PathBuf}, 8 | process::Command, 9 | }; 10 | 11 | #[derive(Deserialize, Debug)] 12 | pub struct DiffRoot { 13 | pub packages: BTreeMap, 14 | 15 | #[expect(dead_code)] 16 | pub schema: String, 17 | } 18 | 19 | #[derive(Deserialize, Debug)] 20 | #[serde(rename_all = "camelCase")] 21 | pub struct DiffPackage { 22 | pub size_delta: i64, 23 | 24 | #[serde(deserialize_with = "version_deserializer")] 25 | pub versions_before: Vec, 26 | 27 | #[serde(deserialize_with = "version_deserializer")] 28 | pub versions_after: Vec, 29 | } 30 | 31 | fn version_deserializer<'de, D>(deserializer: D) -> Result, D::Error> 32 | where 33 | D: Deserializer<'de>, 34 | { 35 | let vec = Vec::>::deserialize(deserializer)?; 36 | Ok(vec 37 | .into_iter() 38 | .map(|s| { 39 | if s.is_empty() { 40 | "".to_string() 41 | } else { 42 | s.into_owned() 43 | } 44 | }) 45 | .collect()) 46 | } 47 | 48 | impl DiffRoot { 49 | pub fn new(lix_path: Option, before: &Path, after: &Path) -> Result { 50 | let lix_exe; 51 | if let Some(lix_path) = lix_path { 52 | lix_exe = lix_path; 53 | } else { 54 | lix_exe = "nix".into(); 55 | } 56 | 57 | let raw_diff = Command::new(lix_exe) 58 | .args(["store", "diff-closures", "--json"]) 59 | .args([before, after]) 60 | .output()?; 61 | 62 | if !raw_diff.status.success() { 63 | eprintln!("{}", String::from_utf8_lossy(&raw_diff.stderr)); 64 | std::process::exit(1); 65 | } 66 | 67 | let stdout = raw_diff.stdout; 68 | if stdout.is_empty() { 69 | eprintln!("No differences found."); 70 | std::process::exit(0); 71 | } 72 | 73 | // Assume nix output is valid UTF-8 74 | let diff_out = String::from_utf8(stdout)?; 75 | 76 | let diff_root = serde_json::from_str::(&diff_out).map_err(|e| { 77 | eprintln!("Failed to parse JSON: {e}"); 78 | std::process::exit(1); 79 | })?; 80 | 81 | Ok(diff_root) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/package.rs: -------------------------------------------------------------------------------- 1 | use humansize::{format_size, DECIMAL}; 2 | use std::{cmp::Ordering, fmt::Display}; 3 | 4 | use super::{ 5 | parser::DiffPackage, 6 | versioning::{Version, VersionComponent, VersionList}, 7 | }; 8 | 9 | #[derive(Default, Debug, PartialEq, Eq)] 10 | pub enum DiffType { 11 | Added, 12 | Removed, 13 | Changed, 14 | 15 | #[default] 16 | Unknown, 17 | } 18 | 19 | #[derive(Debug)] 20 | pub struct Package { 21 | pub size_delta: SizeDelta, 22 | pub diff_type: DiffType, 23 | 24 | pub versions_before: VersionList, 25 | pub versions_after: VersionList, 26 | } 27 | 28 | #[derive(Debug)] 29 | pub struct SizeDelta(pub i64); 30 | 31 | impl DiffType { 32 | pub fn from_versions(before: &[String], after: &[String]) -> DiffType { 33 | match (before.is_empty(), after.is_empty()) { 34 | (true, false) => DiffType::Added, 35 | (false, true) => DiffType::Removed, 36 | (false, false) => DiffType::Changed, 37 | (true, true) => DiffType::Unknown, // should be unreachable but I'm not sure 38 | } 39 | } 40 | } 41 | 42 | impl Display for Package { 43 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 44 | match self.diff_type { 45 | DiffType::Added => { 46 | write!(f, "{}", self.versions_after)?; 47 | } 48 | DiffType::Removed => { 49 | write!(f, "{}", self.versions_before)?; 50 | } 51 | DiffType::Changed => { 52 | write!(f, "{} -> {}", self.versions_before, self.versions_after)?; 53 | } 54 | DiffType::Unknown => unreachable!(), 55 | } 56 | 57 | Ok(()) 58 | } 59 | } 60 | 61 | impl From for Package { 62 | fn from(diff: DiffPackage) -> Self { 63 | let diff_type = DiffType::from_versions(&diff.versions_before, &diff.versions_after); 64 | 65 | let (parsed_before, parsed_after) = match diff_type { 66 | DiffType::Added => handle_diff_added(&diff.versions_after), 67 | DiffType::Removed => handle_diff_removed(&diff.versions_before), 68 | DiffType::Changed => handle_diff_changed(&diff.versions_before, &diff.versions_after), 69 | DiffType::Unknown => unreachable!(), 70 | }; 71 | 72 | Package { 73 | size_delta: diff.size_delta.into(), 74 | versions_before: parsed_before, 75 | versions_after: parsed_after, 76 | diff_type, 77 | } 78 | } 79 | } 80 | 81 | fn handle_diff_added(versions_after: &[String]) -> (VersionList, VersionList) { 82 | let after = to_version_list(versions_after, Ordering::Greater); 83 | (VersionList::new(), after) 84 | } 85 | 86 | fn handle_diff_removed(versions_before: &[String]) -> (VersionList, VersionList) { 87 | let before = to_version_list(versions_before, Ordering::Less); 88 | (before, VersionList::new()) 89 | } 90 | 91 | fn to_version_list(versions: &[String], order: Ordering) -> VersionList { 92 | let mut version_list = VersionList::new(); 93 | 94 | for before in versions { 95 | let parts_before = before.split('.').map(String::from); 96 | let mut version = Version::new(); 97 | for part in parts_before { 98 | version.push(VersionComponent::new(part, order)); 99 | } 100 | version_list.push(version); 101 | } 102 | 103 | version_list 104 | } 105 | 106 | fn handle_diff_changed( 107 | versions_before: &[String], 108 | versions_after: &[String], 109 | ) -> (VersionList, VersionList) { 110 | let mut parsed_before = VersionList::new(); 111 | let mut parsed_after = VersionList::new(); 112 | 113 | for (before, after) in versions_before.iter().zip(versions_after.iter()) { 114 | let mut parts_before = before.split('.').map(String::from).collect::>(); 115 | let mut parts_after = after.split('.').map(String::from).collect::>(); 116 | 117 | let max_len = parts_before.len().max(parts_after.len()); 118 | parts_before.resize(max_len, String::new()); 119 | parts_after.resize(max_len, String::new()); 120 | 121 | let mut ordering = Ordering::Equal; 122 | 123 | let mut line_before = Version::new(); 124 | let mut line_after = Version::new(); 125 | 126 | for (b, a) in parts_before.into_iter().zip(parts_after.into_iter()) { 127 | if ordering == Ordering::Equal { 128 | ordering = b.cmp(&a); 129 | } 130 | line_before.push(VersionComponent::new(b, ordering)); 131 | line_after.push(VersionComponent::new(a, ordering.reverse())); 132 | } 133 | 134 | parsed_before.push(line_before); 135 | parsed_after.push(line_after); 136 | } 137 | 138 | (parsed_before, parsed_after) 139 | } 140 | 141 | impl std::fmt::Display for SizeDelta { 142 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 143 | let sign = if self.0 >= 0 { "+" } else { "-" }; 144 | let size: u64 = self.0.abs().try_into().unwrap_or(0); 145 | write!(f, "{sign}{}", format_size(size, DECIMAL)) 146 | } 147 | } 148 | 149 | impl From for SizeDelta { 150 | fn from(size: i64) -> Self { 151 | SizeDelta(size) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /src/diff.rs: -------------------------------------------------------------------------------- 1 | use nu_ansi_term::Color::{self, Green, Red, Yellow}; 2 | use std::collections::BTreeMap; 3 | use terminal_light::luma; 4 | 5 | use super::{ 6 | package::{DiffType, Package, SizeDelta}, 7 | parser::DiffRoot, 8 | }; 9 | 10 | #[derive(Debug)] 11 | pub struct PackageExtra { 12 | name: String, 13 | base_package: Package, 14 | } 15 | 16 | #[derive(Debug)] 17 | pub struct PackageListDiff { 18 | all: Vec, 19 | added: BTreeMap, 20 | removed: BTreeMap, 21 | changed: BTreeMap, 22 | size_delta: SizeDelta, 23 | longest_name: usize, 24 | 25 | // Whether to sort by size difference when displaying 26 | pub by_size: bool, 27 | } 28 | 29 | impl PackageListDiff { 30 | pub fn new() -> Self { 31 | PackageListDiff { 32 | all: Vec::new(), 33 | added: BTreeMap::new(), 34 | removed: BTreeMap::new(), 35 | changed: BTreeMap::new(), 36 | size_delta: SizeDelta(0), 37 | longest_name: 0, 38 | by_size: false, 39 | } 40 | } 41 | } 42 | 43 | impl std::fmt::Display for PackageListDiff { 44 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 45 | if self.by_size { 46 | self.display_by_size(f)?; 47 | } else { 48 | if self.added.is_empty() && self.removed.is_empty() && self.changed.is_empty() { 49 | return write!(f, "No differences found."); 50 | } 51 | 52 | self.display_by_category(f)?; 53 | } 54 | 55 | { 56 | let delta = &self.size_delta; 57 | writeln!(f, "size diff: {delta}")?; 58 | } 59 | 60 | Ok(()) 61 | } 62 | } 63 | 64 | impl PackageListDiff { 65 | fn display_by_size(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 66 | let all = { 67 | let mut v: Vec<_> = self.all.iter().collect(); 68 | v.sort_by_key(|pkg| -pkg.base_package.size_delta.0); 69 | v 70 | }; 71 | 72 | let name_width = self.longest_name + 2; 73 | let package_width = (all 74 | .iter() 75 | .map(|pkg| strip_ansi_escapes::strip(format!("{}", pkg.base_package)).len()) 76 | .max() 77 | .expect("At least one package exists")) 78 | .min(120); 79 | 80 | for package in &all { 81 | let name = &package.name; 82 | let package = &package.base_package; 83 | let delta = &package.size_delta; 84 | let versions = format!("{package}"); 85 | let visual_len = strip_ansi_escapes::strip(&versions).len(); 86 | let padding = " ".repeat(package_width.saturating_sub(visual_len)); 87 | writeln!(f, "{name:name_width$}{versions}{padding} {delta}")?; 88 | } 89 | 90 | writeln!(f)?; 91 | 92 | Ok(()) 93 | } 94 | 95 | fn display_by_category(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 96 | let text_color = if luma().is_ok_and(|luma| luma > 0.6) { 97 | Color::DarkGray 98 | } else { 99 | Color::LightGray 100 | }; 101 | let title_style = nu_ansi_term::Style::new().underline().bold().fg(text_color); 102 | 103 | let name_width = self.longest_name + 2; 104 | 105 | if !self.changed.is_empty() { 106 | writeln!(f, "{}", &title_style.paint("Changed"))?; 107 | for (name, package) in &self.changed { 108 | writeln!(f, "[{}] {name:name_width$}{package}", Yellow.paint("C"))?; 109 | } 110 | writeln!(f)?; 111 | } 112 | 113 | if !self.added.is_empty() { 114 | writeln!(f, "{}", &title_style.paint("Added"))?; 115 | for (name, package) in &self.added { 116 | write!(f, "[{}] {name:name_width$}{package}", Green.paint("A"))?; 117 | writeln!(f)?; 118 | } 119 | writeln!(f)?; 120 | } 121 | 122 | if !self.removed.is_empty() { 123 | writeln!(f, "{}", &title_style.paint("Removed"))?; 124 | for (name, package) in &self.removed { 125 | writeln!(f, "[{}] {name:name_width$}{package}", Red.paint("R"))?; 126 | } 127 | writeln!(f)?; 128 | } 129 | 130 | Ok(()) 131 | } 132 | 133 | #[allow(clippy::wrong_self_convention)] 134 | pub fn from_diff_root(&mut self, diff_root: DiffRoot) { 135 | for (name, diff_package) in diff_root.packages { 136 | let package = Package::from(diff_package); 137 | 138 | self.size_delta.0 += package.size_delta.0; 139 | self.longest_name = self.longest_name.max(name.len()); 140 | 141 | if self.by_size { 142 | self.all.push(PackageExtra { 143 | name, 144 | base_package: package, 145 | }); 146 | 147 | continue; 148 | } 149 | 150 | match package.diff_type { 151 | DiffType::Added => { 152 | self.added.insert(name, package); 153 | } 154 | DiffType::Removed => { 155 | self.removed.insert(name, package); 156 | } 157 | DiffType::Changed => { 158 | self.changed.insert(name, package); 159 | } 160 | DiffType::Unknown => { 161 | // This should never happen, but just in case 162 | eprintln!("Unknown diff type for package: {name}"); 163 | } 164 | } 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 4 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.25.1" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.1" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" 19 | 20 | [[package]] 21 | name = "anstream" 22 | version = "0.6.21" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" 25 | dependencies = [ 26 | "anstyle", 27 | "anstyle-parse", 28 | "anstyle-query", 29 | "anstyle-wincon", 30 | "colorchoice", 31 | "is_terminal_polyfill", 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle" 37 | version = "1.0.13" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" 40 | 41 | [[package]] 42 | name = "anstyle-parse" 43 | version = "0.2.7" 44 | source = "registry+https://github.com/rust-lang/crates.io-index" 45 | checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" 46 | dependencies = [ 47 | "utf8parse", 48 | ] 49 | 50 | [[package]] 51 | name = "anstyle-query" 52 | version = "1.1.5" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" 55 | dependencies = [ 56 | "windows-sys", 57 | ] 58 | 59 | [[package]] 60 | name = "anstyle-wincon" 61 | version = "3.0.11" 62 | source = "registry+https://github.com/rust-lang/crates.io-index" 63 | checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" 64 | dependencies = [ 65 | "anstyle", 66 | "once_cell_polyfill", 67 | "windows-sys", 68 | ] 69 | 70 | [[package]] 71 | name = "backtrace" 72 | version = "0.3.76" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" 75 | dependencies = [ 76 | "addr2line", 77 | "cfg-if", 78 | "libc", 79 | "miniz_oxide", 80 | "object", 81 | "rustc-demangle", 82 | "windows-link", 83 | ] 84 | 85 | [[package]] 86 | name = "bitflags" 87 | version = "2.10.0" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" 90 | 91 | [[package]] 92 | name = "cfg-if" 93 | version = "1.0.4" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 96 | 97 | [[package]] 98 | name = "cfg_aliases" 99 | version = "0.2.1" 100 | source = "registry+https://github.com/rust-lang/crates.io-index" 101 | checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" 102 | 103 | [[package]] 104 | name = "clap" 105 | version = "4.5.53" 106 | source = "registry+https://github.com/rust-lang/crates.io-index" 107 | checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" 108 | dependencies = [ 109 | "clap_builder", 110 | "clap_derive", 111 | ] 112 | 113 | [[package]] 114 | name = "clap_builder" 115 | version = "4.5.53" 116 | source = "registry+https://github.com/rust-lang/crates.io-index" 117 | checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" 118 | dependencies = [ 119 | "anstream", 120 | "anstyle", 121 | "clap_lex", 122 | "strsim", 123 | ] 124 | 125 | [[package]] 126 | name = "clap_derive" 127 | version = "4.5.49" 128 | source = "registry+https://github.com/rust-lang/crates.io-index" 129 | checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" 130 | dependencies = [ 131 | "heck", 132 | "proc-macro2", 133 | "quote", 134 | "syn", 135 | ] 136 | 137 | [[package]] 138 | name = "clap_lex" 139 | version = "0.7.6" 140 | source = "registry+https://github.com/rust-lang/crates.io-index" 141 | checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" 142 | 143 | [[package]] 144 | name = "color-eyre" 145 | version = "0.6.5" 146 | source = "registry+https://github.com/rust-lang/crates.io-index" 147 | checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" 148 | dependencies = [ 149 | "backtrace", 150 | "color-spantrace", 151 | "eyre", 152 | "indenter", 153 | "once_cell", 154 | "owo-colors", 155 | "tracing-error", 156 | ] 157 | 158 | [[package]] 159 | name = "color-spantrace" 160 | version = "0.3.0" 161 | source = "registry+https://github.com/rust-lang/crates.io-index" 162 | checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" 163 | dependencies = [ 164 | "once_cell", 165 | "owo-colors", 166 | "tracing-core", 167 | "tracing-error", 168 | ] 169 | 170 | [[package]] 171 | name = "colorchoice" 172 | version = "1.0.4" 173 | source = "registry+https://github.com/rust-lang/crates.io-index" 174 | checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" 175 | 176 | [[package]] 177 | name = "convert_case" 178 | version = "0.10.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" 181 | dependencies = [ 182 | "unicode-segmentation", 183 | ] 184 | 185 | [[package]] 186 | name = "coolor" 187 | version = "1.1.0" 188 | source = "registry+https://github.com/rust-lang/crates.io-index" 189 | checksum = "980c2afde4af43d6a05c5be738f9eae595cff86dce1f38f88b95058a98c027f3" 190 | 191 | [[package]] 192 | name = "crossterm" 193 | version = "0.29.0" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" 196 | dependencies = [ 197 | "bitflags", 198 | "crossterm_winapi", 199 | "derive_more", 200 | "document-features", 201 | "mio", 202 | "parking_lot", 203 | "rustix", 204 | "signal-hook", 205 | "signal-hook-mio", 206 | "winapi", 207 | ] 208 | 209 | [[package]] 210 | name = "crossterm_winapi" 211 | version = "0.9.1" 212 | source = "registry+https://github.com/rust-lang/crates.io-index" 213 | checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" 214 | dependencies = [ 215 | "winapi", 216 | ] 217 | 218 | [[package]] 219 | name = "derive_more" 220 | version = "2.1.0" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "10b768e943bed7bf2cab53df09f4bc34bfd217cdb57d971e769874c9a6710618" 223 | dependencies = [ 224 | "derive_more-impl", 225 | ] 226 | 227 | [[package]] 228 | name = "derive_more-impl" 229 | version = "2.1.0" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "6d286bfdaf75e988b4a78e013ecd79c581e06399ab53fbacd2d916c2f904f30b" 232 | dependencies = [ 233 | "convert_case", 234 | "proc-macro2", 235 | "quote", 236 | "rustc_version", 237 | "syn", 238 | ] 239 | 240 | [[package]] 241 | name = "document-features" 242 | version = "0.2.12" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" 245 | dependencies = [ 246 | "litrs", 247 | ] 248 | 249 | [[package]] 250 | name = "errno" 251 | version = "0.3.14" 252 | source = "registry+https://github.com/rust-lang/crates.io-index" 253 | checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" 254 | dependencies = [ 255 | "libc", 256 | "windows-sys", 257 | ] 258 | 259 | [[package]] 260 | name = "eyre" 261 | version = "0.6.12" 262 | source = "registry+https://github.com/rust-lang/crates.io-index" 263 | checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" 264 | dependencies = [ 265 | "indenter", 266 | "once_cell", 267 | ] 268 | 269 | [[package]] 270 | name = "gimli" 271 | version = "0.32.3" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" 274 | 275 | [[package]] 276 | name = "heck" 277 | version = "0.5.0" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 280 | 281 | [[package]] 282 | name = "humansize" 283 | version = "2.1.3" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" 286 | dependencies = [ 287 | "libm", 288 | ] 289 | 290 | [[package]] 291 | name = "indenter" 292 | version = "0.3.4" 293 | source = "registry+https://github.com/rust-lang/crates.io-index" 294 | checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" 295 | 296 | [[package]] 297 | name = "is_terminal_polyfill" 298 | version = "1.70.2" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" 301 | 302 | [[package]] 303 | name = "itoa" 304 | version = "1.0.15" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" 307 | 308 | [[package]] 309 | name = "lazy_static" 310 | version = "1.5.0" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 313 | 314 | [[package]] 315 | name = "libc" 316 | version = "0.2.178" 317 | source = "registry+https://github.com/rust-lang/crates.io-index" 318 | checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" 319 | 320 | [[package]] 321 | name = "libm" 322 | version = "0.2.15" 323 | source = "registry+https://github.com/rust-lang/crates.io-index" 324 | checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" 325 | 326 | [[package]] 327 | name = "linux-raw-sys" 328 | version = "0.11.0" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" 331 | 332 | [[package]] 333 | name = "litrs" 334 | version = "1.0.0" 335 | source = "registry+https://github.com/rust-lang/crates.io-index" 336 | checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" 337 | 338 | [[package]] 339 | name = "lix-diff" 340 | version = "1.1.1" 341 | dependencies = [ 342 | "clap", 343 | "color-eyre", 344 | "humansize", 345 | "nu-ansi-term", 346 | "serde", 347 | "serde_json", 348 | "strip-ansi-escapes", 349 | "terminal-light", 350 | ] 351 | 352 | [[package]] 353 | name = "lock_api" 354 | version = "0.4.14" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" 357 | dependencies = [ 358 | "scopeguard", 359 | ] 360 | 361 | [[package]] 362 | name = "log" 363 | version = "0.4.29" 364 | source = "registry+https://github.com/rust-lang/crates.io-index" 365 | checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" 366 | 367 | [[package]] 368 | name = "memchr" 369 | version = "2.7.6" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 372 | 373 | [[package]] 374 | name = "miniz_oxide" 375 | version = "0.8.9" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" 378 | dependencies = [ 379 | "adler2", 380 | ] 381 | 382 | [[package]] 383 | name = "mio" 384 | version = "1.1.1" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" 387 | dependencies = [ 388 | "libc", 389 | "log", 390 | "wasi", 391 | "windows-sys", 392 | ] 393 | 394 | [[package]] 395 | name = "nix" 396 | version = "0.29.0" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" 399 | dependencies = [ 400 | "bitflags", 401 | "cfg-if", 402 | "cfg_aliases", 403 | "libc", 404 | ] 405 | 406 | [[package]] 407 | name = "nu-ansi-term" 408 | version = "0.50.3" 409 | source = "registry+https://github.com/rust-lang/crates.io-index" 410 | checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" 411 | dependencies = [ 412 | "windows-sys", 413 | ] 414 | 415 | [[package]] 416 | name = "object" 417 | version = "0.37.3" 418 | source = "registry+https://github.com/rust-lang/crates.io-index" 419 | checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" 420 | dependencies = [ 421 | "memchr", 422 | ] 423 | 424 | [[package]] 425 | name = "once_cell" 426 | version = "1.21.3" 427 | source = "registry+https://github.com/rust-lang/crates.io-index" 428 | checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" 429 | 430 | [[package]] 431 | name = "once_cell_polyfill" 432 | version = "1.70.2" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" 435 | 436 | [[package]] 437 | name = "owo-colors" 438 | version = "4.2.3" 439 | source = "registry+https://github.com/rust-lang/crates.io-index" 440 | checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" 441 | 442 | [[package]] 443 | name = "parking_lot" 444 | version = "0.12.5" 445 | source = "registry+https://github.com/rust-lang/crates.io-index" 446 | checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" 447 | dependencies = [ 448 | "lock_api", 449 | "parking_lot_core", 450 | ] 451 | 452 | [[package]] 453 | name = "parking_lot_core" 454 | version = "0.9.12" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" 457 | dependencies = [ 458 | "cfg-if", 459 | "libc", 460 | "redox_syscall", 461 | "smallvec", 462 | "windows-link", 463 | ] 464 | 465 | [[package]] 466 | name = "pin-project-lite" 467 | version = "0.2.16" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" 470 | 471 | [[package]] 472 | name = "proc-macro2" 473 | version = "1.0.103" 474 | source = "registry+https://github.com/rust-lang/crates.io-index" 475 | checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" 476 | dependencies = [ 477 | "unicode-ident", 478 | ] 479 | 480 | [[package]] 481 | name = "quote" 482 | version = "1.0.42" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" 485 | dependencies = [ 486 | "proc-macro2", 487 | ] 488 | 489 | [[package]] 490 | name = "redox_syscall" 491 | version = "0.5.18" 492 | source = "registry+https://github.com/rust-lang/crates.io-index" 493 | checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" 494 | dependencies = [ 495 | "bitflags", 496 | ] 497 | 498 | [[package]] 499 | name = "rustc-demangle" 500 | version = "0.1.26" 501 | source = "registry+https://github.com/rust-lang/crates.io-index" 502 | checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" 503 | 504 | [[package]] 505 | name = "rustc_version" 506 | version = "0.4.1" 507 | source = "registry+https://github.com/rust-lang/crates.io-index" 508 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 509 | dependencies = [ 510 | "semver", 511 | ] 512 | 513 | [[package]] 514 | name = "rustix" 515 | version = "1.1.2" 516 | source = "registry+https://github.com/rust-lang/crates.io-index" 517 | checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" 518 | dependencies = [ 519 | "bitflags", 520 | "errno", 521 | "libc", 522 | "linux-raw-sys", 523 | "windows-sys", 524 | ] 525 | 526 | [[package]] 527 | name = "ryu" 528 | version = "1.0.20" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" 531 | 532 | [[package]] 533 | name = "scopeguard" 534 | version = "1.2.0" 535 | source = "registry+https://github.com/rust-lang/crates.io-index" 536 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 537 | 538 | [[package]] 539 | name = "semver" 540 | version = "1.0.27" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" 543 | 544 | [[package]] 545 | name = "serde" 546 | version = "1.0.228" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" 549 | dependencies = [ 550 | "serde_core", 551 | "serde_derive", 552 | ] 553 | 554 | [[package]] 555 | name = "serde_core" 556 | version = "1.0.228" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" 559 | dependencies = [ 560 | "serde_derive", 561 | ] 562 | 563 | [[package]] 564 | name = "serde_derive" 565 | version = "1.0.228" 566 | source = "registry+https://github.com/rust-lang/crates.io-index" 567 | checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" 568 | dependencies = [ 569 | "proc-macro2", 570 | "quote", 571 | "syn", 572 | ] 573 | 574 | [[package]] 575 | name = "serde_json" 576 | version = "1.0.145" 577 | source = "registry+https://github.com/rust-lang/crates.io-index" 578 | checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" 579 | dependencies = [ 580 | "itoa", 581 | "memchr", 582 | "ryu", 583 | "serde", 584 | "serde_core", 585 | ] 586 | 587 | [[package]] 588 | name = "sharded-slab" 589 | version = "0.1.7" 590 | source = "registry+https://github.com/rust-lang/crates.io-index" 591 | checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" 592 | dependencies = [ 593 | "lazy_static", 594 | ] 595 | 596 | [[package]] 597 | name = "signal-hook" 598 | version = "0.3.18" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" 601 | dependencies = [ 602 | "libc", 603 | "signal-hook-registry", 604 | ] 605 | 606 | [[package]] 607 | name = "signal-hook-mio" 608 | version = "0.2.5" 609 | source = "registry+https://github.com/rust-lang/crates.io-index" 610 | checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc" 611 | dependencies = [ 612 | "libc", 613 | "mio", 614 | "signal-hook", 615 | ] 616 | 617 | [[package]] 618 | name = "signal-hook-registry" 619 | version = "1.4.7" 620 | source = "registry+https://github.com/rust-lang/crates.io-index" 621 | checksum = "7664a098b8e616bdfcc2dc0e9ac44eb231eedf41db4e9fe95d8d32ec728dedad" 622 | dependencies = [ 623 | "libc", 624 | ] 625 | 626 | [[package]] 627 | name = "smallvec" 628 | version = "1.15.1" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" 631 | 632 | [[package]] 633 | name = "strip-ansi-escapes" 634 | version = "0.2.1" 635 | source = "registry+https://github.com/rust-lang/crates.io-index" 636 | checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025" 637 | dependencies = [ 638 | "vte", 639 | ] 640 | 641 | [[package]] 642 | name = "strsim" 643 | version = "0.11.1" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 646 | 647 | [[package]] 648 | name = "syn" 649 | version = "2.0.111" 650 | source = "registry+https://github.com/rust-lang/crates.io-index" 651 | checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" 652 | dependencies = [ 653 | "proc-macro2", 654 | "quote", 655 | "unicode-ident", 656 | ] 657 | 658 | [[package]] 659 | name = "terminal-light" 660 | version = "1.8.0" 661 | source = "registry+https://github.com/rust-lang/crates.io-index" 662 | checksum = "a6f76be906d875a0ce764c52a055858c24847cb7dc674d3a5ad8cf7e3dd4ee9f" 663 | dependencies = [ 664 | "coolor", 665 | "crossterm", 666 | "thiserror", 667 | "xterm-query", 668 | ] 669 | 670 | [[package]] 671 | name = "thiserror" 672 | version = "1.0.69" 673 | source = "registry+https://github.com/rust-lang/crates.io-index" 674 | checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" 675 | dependencies = [ 676 | "thiserror-impl", 677 | ] 678 | 679 | [[package]] 680 | name = "thiserror-impl" 681 | version = "1.0.69" 682 | source = "registry+https://github.com/rust-lang/crates.io-index" 683 | checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" 684 | dependencies = [ 685 | "proc-macro2", 686 | "quote", 687 | "syn", 688 | ] 689 | 690 | [[package]] 691 | name = "thread_local" 692 | version = "1.1.9" 693 | source = "registry+https://github.com/rust-lang/crates.io-index" 694 | checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" 695 | dependencies = [ 696 | "cfg-if", 697 | ] 698 | 699 | [[package]] 700 | name = "tracing" 701 | version = "0.1.43" 702 | source = "registry+https://github.com/rust-lang/crates.io-index" 703 | checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" 704 | dependencies = [ 705 | "pin-project-lite", 706 | "tracing-core", 707 | ] 708 | 709 | [[package]] 710 | name = "tracing-core" 711 | version = "0.1.35" 712 | source = "registry+https://github.com/rust-lang/crates.io-index" 713 | checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" 714 | dependencies = [ 715 | "once_cell", 716 | "valuable", 717 | ] 718 | 719 | [[package]] 720 | name = "tracing-error" 721 | version = "0.2.1" 722 | source = "registry+https://github.com/rust-lang/crates.io-index" 723 | checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" 724 | dependencies = [ 725 | "tracing", 726 | "tracing-subscriber", 727 | ] 728 | 729 | [[package]] 730 | name = "tracing-subscriber" 731 | version = "0.3.22" 732 | source = "registry+https://github.com/rust-lang/crates.io-index" 733 | checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" 734 | dependencies = [ 735 | "sharded-slab", 736 | "thread_local", 737 | "tracing-core", 738 | ] 739 | 740 | [[package]] 741 | name = "unicode-ident" 742 | version = "1.0.22" 743 | source = "registry+https://github.com/rust-lang/crates.io-index" 744 | checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" 745 | 746 | [[package]] 747 | name = "unicode-segmentation" 748 | version = "1.12.0" 749 | source = "registry+https://github.com/rust-lang/crates.io-index" 750 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 751 | 752 | [[package]] 753 | name = "utf8parse" 754 | version = "0.2.2" 755 | source = "registry+https://github.com/rust-lang/crates.io-index" 756 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 757 | 758 | [[package]] 759 | name = "valuable" 760 | version = "0.1.1" 761 | source = "registry+https://github.com/rust-lang/crates.io-index" 762 | checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" 763 | 764 | [[package]] 765 | name = "vte" 766 | version = "0.14.1" 767 | source = "registry+https://github.com/rust-lang/crates.io-index" 768 | checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077" 769 | dependencies = [ 770 | "memchr", 771 | ] 772 | 773 | [[package]] 774 | name = "wasi" 775 | version = "0.11.1+wasi-snapshot-preview1" 776 | source = "registry+https://github.com/rust-lang/crates.io-index" 777 | checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" 778 | 779 | [[package]] 780 | name = "winapi" 781 | version = "0.3.9" 782 | source = "registry+https://github.com/rust-lang/crates.io-index" 783 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 784 | dependencies = [ 785 | "winapi-i686-pc-windows-gnu", 786 | "winapi-x86_64-pc-windows-gnu", 787 | ] 788 | 789 | [[package]] 790 | name = "winapi-i686-pc-windows-gnu" 791 | version = "0.4.0" 792 | source = "registry+https://github.com/rust-lang/crates.io-index" 793 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 794 | 795 | [[package]] 796 | name = "winapi-x86_64-pc-windows-gnu" 797 | version = "0.4.0" 798 | source = "registry+https://github.com/rust-lang/crates.io-index" 799 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 800 | 801 | [[package]] 802 | name = "windows-link" 803 | version = "0.2.1" 804 | source = "registry+https://github.com/rust-lang/crates.io-index" 805 | checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" 806 | 807 | [[package]] 808 | name = "windows-sys" 809 | version = "0.61.2" 810 | source = "registry+https://github.com/rust-lang/crates.io-index" 811 | checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" 812 | dependencies = [ 813 | "windows-link", 814 | ] 815 | 816 | [[package]] 817 | name = "xterm-query" 818 | version = "0.5.2" 819 | source = "registry+https://github.com/rust-lang/crates.io-index" 820 | checksum = "292c33df434fde4ecd87a7afecdfa1681a3d29567fc69c774a0d83d32c095331" 821 | dependencies = [ 822 | "nix", 823 | "thiserror", 824 | ] 825 | --------------------------------------------------------------------------------