From 28ba2135ec8823a29b7d07db19bf46c40a5fcc5c Mon Sep 17 00:00:00 2001 From: pivodevat Date: Thu, 18 Sep 2025 12:21:01 +0300 Subject: [PATCH] trim + fixes --- Cargo.toml | 2 ++ assets/etc/aeropkg.md | 23 +++++-------- install.sh | 1 + src/commands/mod.rs | 6 ++++ src/commands/trim.rs | 78 +++++++++++++++++++++++++++++++++++++++++++ src/main.rs | 37 ++++++++++++++++++++ src/utils/env.rs | 0 src/utils/hardcopy.rs | 2 +- src/utils/hooks.rs | 0 src/utils/parser.rs | 15 ++++----- 10 files changed, 139 insertions(+), 25 deletions(-) create mode 100644 src/commands/trim.rs create mode 100644 src/utils/env.rs create mode 100644 src/utils/hooks.rs diff --git a/Cargo.toml b/Cargo.toml index 7d7b11f..d0cce33 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,8 @@ edition='2024' [dependencies] clap = "4.5.39" rayon = "1.10.0" +glob = "0.3" +chrono = { version = "0.4", features = ["serde"] } [profile.release] opt-level = 3 diff --git a/assets/etc/aeropkg.md b/assets/etc/aeropkg.md index 99ba308..d0a1ede 100644 --- a/assets/etc/aeropkg.md +++ b/assets/etc/aeropkg.md @@ -14,24 +14,17 @@ musl /pkg/gnu/aeropkg/var/musl > Trimming removes unused files for a specified period > > Usage: -> pkg trim \ \ -> period relative time format: -> y — years, M — months, d — days, h — hours, m — minutes, s — seconds -> period absolute date format: -> DD.MM.YYYY HH:mm:ss -> -> Example: -> pkg trim musl 2d 10h -> pkg trim musl 04.09.2025 08:57:07 +> pkg trim \ \ \[time\] +> date: DD.MM.YYYY +> time: HH:mm:ss > > Configure format -> Any Linux path format, relative from /pkg/\ +> Relative paths with /pkg/, support wildcard +> Add ! to exclude -``` cfg *** Trim exclude *** -* -``` - -``` cfg *** Trim include *** +``` cfg *** Trim rules *** +!** +!aeropkg ``` diff --git a/install.sh b/install.sh index 90996f3..0ff5e0f 100755 --- a/install.sh +++ b/install.sh @@ -11,6 +11,7 @@ SCRIPT_DIR=$(dirname "$(readlink -f "$0")") echo "Установка в '$INSTALL_PATH'..." AEROPKG_HOME=$INSTALL_PATH cargo install --path . --root "$INSTALL_PATH" +patchelf --set-interpreter /pkg/gnu/glibc/lib/ld-linux-x86-64.so.2 /pkg/gnu/aeropkg/bin/pkg if [ $? -ne 0 ]; then echo "Ошибка при установке." diff --git a/src/commands/mod.rs b/src/commands/mod.rs index f444a36..49c2675 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -3,6 +3,7 @@ pub mod delete; pub mod link; pub mod disable; pub mod enable; +pub mod trim; use std::path::PathBuf; @@ -10,3 +11,8 @@ use std::path::PathBuf; pub fn get_var_path() -> PathBuf { PathBuf::from(env!("AEROPKG_HOME")).join("var") } + +pub fn get_etc_path() -> PathBuf { + PathBuf::from(env!("AEROPKG_HOME")).join("etc") +} + diff --git a/src/commands/trim.rs b/src/commands/trim.rs new file mode 100644 index 0000000..cfaee2e --- /dev/null +++ b/src/commands/trim.rs @@ -0,0 +1,78 @@ +use std::path::{Path, PathBuf}; +use std::fs; +use std::os::unix::fs::MetadataExt; +use glob::Pattern; + + + +fn trim(repo: &String, path: &Path, trim_date: i64, rules: &String) -> Result<(), std::io::Error> { + let metadata = fs::symlink_metadata(path)?; + + if metadata.is_dir() { + if rules_check(repo, path, rules) { + for entry in fs::read_dir(path)? { + let entry = entry?; + let entry_path = entry.path(); + trim(repo, &entry_path, trim_date, rules)?; + } + } + } else if metadata.is_file() { + if rules_check(repo, path, rules) { + let atime = metadata.atime(); + if atime < trim_date { + fs::remove_file(path)?; + } + } + } else if metadata.file_type().is_symlink() { + if let Ok(symlink_value) = fs::read_link(path) { + let symlink_path = if symlink_value.is_relative() { path.parent().unwrap().join(&symlink_value) } else { symlink_value }; + if !symlink_path.exists() { + println!("remove symlink: {} {}", path.display(), symlink_path.display()); + fs::remove_file(path)?; + } + } + } + + Ok(()) +} + +pub fn trim_handler(repo: &String, trim_date: i64) { + let etc_path = super::get_etc_path(); + let cfg_path = etc_path.join("aeropkg.md"); + + let rules = crate::utils::parser::get_trim_rules(&cfg_path).unwrap(); + + let pkg_dir = PathBuf::from("/pkg").join(repo); + + trim(repo, &pkg_dir, trim_date, &rules).unwrap(); +} + + +fn rules_check(repo: &String, path: &Path, rules: &String) -> bool { + let mut confirm = true; + + for rule in rules.lines() { + let mut rule = rule.trim(); + + let invert = rule.starts_with('!'); + if invert { + rule = &rule[1..]; + } + + let wildcard_string = if rule.starts_with('/') { + rule.to_string() + } else { + format!("/pkg/{}/{}", repo, rule) + }; + + let path_str = path.to_str().unwrap_or(""); + + if let Ok(pattern) = Pattern::new(&wildcard_string) { + if pattern.matches(path_str) { + confirm = !invert; + } + } + } + + confirm +} diff --git a/src/main.rs b/src/main.rs index 4830cb0..997dd32 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ mod commands; mod utils; +use chrono::NaiveDateTime; fn main() { let matches = clap::Command::new("pkg") @@ -53,6 +54,28 @@ fn main() { .index(2), ), ) + .subcommand( + clap::Command::new("trim") + .about("Remove unused files within a specified period") + .arg( + clap::Arg::new("repo") + .help("Repository name") + .required(true) + .index(1), + ) + .arg( + clap::Arg::new("date") + .help("DD.MM.YYYY") + .required(true) + .index(2), + ) + .arg( + clap::Arg::new("time") + .help("HH:mm:ss") + .required(false) + .index(3), + ), + ) .subcommand( clap::Command::new("disable") .about("Disable package") @@ -120,6 +143,20 @@ fn main() { Ok(_) => println!("link completed successfully."), Err(e) => eprintln!("Error during link: {}", e), } + } else if let Some(trim_matches) = matches.subcommand_matches("trim") { + let repo = trim_matches.get_one::("repo").unwrap(); + let date = trim_matches.get_one::("date").unwrap(); + + let time = trim_matches.get_one::("time").map(|s| s.as_str()).unwrap_or("00:00:00"); + + let datetime_str = format!("{} {}", date, time); + + let datetime = NaiveDateTime::parse_from_str(&datetime_str, "%d.%m.%Y %H:%M:%S") + .expect("Invalid date or time format. Expected format: DD.MM.YYYY HH:mm:ss"); + + let trim_date = datetime.and_utc().timestamp(); + + commands::trim::trim_handler(&repo, trim_date); } else if let Some(disable_matches) = matches.subcommand_matches("disable") { let repo = disable_matches.get_one::("repo").unwrap(); let pkgname = disable_matches.get_one::("pkgname").unwrap(); diff --git a/src/utils/env.rs b/src/utils/env.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/hardcopy.rs b/src/utils/hardcopy.rs index df75ef0..d52c6ec 100644 --- a/src/utils/hardcopy.rs +++ b/src/utils/hardcopy.rs @@ -76,7 +76,7 @@ fn hardcopy( })?; } else if metadata.file_type().is_symlink() { let symlink_value = fs::read_link(source)?; - fs::remove_file(destination)?; + if destination.exists() { fs::remove_file(destination)? } unix::fs::symlink(symlink_value, destination)?; } diff --git a/src/utils/hooks.rs b/src/utils/hooks.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/utils/parser.rs b/src/utils/parser.rs index e2b47f5..b27ed1d 100644 --- a/src/utils/parser.rs +++ b/src/utils/parser.rs @@ -1,5 +1,4 @@ use std::fs; -use std::env; use std::io::{self, BufRead}; use std::path::{Path,PathBuf}; @@ -59,10 +58,12 @@ pub fn get_patch_script>(file_path: P) -> io::Result { extract_block(file_path, "``` sh *** config ***", "```") } - +pub fn get_trim_rules>(file_path: P) -> io::Result { + extract_block(file_path, "``` cfg *** Trim rules ***", "```") +} pub fn get_repo_list() -> io::Result> { - let file_path = crate::commands::get_var_path().join("aeropkg.md"); + let file_path = crate::commands::get_etc_path().join("aeropkg.md"); let block = extract_block(file_path, "``` cfg *** Repository list and priority ***", "```")?; @@ -82,13 +83,9 @@ pub fn get_repo_list() -> io::Result> { pub fn get_repo_addr(repo: &str) -> io::Result { - let exe_path = env::current_exe()?; - let file_path = exe_path - .parent() - .ok_or_else(|| io::Error::new(io::ErrorKind::Other, "Failed to get executable directory"))? - .join("../etc/aeropkg.md"); + let file_path = crate::commands::get_etc_path().join("aeropkg.md"); - let block = extract_block(file_path, "``` sh *** Repository list and priority ***", "```")?; + let block = extract_block(file_path, "``` cfg *** Repository list and priority ***", "```")?; for line in block.lines() { let trimmed_line = line.trim();