diff --git a/src/scanner/debftrace.rs b/src/scanner/debftrace.rs new file mode 100644 index 0000000..43f2f30 --- /dev/null +++ b/src/scanner/debftrace.rs @@ -0,0 +1,64 @@ +use crate::rootfs::RootFS; + +use super::traceitf::PkgFileTrace; +use std::{ + collections::HashMap, + fs::{self, DirEntry}, + path::PathBuf, +}; + +pub struct DebPkgFileTrace { + file_to_pkg: HashMap, +} + +impl DebPkgFileTrace { + pub fn new() -> Self { + let mut d = DebPkgFileTrace { file_to_pkg: HashMap::default() }; + d.load(); + d + } + + /// Read dpkg cache. All of it. + fn load(&mut self) { + if let Ok(rd) = fs::read_dir("/var/lib/dpkg/info") { + for d in rd.filter_map(Result::ok).collect::>() { + if d.path().to_str().unwrap().ends_with(".list") { + self.load_pkg(d.path()); + } + } + } + } + + fn load_pkg(&mut self, pinfo: PathBuf) { + // Path to package name + let pkgname = &pinfo + .file_name() + .unwrap_or_default() + .to_str() + .unwrap() + .strip_suffix(".list") + .unwrap() + .split(':') + .collect::>()[0]; + + if let Ok(pkg_data) = fs::read_to_string(&pinfo) { + for f_pth in pkg_data.split('\n').collect::>().iter().map(PathBuf::from) { + if f_pth.exists() && f_pth.is_file() { + self.file_to_pkg.insert(f_pth, pkgname.to_string().to_owned()); + } + } + } + } +} + +impl PkgFileTrace for DebPkgFileTrace { + fn trace(&mut self, filename: PathBuf) -> Option { + for p in RootFS::expand_target(filename, true) { + if let Some(pkg) = self.file_to_pkg.get(&p) { + return Some(pkg.to_owned()); + } + } + + None + } +} diff --git a/src/scanner/dlst.rs b/src/scanner/dlst.rs index 0735277..7f0c038 100644 --- a/src/scanner/dlst.rs +++ b/src/scanner/dlst.rs @@ -2,11 +2,15 @@ Data lister (fancy STDOUT printer) */ -use crate::filters::resources; +use crate::{ + filters::resources, + scanner::{debftrace::DebPkgFileTrace, traceitf::PkgFileTrace}, +}; use bytesize::ByteSize; use colored::Colorize; use filesize::PathExt; use std::{ + collections::HashSet, os::unix::prelude::PermissionsExt, path::{Path, PathBuf}, }; @@ -110,6 +114,17 @@ impl<'a> ContentFormatter<'a> { d_size += p.metadata().unwrap().len(); } + // Collect preserved packages + let mut pkgs: HashSet = HashSet::default(); + let mut pt = DebPkgFileTrace::new(); + for p in self.fs_data { + if let Some(pkg) = pt.trace(p.clone()) { + pkgs.insert(pkg); + } + } + let mut pkgs = pkgs.into_iter().collect::>(); + pkgs.sort(); + // Print the summary println!( "\nRemoved {} files, releasing {} of a disk space", @@ -128,7 +143,7 @@ impl<'a> ContentFormatter<'a> { ByteSize::b(j_size).to_string().bright_yellow() ); } - println!(""); + println!("Kept {} packages as follows:\n {}\n", pkgs.len().to_string().bright_yellow(), pkgs.join(", ")); } /// Get dir/name split, painted accordingly diff --git a/src/scanner/mod.rs b/src/scanner/mod.rs index d25cc80..b8f1c6a 100644 --- a/src/scanner/mod.rs +++ b/src/scanner/mod.rs @@ -1,4 +1,5 @@ pub mod binlib; +pub mod debftrace; pub mod debpkg; pub(crate) mod dlst; pub mod general; diff --git a/src/scanner/traceitf.rs b/src/scanner/traceitf.rs index 593349a..9bec7e9 100644 --- a/src/scanner/traceitf.rs +++ b/src/scanner/traceitf.rs @@ -1,5 +1,12 @@ +use std::path::PathBuf; + /// Package dependency trace pub trait PkgDepTrace { fn trace(&mut self, pkgname: String) -> Vec; fn exclude(&mut self, pkgs: Vec) -> &mut Self; } + +pub trait PkgFileTrace { + /// Return a package name, to which this file belongs to + fn trace(&mut self, filename: PathBuf) -> Option; +}