From 7e5dbd6cf5f616f52aa935118acb798dba1adad3 Mon Sep 17 00:00:00 2001 From: Jonas Zeltner Date: Sun, 25 Aug 2024 14:31:45 +0200 Subject: [PATCH] Add user edited PKGBUILD dependencies Problem: When the user adds dependencies to the PKGBUILD, rua does not add those to its internal structure Solution: Introduce a new struct `ToInstall`, that saves the packages that rua has to install. When the user exits from the review shell, the PKGBUILD is parsed and new dependencies added. --- Cargo.lock | 108 ++++++++++++++++++---------- Cargo.toml | 1 + src/action_install.rs | 49 +++++++------ src/aur_rpc_utils.rs | 86 ++++++++-------------- src/main.rs | 1 + src/reviewing.rs | 71 +++++++++++++++++- src/to_install.rs | 163 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 359 insertions(+), 120 deletions(-) create mode 100644 src/to_install.rs diff --git a/Cargo.lock b/Cargo.lock index 2bc3c03..35745db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -342,9 +342,9 @@ dependencies = [ [[package]] name = "env_logger" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95b3f3e67048839cb0d0781f445682a35113da7121f7c949db0e2be96a4fbece" +checksum = "4cd405aab171cb85d6735e5c8d9db038c17d3ca007a4d2c25f337935c3d90580" dependencies = [ "humantime", "is-terminal", @@ -562,6 +562,12 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + [[package]] name = "http" version = "0.2.11" @@ -755,9 +761,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.151" +version = "0.2.158" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302d7ab3130588088d277783b1e2d2e10c9e9e4a16dd9050e6ec93fb3e7048f4" +checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] name = "libflate" @@ -861,11 +867,10 @@ dependencies = [ [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" dependencies = [ - "lazy_static", "libc", "log", "openssl", @@ -985,6 +990,16 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69d3587f8a9e599cc7ec2c00e331f71c4e69a5f9a4b8a6efd5b07466b9736f9a" +[[package]] +name = "pkgbuild" +version = "0.1.0" +source = "git+https://github.com/7Ji/pkgbuild-rs#176bd3aeac3c2e3d1331362245339a8539a82a75" +dependencies = [ + "hex", + "log", + "tempfile", +] + [[package]] name = "prettytable-rs" version = "0.10.0" @@ -1201,6 +1216,7 @@ dependencies = [ "libflate", "libscmp", "log", + "pkgbuild", "prettytable-rs", "raur", "regex", @@ -1222,9 +1238,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" [[package]] name = "rustix" -version = "0.38.28" +version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72e572a5e8ca657d7366229cdde4bd14c4eb5499a9573d4d366fe1b599daa316" +checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ "bitflags 2.4.1", "errno", @@ -1507,15 +1523,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.9.0" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01ce4141aa927a6d1bd34a041795abd0db1cccba5d5f24b009f694bdf3a1f3fa" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand", - "redox_syscall", + "once_cell", "rustix", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -1914,7 +1930,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", ] [[package]] @@ -1932,7 +1948,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -1952,17 +1977,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.0", - "windows_aarch64_msvc 0.52.0", - "windows_i686_gnu 0.52.0", - "windows_i686_msvc 0.52.0", - "windows_x86_64_gnu 0.52.0", - "windows_x86_64_gnullvm 0.52.0", - "windows_x86_64_msvc 0.52.0", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -1973,9 +1999,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -1985,9 +2011,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -1997,9 +2023,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.0" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -2009,9 +2041,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -2021,9 +2053,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -2033,9 +2065,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -2045,9 +2077,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.0" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winreg" diff --git a/Cargo.toml b/Cargo.toml index 70d64d2..d308e89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ lazy_static = "1.4.0" libc = { version = "0.2.151", default-features = false } libflate = "2.0.0" log = { version = "0.4.20", default-features = false } +pkgbuild = { git = "https://github.com/7Ji/pkgbuild-rs", version = "0.1.0" } prettytable-rs = "0.10.0" raur = { version = "7.0.0", default-features = false, features = ["blocking", "rustls-tls"] } regex = { version = "1.9.6", default-features = false, features = ["perf"] } diff --git a/src/action_install.rs b/src/action_install.rs index c3ca797..594970a 100644 --- a/src/action_install.rs +++ b/src/action_install.rs @@ -5,6 +5,8 @@ use crate::reviewing; use crate::rua_paths::RuaPaths; use crate::tar_check; use crate::terminal_util; +use crate::to_install::DepthMap; +use crate::to_install::ToInstall; use crate::wrapped; use fs_extra::dir::CopyOptions; use indexmap::IndexMap; @@ -19,42 +21,37 @@ use std::path::PathBuf; pub fn install(targets: &[String], rua_paths: &RuaPaths, is_offline: bool, asdeps: bool) { let alpm = new_alpm_wrapper(); - let (split_to_raur, pacman_deps, split_to_depth) = - aur_rpc_utils::recursive_info(targets, &*alpm).unwrap_or_else(|err| { + let mut to_install = + aur_rpc_utils::get_packages_to_install(targets, &*alpm).unwrap_or_else(|err| { panic!("Failed to fetch info from AUR, {}", err); }); - let split_to_pkgbase: IndexMap = split_to_raur + let split_to_pkgbase: IndexMap = to_install + .infos() .iter() - .map(|(split, raur)| (split.to_string(), raur.package_base.to_string())) + .map(|(split, info)| (split.to_string(), info.pkg_base().to_string())) .collect(); - let not_found = split_to_depth - .keys() - .filter(|pkg| !split_to_raur.contains_key(*pkg)) - .collect_vec(); - if !not_found.is_empty() { + let nf = to_install.not_found(); + if !nf.is_empty() { eprintln!( "Need to install packages: {:?}, but they are not found on AUR.", - not_found + nf ); std::process::exit(1) } - show_install_summary(&pacman_deps, &split_to_depth); + show_install_summary(&to_install); for pkgbase in split_to_pkgbase.values().collect::>() { let dir = rua_paths.review_dir(pkgbase); fs::create_dir_all(&dir).unwrap_or_else(|err| { panic!("Failed to create repository dir for {}, {}", pkgbase, err) }); - reviewing::review_repo(&dir, pkgbase, rua_paths); + reviewing::review_repo(&dir, pkgbase, rua_paths, &mut to_install, &*alpm); } + show_install_summary(&to_install); + + let (_, pacman_deps, depths) = to_install.into_inner(); pacman::ensure_pacman_packages_installed(pacman_deps); - install_all( - rua_paths, - split_to_depth, - split_to_pkgbase, - is_offline, - asdeps, - ); + install_all(rua_paths, depths, split_to_pkgbase, is_offline, asdeps); for target in targets { // Delete temp directories after successful build+install if let Err(err) = rm_rf::remove(rua_paths.build_dir(target)) { @@ -68,7 +65,9 @@ pub fn install(targets: &[String], rua_paths: &RuaPaths, is_offline: bool, asdep } } -fn show_install_summary(pacman_deps: &IndexSet, aur_packages: &IndexMap) { +fn show_install_summary(to_install: &ToInstall) { + let pacman_deps = to_install.pacman_deps(); + let aur_packages = to_install.depths(); if pacman_deps.len() + aur_packages.len() == 1 { return; } @@ -81,7 +80,7 @@ fn show_install_summary(pacman_deps: &IndexSet, aur_packages: &IndexMap< }; eprintln!("\nAnd the following AUR packages will need to be built and installed:"); let mut aur_packages = aur_packages.iter().collect::>(); - aur_packages.sort_by_key(|pair| -*pair.1); + aur_packages.sort_by_key(|(_, depth)| -(**depth as isize)); for (aur, dep) in &aur_packages { debug!("depth {}: {}", dep, aur); } @@ -100,7 +99,7 @@ fn show_install_summary(pacman_deps: &IndexSet, aur_packages: &IndexMap< fn install_all( rua_paths: &RuaPaths, - split_to_depth: IndexMap, + split_to_depth: DepthMap, split_to_pkgbase: IndexMap, offline: bool, asdeps: bool, @@ -118,18 +117,18 @@ fn install_all( (pkgbase.to_string(), *depth, split.to_string()) }); // sort pairs in descending depth order - let packages = packages.sorted_by_key(|(_pkgbase, depth, _split)| -depth); + let packages = packages.sorted_by_key(|(_pkgbase, depth, _split)| -(*depth as isize)); // Note that a pkgbase can appear at multiple depths because // multiple split pkgnames can be at multiple depths. // In this case, we only take the first occurrence of pkgbase, // which would be the maximum depth because of sort order. // We only take one occurrence because we want the package to only be built once. - let packages: Vec<(String, i32, String)> = packages + let packages = packages .unique_by(|(pkgbase, _depth, _split)| pkgbase.to_string()) .collect::>(); // once we have a collection of pkgname-s and their depth, proceed straightforwardly. for (depth, packages) in &packages.iter().group_by(|(_pkgbase, depth, _split)| *depth) { - let packages = packages.collect::>(); + let packages = packages.collect::>(); for (pkgbase, _depth, _split) in &packages { let review_dir = rua_paths.review_dir(pkgbase); let build_dir = rua_paths.build_dir(pkgbase); diff --git a/src/aur_rpc_utils.rs b/src/aur_rpc_utils.rs index 472b1a5..c1fcf36 100644 --- a/src/aur_rpc_utils.rs +++ b/src/aur_rpc_utils.rs @@ -1,76 +1,50 @@ use crate::alpm_wrapper::AlpmWrapper; +use crate::to_install::PkgInfo; +use crate::to_install::ToInstall; +use anyhow::Context; use anyhow::Result; use indexmap::IndexMap; -use indexmap::IndexSet; -use itertools::Itertools; use lazy_static::lazy_static; use log::trace; -use raur::blocking::Handle; +use raur::blocking::Handle as RaurHandle; use raur::blocking::Raur; use raur::Package; use regex::Regex; -type RaurInfo = IndexMap; -type PacmanDependencies = IndexSet; -type DepthMap = IndexMap; -type RecursiveInfo = (RaurInfo, PacmanDependencies, DepthMap); - const BATCH_SIZE: usize = 200; -pub fn recursive_info( +pub fn get_packages_to_install( root_packages_to_process: &[String], alpm: &dyn AlpmWrapper, -) -> Result { - let raur_handle = Handle::default(); - let mut queue: Vec = Vec::from(root_packages_to_process); - let mut depth_map = IndexMap::new(); - for pkg in &queue { - depth_map.insert(pkg.to_string(), 0); - } - let mut pacman_deps: IndexSet = IndexSet::new(); - let mut info_map: IndexMap = IndexMap::new(); +) -> Result { + let mut to_install = ToInstall::new(root_packages_to_process); + aur_info(root_packages_to_process.to_vec(), &mut to_install, alpm) + .context("Could not build the initial dependency tree")?; + Ok(to_install) +} + +pub fn aur_info( + mut queue: Vec, + to_install: &mut ToInstall, + alpm: &dyn AlpmWrapper, +) -> Result<()> { + let raur_handle = RaurHandle::default(); while !queue.is_empty() { let split_at = queue.len().max(BATCH_SIZE) - BATCH_SIZE; let to_process = queue.split_off(split_at); trace!("to_process: {:?}", to_process); - for info in raur_handle.info(&to_process)? { - let make_deps = info.make_depends.iter(); - let check_deps = info.check_depends.iter(); - let flat_deps = info.depends.iter(); - let deps = make_deps - .chain(flat_deps) - .chain(check_deps) - .map(|d| clean_and_check_package_name(d)) - .collect_vec(); - - for dependency in deps.into_iter() { - if alpm.is_installed(&dependency)? { - // skip if already installed - } else if !alpm.is_installable(&dependency)? { - if !depth_map.contains_key(&dependency) { - eprintln!( - "Package {} depends on {}. Resolving...", - info.name, dependency - ); - queue.push(dependency.to_string()) - } else { - eprintln!("Skipping already resolved dependency {}", dependency); - } - let parent_depth = depth_map - .get(&info.name) - .expect("Internal error: queue element does not have depth"); - let new_depth = depth_map - .get(&dependency) - .map_or(parent_depth + 1, |d| (*d).max(parent_depth + 1)); - depth_map.insert(dependency.to_string(), new_depth); - } else { - pacman_deps.insert(dependency.to_owned()); - } - } - info_map.insert(info.name.to_string(), info); + for info in raur_handle + .info(&to_process) + .context("Could not rertieve package info from AUR")? + { + let info = PkgInfo::from(info); + let new_deps = to_install + .add_package(info, alpm) + .context("Could not add package")?; + queue.extend(new_deps) } } - Ok((info_map, pacman_deps, depth_map)) + Ok(()) } /// Queries the AUR for the provided given package names and returns a map of all packages @@ -79,7 +53,7 @@ pub fn recursive_info( /// # Arguments /// * `packages_to_query` - A slice of package names to find in the AUR pub fn info_map>(packages_to_query: &[S]) -> Result> { - let raur_handle = Handle::new(); + let raur_handle = RaurHandle::new(); let mut result = IndexMap::new(); for group in packages_to_query.chunks(BATCH_SIZE) { let group_info = raur_handle.info(group)?; @@ -90,7 +64,7 @@ pub fn info_map>(packages_to_query: &[S]) -> Result String { +pub fn clean_and_check_package_name(name: &str) -> String { match clean_package_name(name) { Some(name) => name, None => { diff --git a/src/main.rs b/src/main.rs index 585d9da..a4a0f06 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ mod rua_paths; mod srcinfo_to_pkgbuild; mod tar_check; mod terminal_util; +mod to_install; mod wrapped; use crate::print_package_info::info; diff --git a/src/reviewing.rs b/src/reviewing.rs index 129f6a2..339db67 100644 --- a/src/reviewing.rs +++ b/src/reviewing.rs @@ -1,12 +1,25 @@ +use crate::alpm_wrapper::AlpmWrapper; +use crate::aur_rpc_utils::aur_info; use crate::git_utils; use crate::rua_paths::RuaPaths; use crate::terminal_util; +use crate::to_install::pkgbuild_to_info; +use crate::to_install::ToInstall; use crate::wrapped; +use anyhow::Context; use colored::Colorize; use log::debug; +use log::error; +use log::warn; use std::path::Path; -pub fn review_repo(dir: &Path, pkgbase: &str, rua_paths: &RuaPaths) { +pub fn review_repo( + dir: &Path, + pkgbase: &str, + rua_paths: &RuaPaths, + to_install: &mut ToInstall, + alpm: &dyn AlpmWrapper, +) { let mut dir_contents = dir.read_dir().unwrap_or_else(|err| { panic!( "{}:{} Failed to read directory for reviewing, {}", @@ -101,7 +114,63 @@ pub fn review_repo(dir: &Path, pkgbase: &str, rua_paths: &RuaPaths) { } else if &user_input == "m" && !is_upstream_merged { git_utils::merge_upstream(dir, rua_paths); } else if &user_input == "o" && is_upstream_merged { + update_to_install(dir, to_install, alpm); break; } } } + +fn update_to_install(dir: &Path, to_install: &mut ToInstall, alpm: &dyn AlpmWrapper) { + let infos = parse_pgkbuild(dir) + .map(pkgbuild_to_info) + .unwrap_or_default(); + + for info in infos { + let new_deps = to_install + .add_package(info, alpm) + .context("Could not add package to dependency tree"); + match new_deps { + Ok(nd) => { + let e = + aur_info(nd, to_install, alpm).context("Could not process new dependencies"); + if let Err(e) = e { + error!("{e}"); + } + } + Err(e) => { + error!("{e}"); + } + } + } +} + +fn parse_pgkbuild(dir: &Path) -> Option { + let mut parser = match pkgbuild::Parser::new() { + Ok(p) => Some(p), + Err(e) => { + warn!( + "Could not create PKGBULD parser: {e} +rua will not be able to adapt the dependency tree to your changes!" + ); + None + } + }; + match &mut parser { + None => None, + Some(p) => { + let mut opts = pkgbuild::ParserOptions::new(); + opts.set_work_dir(Some(dir)); + p.set_options(opts); + match p.parse_one(None::<&Path>) { + Ok(ok) => Some(ok), + Err(e) => { + warn!( + "Could not parse original PKGBULD: {e} +rua will not be able to adapt the dependency tree to your changes!" + ); + None + } + } + } + } +} diff --git a/src/to_install.rs b/src/to_install.rs new file mode 100644 index 0000000..06eaf1b --- /dev/null +++ b/src/to_install.rs @@ -0,0 +1,163 @@ +use std::ops::Not; + +use anyhow::Context; +use indexmap::IndexMap; +use indexmap::IndexSet; +use log::trace; + +use crate::alpm_wrapper::AlpmWrapper; +use crate::aur_rpc_utils::clean_and_check_package_name; + +#[derive(Debug)] +pub struct PkgInfo { + name: String, + base: String, + dependencies: IndexSet, +} +impl PkgInfo { + fn new(name: String, base: String, dependencies: IndexSet) -> Self { + Self { + name, + base, + dependencies, + } + } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn pkg_base(&self) -> &str { + &self.base + } +} +impl From for PkgInfo { + fn from(pkg: raur::Package) -> Self { + let make_deps = pkg.make_depends.iter(); + let check_deps = pkg.check_depends.iter(); + let flat_deps = pkg.depends.iter(); + let deps = make_deps + .chain(flat_deps) + .chain(check_deps) + .map(|d| clean_and_check_package_name(d)) + .collect::>(); + Self::new(pkg.name, pkg.package_base, deps) + } +} +pub fn pkgbuild_to_info(pkg_build: pkgbuild::Pkgbuild) -> Vec { + pkg_build + .pkgs + .iter() + .map(|pkg| { + let deps = pkg + .depends(None) + .into_iter() + .chain(pkg_build.makedepends(None)) + .chain(pkg_build.checkdepends(None)) + .chain(pkg_build.depends(None)) + .map(|d| clean_and_check_package_name(&d.name)) + .collect(); + PkgInfo::new(pkg.pkgname.clone(), pkg_build.pkgbase.clone(), deps) + }) + .collect() +} + +pub type Infos = IndexMap; +pub type PacmanDependencies = IndexSet; +pub type DepthMap = IndexMap; + +#[derive(Debug)] +pub struct ToInstall { + infos: Infos, + pacman_deps: PacmanDependencies, + depths: DepthMap, +} +impl ToInstall { + pub fn new(root_packages: &[String]) -> Self { + let dm = root_packages + .iter() + .map(|p| (p.to_string(), 0)) + .collect::(); + Self { + infos: Infos::new(), + pacman_deps: PacmanDependencies::new(), + depths: dm, + } + } + pub fn into_inner(self) -> (Infos, PacmanDependencies, DepthMap) { + let Self { + infos: is, + pacman_deps: pacs, + depths: ds, + } = self; + (is, pacs, ds) + } + pub fn add_package( + &mut self, + info: PkgInfo, + alpm: &dyn AlpmWrapper, + ) -> Result, anyhow::Error> { + let mut new_aur_dependencies = vec![]; + for dependency in &info.dependencies { + if self.pacman_deps.contains(dependency) { + trace!("{dependency} is already resolved as a pacman dependency"); + } else if alpm + .is_installed(dependency) + .context("Could not determine if dependency is installed")? + { + trace!("{dependency} is already installed"); + } else if alpm + .is_installable(dependency) + .context("Could not determine if dependency is installable")? + { + self.pacman_deps.insert(dependency.to_owned()); + } else { + if self.depths.contains_key(dependency) { + eprintln!("Skipping already resolved dependency {}", dependency); + continue; + } + + eprintln!( + "Package {} depends on {}. Resolving...", + info.name, dependency + ); + new_aur_dependencies.push(dependency.to_string()); + + let parent_depth = self + .depths + .get(&info.name) + .expect("Internal error: queue element does not have depth"); + let new_depth = self + .depths + .get(dependency) + .map_or(parent_depth + 1, |d| (*d).max(parent_depth + 1)); + self.depths.insert(dependency.to_string(), new_depth); + } + } + self.infos.insert(info.name().to_string(), info); + Ok(new_aur_dependencies) + } + + pub fn infos(&self) -> &Infos { + &self.infos + } + + pub fn pacman_deps(&self) -> &PacmanDependencies { + &self.pacman_deps + } + + pub fn depths(&self) -> &DepthMap { + &self.depths + } + + pub fn not_found(&self) -> Vec<&str> { + self.depths() + .keys() + .filter_map(|pkg| { + (self.infos().contains_key(pkg)) + .not() + .then_some(pkg.as_str()) + }) + .collect::>() + } +}