From 0e296ea5b0cb2ab884c74ccea42df86ca05840e0 Mon Sep 17 00:00:00 2001 From: Chawye Hsu Date: Tue, 25 Jul 2023 10:11:24 +0800 Subject: [PATCH] feat(libscoop|resolve): added fn `select_candidate` Signed-off-by: Chawye Hsu --- crates/libscoop/src/error.rs | 7 +++ crates/libscoop/src/operation.rs | 41 +--------------- crates/libscoop/src/package/mod.rs | 4 +- crates/libscoop/src/package/resolve.rs | 68 ++++++++++++++++++++------ crates/libscoop/src/session.rs | 7 ++- 5 files changed, 69 insertions(+), 58 deletions(-) diff --git a/crates/libscoop/src/error.rs b/crates/libscoop/src/error.rs index 007fe52..acd1d52 100644 --- a/crates/libscoop/src/error.rs +++ b/crates/libscoop/src/error.rs @@ -48,11 +48,18 @@ pub enum Error { #[error("error")] InvalidHashValue(String), + /// Throw when receiving an invalid answer from the frontend. + #[error("invalid answer")] + InvalidAnswer, + /// Package not found error, this may occur when doing an explicit lookup /// for a package and no record with the given query was found. #[error("Could not find package named '{0}'")] PackageNotFound(String), + #[error("Found multiple candidates for package named '{0}'")] + PackageMultipleCandidates(String), + /// Thrown when trying to perform (un)hold operation on a package that is /// not installed. #[error("package '{0}' is not installed")] diff --git a/crates/libscoop/src/operation.rs b/crates/libscoop/src/operation.rs index 8869bf7..e2ba2cf 100644 --- a/crates/libscoop/src/operation.rs +++ b/crates/libscoop/src/operation.rs @@ -307,46 +307,7 @@ pub fn package_sync( } if pkg.len() > 1 { - // Try to filter out installed ones if possible - pkg = pkg - .into_iter() - .filter(|p| !p.is_strictly_installed()) - .collect::>(); - // Luckily, there is no more than one package left - if pkg.len() <= 1 { - continue; - } - - if let Some(tx) = emitter.clone() { - println!( - "{}", - pkg.iter().map(|p| p.ident()).collect::>().join(" ") - ); - let question = pkg.iter().map(|p| p.ident()).collect::>(); - if tx.send(Event::SelectPackage(question)).is_ok() { - let rx = session.event_bus().inner_receiver(); - while let Ok(answer) = rx.recv() { - if let Event::SelectPackageAnswer(idx) = answer { - println!("{}", idx); - if idx < pkg.len() { - pkg = vec![pkg[idx].clone()]; - break; - } else { - return Err(Error::Custom(format!( - "Invalid package index: {}", - idx - ))); - } - } - } - } - } else { - // TODO: handle this case smartly - return Err(Error::Custom(format!( - "Found multiple candidates for package named '{}'", - query - ))); - } + resolve::select_candidate(session, &mut pkg)?; } packages.extend(pkg); diff --git a/crates/libscoop/src/package/mod.rs b/crates/libscoop/src/package/mod.rs index 9010160..57ea93c 100644 --- a/crates/libscoop/src/package/mod.rs +++ b/crates/libscoop/src/package/mod.rs @@ -1,6 +1,6 @@ mod manifest; -pub(super) mod query; -pub(super) mod resolve; +pub mod query; +pub mod resolve; mod sync; use lazycell::LazyCell; diff --git a/crates/libscoop/src/package/resolve.rs b/crates/libscoop/src/package/resolve.rs index bd605bd..4b38290 100644 --- a/crates/libscoop/src/package/resolve.rs +++ b/crates/libscoop/src/package/resolve.rs @@ -1,14 +1,11 @@ -#![allow(dead_code)] - use crate::{ error::Fallible, + event, internal::dag::DepGraph, - package::{query, QueryOption}, + package::{query, Package, QueryOption}, Error, Session, }; -use super::Package; - pub(crate) fn resolve_dependencies(session: &Session, packages: &mut Vec) -> Fallible<()> { let mut graph = DepGraph::::new(); let mut to_resolve = packages.clone(); @@ -32,20 +29,16 @@ pub(crate) fn resolve_dependencies(session: &Session, packages: &mut Vec 1 { - // TODO: handle this case smartly - return Err(Error::Custom(format!( - "Found multiple candidates for package named '{}'", - query - ))); + if candidates.len() > 1 { + select_candidate(session, &mut candidates)?; } - if !packages.contains(&dpkg[0]) { - dpkgs.push(dpkg[0].clone()); + if !packages.contains(&candidates[0]) { + dpkgs.push(candidates[0].clone()); } } @@ -98,3 +91,48 @@ pub(crate) fn resolve_dependents(session: &Session, packages: &mut Vec) Ok(()) } + +/// Select one from multiple package candidates, interactively if possible. +pub(crate) fn select_candidate(session: &Session, candidates: &mut Vec) -> Fallible<()> { + // Try to filter out installed ones if possible + candidates.retain(|p| !p.is_strictly_installed()); + + // Luckily, there is no more than one package left + if candidates.len() <= 1 { + return Ok(()); + } + + let name = candidates[0].name().to_owned(); + + // Sort candidates by package ident, in other words, by alphabetical order + // of bucket name. + candidates.sort_by_key(|p| p.ident()); + + // Only we can ask user/frontend to select one from multiple candidates + // when the outbound tx is available for us to do an interactive q&a. + if let Some(tx) = session.emitter() { + let question = candidates.iter().map(|p| p.ident()).collect::>(); + + if tx.send(event::Event::SelectPackage(question)).is_ok() { + // The unwrap is safe here because we have obtained the outbound tx, + // so the inbound rx must be available. + let rx = session.receiver().unwrap(); + + while let Ok(answer) = rx.recv() { + if let event::Event::SelectPackageAnswer(idx) = answer { + // bounds check + if idx < candidates.len() { + *candidates = vec![candidates[idx].clone()]; + + return Ok(()); + } + + return Err(Error::InvalidAnswer); + } + } + } + } + + // TODO: handle this case smartly using pre-defined bucket priority + Err(Error::PackageMultipleCandidates(name)) +} diff --git a/crates/libscoop/src/session.rs b/crates/libscoop/src/session.rs index b6c1d83..e643f3a 100644 --- a/crates/libscoop/src/session.rs +++ b/crates/libscoop/src/session.rs @@ -1,4 +1,4 @@ -use flume::Sender; +use flume::{Receiver, Sender}; use lazycell::LazyCell; use std::cell::{Ref, RefCell, RefMut}; use std::path::Path; @@ -107,6 +107,11 @@ impl Session { self.event_bus.borrow().map(|bus| bus.inner_sender()) } + /// Get an inbound receiver to reveive events. + pub(crate) fn receiver(&self) -> Option<&Receiver> { + self.event_bus.borrow().map(|bus| bus.inner_receiver()) + } + /// Set the user agent for the session. /// /// User agent is used when performing network related operations such as