GPG keystore initialization, reimplemented progress event modules,
additions to global configuration module, and bug fixes. - Progress verbosity option for progress indicators - Logging verbsoity option for future implementation of logging infrastructure - Small addendum to cache API, with get_instance providing a Result enum - Validate the program is running under a non-privileged user upon front-end invocation
This commit is contained in:
parent
fa9de91330
commit
3b5951cea6
22 changed files with 583 additions and 313 deletions
|
@ -33,9 +33,9 @@ use pacwrap_core::{err,
|
|||
TransactionMetadata,
|
||||
TransactionParameters,
|
||||
MAGIC_NUMBER},
|
||||
event::{download::{DownloadCallback, download_event},
|
||||
progress::{ProgressEvent, callback},
|
||||
query::questioncb}},
|
||||
event::{download::{self, DownloadEvent},
|
||||
progress::{self, ProgressEvent},
|
||||
query}},
|
||||
utils::{print_warning, read_le_32}, config::Global};
|
||||
|
||||
use crate::error::AgentError;
|
||||
|
@ -70,13 +70,17 @@ pub fn transact() -> Result<()> {
|
|||
let alpm = sync::instantiate_alpm_agent(&config, &alpm_remotes);
|
||||
let mut handle = TransactionHandle::new(&config, alpm, &mut metadata);
|
||||
|
||||
conduct_transaction(&mut handle, params)
|
||||
conduct_transaction(&config, &mut handle, params)
|
||||
}
|
||||
|
||||
fn conduct_transaction(handle: &mut TransactionHandle, agent: TransactionParameters) -> Result<()> {
|
||||
fn conduct_transaction(config: &Global, handle: &mut TransactionHandle, agent: TransactionParameters) -> Result<()> {
|
||||
let flags = handle.retrieve_flags();
|
||||
let mode = agent.mode();
|
||||
let action = agent.action();
|
||||
let config = config.config();
|
||||
let progress = config.progress();
|
||||
let bytes = agent.bytes();
|
||||
let files = agent.files();
|
||||
|
||||
if let Err(error) = handle.alpm_mut().trans_init(flags.1.unwrap()) {
|
||||
err!(SyncError::InitializationFailure(error.to_string().into()))?
|
||||
|
@ -96,9 +100,17 @@ fn conduct_transaction(handle: &mut TransactionHandle, agent: TransactionParamet
|
|||
erroneous_preparation(error)?
|
||||
}
|
||||
|
||||
handle.alpm().set_progress_cb(ProgressEvent::new(&action), callback(&mode));
|
||||
handle.alpm().set_question_cb((), questioncb);
|
||||
handle.alpm().set_dl_cb(DownloadCallback::new(agent.bytes(), agent.files()), download_event);
|
||||
let progress_cb = ProgressEvent::new()
|
||||
.style(progress.0)
|
||||
.configure(&action);
|
||||
let download_cb = DownloadEvent::new()
|
||||
.style(progress.0)
|
||||
.total(bytes, files)
|
||||
.configure(&mode, progress.1);
|
||||
|
||||
handle.alpm().set_question_cb((), query::callback);
|
||||
handle.alpm().set_progress_cb(progress_cb, progress::callback(&mode, progress.0));
|
||||
handle.alpm().set_dl_cb(download_cb, download::callback(progress.1));
|
||||
|
||||
if let Err(error) = handle.alpm_mut().trans_commit() {
|
||||
erroneous_transaction(error)?
|
||||
|
|
|
@ -45,7 +45,7 @@ pub mod cache;
|
|||
pub mod instance;
|
||||
pub mod init;
|
||||
pub mod register;
|
||||
mod global;
|
||||
pub mod global;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConfigError {
|
||||
|
@ -64,8 +64,8 @@ impl Display for ConfigError {
|
|||
match self {
|
||||
Self::Filesystem(module, err) => write!(fmter, "Failed to register filesystem {}: {} ", module, err),
|
||||
Self::Permission(module, err) => write!(fmter, "Failed to register permission {}: {} ", module, err),
|
||||
Self::Load(ins, error) => write!(fmter, "Failed to load '{ins}.yml': {error}"),
|
||||
Self::Save(ins, error) => write!(fmter, "Failed to save '{ins}.yml': {error}"),
|
||||
Self::Load(ins, error) => write!(fmter, "Failed to load '{ins}': {error}"),
|
||||
Self::Save(ins, error) => write!(fmter, "Failed to save '{ins}': {error}"),
|
||||
Self::AlreadyExists(ins) => write!(fmter, "Container {}{ins}{} already exists.", *BOLD, *RESET),
|
||||
Self::ConfigNotFound(ins) => write!(fmter, "Configuration '{}{ins}{}.yml' not found.", *BOLD, *RESET)
|
||||
}
|
||||
|
|
|
@ -54,7 +54,13 @@ impl <'a>InstanceCache<'a> {
|
|||
err!(ConfigError::AlreadyExists(ins.into()))?
|
||||
}
|
||||
|
||||
let deps = deps.iter().map(|a| (*a).into()).collect();
|
||||
for dep in deps.iter() {
|
||||
if let None = self.instances.get(dep) {
|
||||
err!(ErrorKind::DependencyNotFound((*dep).into(), ins.into()))?
|
||||
}
|
||||
}
|
||||
|
||||
let deps = deps.iter().map(|a| (*a).into()).collect();
|
||||
let handle = match config::provide_new_handle(ins) {
|
||||
Ok(mut handle) => {
|
||||
handle.metadata_mut().set(deps, vec!());
|
||||
|
@ -79,7 +85,7 @@ impl <'a>InstanceCache<'a> {
|
|||
|
||||
fn map(&mut self, ins: &'a str) -> Result<()> {
|
||||
if let Some(_) = self.instances.get(ins) {
|
||||
err!(ConfigError::AlreadyExists(ins.into()))?
|
||||
err!(ConfigError::AlreadyExists(ins.to_owned()))?
|
||||
}
|
||||
|
||||
Ok(self.register(ins, match config::provide_handle(ins) {
|
||||
|
@ -125,7 +131,13 @@ impl <'a>InstanceCache<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_instance(&self, ins: &str) -> Option<&InstanceHandle> {
|
||||
pub fn get_instance(&self, ins: &str) -> Result<&InstanceHandle> {
|
||||
match self.instances.get(ins) {
|
||||
Some(ins) => Ok(ins), None => err!(ErrorKind::InstanceNotFound(ins.into())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_instance_option(&self, ins: &str) -> Option<&InstanceHandle> {
|
||||
self.instances.get(ins)
|
||||
}
|
||||
}
|
||||
|
@ -146,24 +158,19 @@ pub fn populate<'a>() -> Result<InstanceCache<'a>> {
|
|||
|
||||
fn roots<'a>() -> Result<Vec<&'a str>> {
|
||||
match read_dir(format!("{}/root", *DATA_DIR)) {
|
||||
Ok(dir) => Ok(dir.filter(|f| match f {
|
||||
Ok(dir) => Ok(dir.filter(|f| match f {
|
||||
Ok(f) => match f.metadata() {
|
||||
Ok(meta) => meta.is_dir() | meta.is_symlink(),
|
||||
Err(_) => false,
|
||||
Ok(meta) => meta.is_dir() | meta.is_symlink(), Err(_) => false
|
||||
},
|
||||
Err(_) => false })
|
||||
Err(_) => false
|
||||
})
|
||||
.map(|s| match s {
|
||||
Ok(f) => f.file_name()
|
||||
.to_str()
|
||||
.unwrap_or("")
|
||||
.to_string()
|
||||
.leak(),
|
||||
Ok(f) => match f.file_name().to_str() {
|
||||
Some(f) => f.to_owned().leak(), None => "",
|
||||
},
|
||||
Err(_) => "",
|
||||
})
|
||||
.filter_map(|e| match e.is_empty() {
|
||||
true => None,
|
||||
false => Some(e)
|
||||
})
|
||||
.filter(|e| ! e.is_empty())
|
||||
.collect()),
|
||||
Err(error) => err!(ErrorKind::IOError(format!("{}/root", *DATA_DIR), error.kind())),
|
||||
}
|
||||
|
|
|
@ -35,12 +35,27 @@ pub enum Verbosity {
|
|||
Verbose,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub enum ProgressKind {
|
||||
Simple,
|
||||
Condensed,
|
||||
CondensedForeign,
|
||||
CondensedLocal,
|
||||
Verbose,
|
||||
}
|
||||
|
||||
impl Default for Verbosity {
|
||||
fn default() -> Self {
|
||||
Self::Verbose
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for ProgressKind {
|
||||
fn default() -> Self {
|
||||
Self::CondensedForeign
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Global {
|
||||
#[serde(default = "Configuration::new")]
|
||||
|
@ -49,12 +64,22 @@ pub struct Global {
|
|||
alpm: AlpmConfiguration,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Progress {
|
||||
#[serde(default = "ProgressKind::default")]
|
||||
transact: ProgressKind,
|
||||
#[serde(default = "ProgressKind::default")]
|
||||
download: ProgressKind,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Configuration {
|
||||
#[serde(default = "Verbosity::default")]
|
||||
summary: Verbosity,
|
||||
#[serde(default = "Verbosity::default")]
|
||||
progress: Verbosity,
|
||||
logging: Verbosity,
|
||||
#[serde(default = "Progress::new")]
|
||||
progress: Progress,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
|
@ -79,7 +104,29 @@ impl Configuration {
|
|||
fn new() -> Self {
|
||||
Self {
|
||||
summary: Verbosity::Basic,
|
||||
progress: Verbosity::Verbose,
|
||||
logging: Verbosity::Basic,
|
||||
progress: Progress::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn progress(&self) -> (&ProgressKind, &ProgressKind) {
|
||||
(&self.progress.transact, &self.progress.download)
|
||||
}
|
||||
|
||||
pub fn logging(&self) -> &Verbosity {
|
||||
&self.summary
|
||||
}
|
||||
|
||||
pub fn summary(&self) -> &Verbosity {
|
||||
&self.summary
|
||||
}
|
||||
}
|
||||
|
||||
impl Progress {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
transact: ProgressKind::CondensedForeign,
|
||||
download: ProgressKind::CondensedForeign,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,8 @@ use crate::{err,
|
|||
Error,
|
||||
Result,
|
||||
ErrorKind,
|
||||
constants::{CACHE_DIR, CONFIG_DIR, DATA_DIR}};
|
||||
constants::{CACHE_DIR, CONFIG_DIR, DATA_DIR},
|
||||
config::global::CONFIG};
|
||||
|
||||
static REPO_CONF_DEFAULT: &'static str = r###"## See the pacman.conf(5) manpage for information on repository directives.
|
||||
## All other libalpm-related options therein are ignored.
|
||||
|
@ -44,8 +45,11 @@ static PACWRAP_CONF_DEFAULT: &'static str = r###"## See the pacwrap.yml(2) manpa
|
|||
## Documentation is also available at https://git.sapphirus.org/pacwrap/pacwrap/docs/
|
||||
|
||||
config:
|
||||
summary: Basic
|
||||
progress: Verbose
|
||||
logging: Basic
|
||||
summmary: Basic
|
||||
#progress:
|
||||
#transact: CondensedForeign
|
||||
#download: CondensedForeign
|
||||
alpm:
|
||||
#ignore_pkg:
|
||||
#- somepackage
|
||||
|
@ -84,7 +88,7 @@ impl DirectoryLayout {
|
|||
|
||||
fn data_layout() -> DirectoryLayout {
|
||||
DirectoryLayout {
|
||||
dirs: vec!("/root", "/home", "/state", "/pacman/gnupg", "/pacman/sync"),
|
||||
dirs: vec!("/root", "/home", "/state", "/pacman/sync"),
|
||||
root: *DATA_DIR,
|
||||
}
|
||||
}
|
||||
|
@ -121,6 +125,8 @@ fn write_to_file(location: &str, contents: &str) -> Result<()> {
|
|||
}
|
||||
|
||||
pub fn init() -> Result<()> {
|
||||
let _ = *CONFIG;
|
||||
|
||||
config_layout().instantiate()?;
|
||||
data_layout().instantiate()?;
|
||||
cache_layout().instantiate()?;
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
*/
|
||||
|
||||
use std::{borrow::Cow,
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
fmt::{Display, Debug, Formatter},
|
||||
vec::Vec};
|
||||
|
||||
|
@ -29,7 +28,8 @@ use crate::{Result,
|
|||
filesystem::{Filesystem, root::ROOT, home::HOME},
|
||||
dbus::Dbus,
|
||||
vars::InsVars,
|
||||
save}};
|
||||
save},
|
||||
constants::UNIX_TIMESTAMP};
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Instance<'a> {
|
||||
|
@ -228,14 +228,14 @@ impl <'a>InstanceMetadata<'a> {
|
|||
container_type: ctype,
|
||||
dependencies: deps.iter().map(|a| (*a).into()).collect(),
|
||||
explicit_packages: pkgs.iter().map(|a| (*a).into()).collect(),
|
||||
meta_version: time_as_seconds(),
|
||||
meta_version: *UNIX_TIMESTAMP,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, deps: Vec<&'a str>, pkgs: Vec<&'a str>) {
|
||||
self.dependencies = deps.iter().map(|a| (*a).into()).collect();
|
||||
self.explicit_packages = pkgs.iter().map(|a| (*a).into()).collect();
|
||||
self.meta_version = time_as_seconds();
|
||||
self.meta_version = *UNIX_TIMESTAMP;
|
||||
}
|
||||
|
||||
pub fn container_type(&self) -> &InstanceType {
|
||||
|
@ -249,15 +249,16 @@ impl <'a>InstanceMetadata<'a> {
|
|||
pub fn explicit_packages(&'a self) -> Vec<&'a str> {
|
||||
self.explicit_packages.iter().map(|a| a.as_ref()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn time_as_seconds() -> u64 {
|
||||
SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.unwrap()
|
||||
.as_secs()
|
||||
pub fn timestamp(&self) -> u64 {
|
||||
self.meta_version
|
||||
}
|
||||
}
|
||||
|
||||
fn default_true() -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn time_as_seconds() -> u64 {
|
||||
*UNIX_TIMESTAMP
|
||||
}
|
||||
|
|
|
@ -22,11 +22,12 @@ use std::{env::var, process::id};
|
|||
use lazy_static::lazy_static;
|
||||
use nix::unistd::{geteuid, getegid};
|
||||
|
||||
use crate::{error, Error, ErrorKind, utils::{is_color_terminal, is_truecolor_terminal}};
|
||||
use crate::{error, Error, ErrorKind, utils::{is_color_terminal, is_truecolor_terminal, unix_time_as_seconds}};
|
||||
|
||||
pub const BWRAP_EXECUTABLE: &str = "bwrap";
|
||||
pub const DBUS_PROXY_EXECUTABLE: &str = "xdg-dbus-proxy";
|
||||
pub const DEFAULT_PATH: &str = "/usr/local/bin:/usr/bin/:/bin";
|
||||
pub const PACMAN_KEY_SCRIPT: &'static str = "pacman-key";
|
||||
|
||||
const PACWRAP_CONFIG_DIR: &str = "/.config/pacwrap";
|
||||
const PACWRAP_DATA_DIR: &str = "/.local/share/pacwrap";
|
||||
|
@ -66,6 +67,7 @@ lazy_static! {
|
|||
pub static ref DBUS_SOCKET: String = format!("/run/user/{}/pacwrap_dbus_{}", *UID, &id());
|
||||
pub static ref WAYLAND_SOCKET: String = format!("{}{}", *XDG_RUNTIME_DIR, *WAYLAND_DISPLAY);
|
||||
pub static ref LOG_LOCATION: &'static str = format_str!("{}/pacwrap.log", *DATA_DIR);
|
||||
pub static ref UNIX_TIMESTAMP: u64 = unix_time_as_seconds();
|
||||
pub static ref IS_COLOR_TERMINLAL: bool = is_color_terminal();
|
||||
pub static ref IS_TRUECOLOR_TERMINLAL: bool = is_truecolor_terminal();
|
||||
pub static ref BOLD: &'static str = bold();
|
||||
|
|
|
@ -17,12 +17,10 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{process::{Child, Command}, fmt::{Formatter, Display}, os::fd::AsRawFd};
|
||||
use std::{process::{Child, Command, Stdio}, fmt::{Formatter, Display}};
|
||||
|
||||
use command_fds::{CommandFdExt, FdMapping};
|
||||
use lazy_static::lazy_static;
|
||||
use os_pipe::{PipeWriter, PipeReader};
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::{err,
|
||||
impl_error,
|
||||
|
@ -31,10 +29,10 @@ use crate::{err,
|
|||
ErrorKind,
|
||||
Error,
|
||||
Result,
|
||||
constants::{LOG_LOCATION, BWRAP_EXECUTABLE, BOLD, RESET, GID, UID, TERM, LANG, COLORTERM},
|
||||
config::{InstanceHandle, CONFIG},
|
||||
sync::{DEFAULT_ALPM_CONF, SyncError, transaction::{TransactionParameters, TransactionMetadata}},
|
||||
exec::seccomp::{provide_bpf_program, FilterType::*}};
|
||||
constants::{LOG_LOCATION, BWRAP_EXECUTABLE, BOLD, RESET, GID, UID, TERM, LANG, COLORTERM, PACMAN_KEY_SCRIPT},
|
||||
config::InstanceHandle,
|
||||
sync::transaction::{TransactionParameters, TransactionMetadata},
|
||||
exec::{utils::{wait_on_process, agent_params}, seccomp::{provide_bpf_program, FilterType::*}}};
|
||||
|
||||
pub mod args;
|
||||
pub mod utils;
|
||||
|
@ -173,17 +171,15 @@ pub fn transaction_agent(ins: &InstanceHandle, params: &TransactionParameters, m
|
|||
}
|
||||
}
|
||||
|
||||
fn agent_params(reader: &PipeReader, writer: &PipeWriter, params: &TransactionParameters, metadata: &TransactionMetadata) -> Result<i32> {
|
||||
serialize(params, writer)?;
|
||||
serialize(&*CONFIG, writer)?;
|
||||
serialize(&*DEFAULT_ALPM_CONF, writer)?;
|
||||
serialize(metadata, writer)?;
|
||||
Ok(reader.as_raw_fd())
|
||||
}
|
||||
|
||||
fn serialize<T: for<'de> Serialize>(input: &T, file: &PipeWriter) -> Result<()> {
|
||||
match bincode::serialize_into::<&PipeWriter, T>(file, input) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(error) => err!(SyncError::TransactionFailure(format!("Agent data serialization failed: {}", error))),
|
||||
pub fn pacman_key(path: &str, cmd: Vec<&str>) -> Result<()> {
|
||||
match Command::new(PACMAN_KEY_SCRIPT)
|
||||
.stderr(Stdio::null())
|
||||
.env("EUID", "0")
|
||||
.arg("--gpgdir")
|
||||
.arg(path)
|
||||
.args(cmd)
|
||||
.spawn() {
|
||||
Ok(proc) => wait_on_process(PACMAN_KEY_SCRIPT, proc),
|
||||
Err(error) => err!(ErrorKind::ProcessInitFailure(PACMAN_KEY_SCRIPT, error.kind()))?,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,12 +17,20 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use std::{process::Child, io::Read};
|
||||
use std::{process::Child, io::Read, os::fd::AsRawFd};
|
||||
|
||||
use os_pipe::{PipeReader, PipeWriter};
|
||||
use serde::Serialize;
|
||||
use serde_yaml::Value;
|
||||
|
||||
use crate::{constants::BWRAP_EXECUTABLE, config::InstanceHandle, ErrorKind, error::*, err};
|
||||
use crate::{err,
|
||||
error::*,
|
||||
ErrorKind,
|
||||
constants::BWRAP_EXECUTABLE,
|
||||
config::{InstanceHandle, CONFIG},
|
||||
sync::{SyncError, transaction::{TransactionParameters,
|
||||
TransactionMetadata},
|
||||
DEFAULT_ALPM_CONF}};
|
||||
|
||||
pub fn execute_fakeroot_container(ins: &InstanceHandle, arguments: Vec<&str>) -> Result<()> {
|
||||
match super::fakeroot_container(ins, arguments)?.wait() {
|
||||
|
@ -46,13 +54,31 @@ pub fn bwrap_json(mut reader: PipeReader, writer: PipeWriter) -> Result<i32> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn handle_process(name: &str, result: std::result::Result<Child, std::io::Error>) -> Result<()> {
|
||||
pub fn agent_params(reader: &PipeReader, writer: &PipeWriter, params: &TransactionParameters, metadata: &TransactionMetadata) -> Result<i32> {
|
||||
serialize(params, writer)?;
|
||||
serialize(&*CONFIG, writer)?;
|
||||
serialize(&*DEFAULT_ALPM_CONF, writer)?;
|
||||
serialize(metadata, writer)?;
|
||||
Ok(reader.as_raw_fd())
|
||||
}
|
||||
|
||||
fn serialize<T: for<'de> Serialize>(input: &T, file: &PipeWriter) -> Result<()> {
|
||||
match bincode::serialize_into::<&PipeWriter, T>(file, input) {
|
||||
Ok(()) => Ok(()),
|
||||
Err(error) => err!(SyncError::TransactionFailure(format!("Agent data serialization failed: {}", error))),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_process(name: &'static str, result: std::result::Result<Child, std::io::Error>) -> Result<()> {
|
||||
match result {
|
||||
Ok(child) => Ok(wait_on_process(child)),
|
||||
Ok(child) => wait_on_process(name, child),
|
||||
Err(error) => err!(ErrorKind::IOError(name.into(), error.kind())),
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_on_process(mut child: Child) {
|
||||
child.wait().ok();
|
||||
pub fn wait_on_process(name: &'static str, mut child: Child) -> Result<()> {
|
||||
match child.wait() {
|
||||
Ok(_) => Ok(()),
|
||||
Err(error) => err!(ErrorKind::ProcessWaitFailure(name, error.kind()))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
use std::fmt::{Display, Formatter};
|
||||
use std::{fmt::{Display, Formatter}, path::Path};
|
||||
|
||||
use alpm::{Alpm, SigLevel, Usage};
|
||||
use lazy_static::lazy_static;
|
||||
|
@ -26,12 +26,14 @@ use serde::{Serialize, Deserialize};
|
|||
use crate::{err,
|
||||
impl_error,
|
||||
error::*,
|
||||
ErrorKind,
|
||||
utils::print_warning,
|
||||
exec::pacman_key,
|
||||
constants::{BAR_GREEN, RESET, BOLD, CACHE_DIR, DATA_DIR, CONFIG_DIR, ARROW_RED},
|
||||
sync::event::download::{DownloadCallback, download_event},
|
||||
sync::event::download::{self, DownloadEvent},
|
||||
config::{InsVars,
|
||||
InstanceHandle,
|
||||
cache::InstanceCache, CONFIG, Global}};
|
||||
cache::InstanceCache, CONFIG, Global, global::ProgressKind}};
|
||||
|
||||
pub mod event;
|
||||
pub mod utils;
|
||||
|
@ -64,6 +66,7 @@ pub enum SyncError {
|
|||
InitializationFailure(String),
|
||||
InternalError(String),
|
||||
NoCompatibleRemotes,
|
||||
UnableToLocateKeyrings,
|
||||
}
|
||||
|
||||
impl_error!(SyncError);
|
||||
|
@ -85,6 +88,7 @@ impl Display for SyncError {
|
|||
Self::NoCompatibleRemotes => write!(fmter, "No compatible containers available to synchronize remote database."),
|
||||
Self::InvalidMagicNumber => write!(fmter, "Deserialization of input parameters failed: Invalid magic number."),
|
||||
Self::InternalError(msg) => write!(fmter, "Internal failure: {msg}"),
|
||||
Self::UnableToLocateKeyrings => write!(fmter, "Unable to locate pacman keyrings."),
|
||||
Self::NothingToDo(_) => write!(fmter, "Nothing to do."),
|
||||
_ => Ok(()),
|
||||
}?;
|
||||
|
@ -124,7 +128,7 @@ impl AlpmConfigData {
|
|||
|
||||
remotes.push(("pacwrap".into(),
|
||||
(SigLevel::PACKAGE_MARGINAL_OK | SigLevel::DATABASE_MARGINAL_OK).bits(),
|
||||
vec![format!("file://{}", env!("PACWRAP_DIST_REPO")), format!("file:///mnt/dist-repo/")]));
|
||||
vec![format!("file://{}", env!("PACWRAP_DIST_REPO")), format!("file:///mnt/share/dist-repo/")]));
|
||||
|
||||
Self {
|
||||
repos: remotes,
|
||||
|
@ -149,7 +153,6 @@ pub fn instantiate_alpm_agent(config: &Global, remotes: &AlpmConfigData) -> Alpm
|
|||
|
||||
pub fn instantiate_alpm(inshandle: &InstanceHandle) -> Alpm {
|
||||
alpm_handle(inshandle.vars(), format!("{}/var/lib/pacman/", inshandle.vars().root()), &*DEFAULT_ALPM_CONF)
|
||||
|
||||
}
|
||||
|
||||
fn alpm_handle(insvars: &InsVars, db_path: String, remotes: &AlpmConfigData) -> Alpm {
|
||||
|
@ -166,6 +169,29 @@ fn alpm_handle(insvars: &InsVars, db_path: String, remotes: &AlpmConfigData) ->
|
|||
handle
|
||||
}
|
||||
|
||||
//TODO: Port pacman-key to Rust
|
||||
|
||||
pub fn instantiate_trust() -> Result<()> {
|
||||
let path = &format!("{}/pacman/gnupg/", *DATA_DIR);
|
||||
|
||||
if Path::new(path).exists() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("{} {}Initializing package trust database...{}", *BAR_GREEN, *BOLD, *RESET);
|
||||
|
||||
if ! Path::new("/usr/share/pacman/keyrings").exists() {
|
||||
err!(SyncError::UnableToLocateKeyrings)?
|
||||
}
|
||||
|
||||
if let Err(error) = std::fs::create_dir_all(path) {
|
||||
err!(ErrorKind::IOError(path.into(), error.kind()))?
|
||||
}
|
||||
|
||||
pacman_key(path, vec!["--init"])?;
|
||||
pacman_key(path, vec!["--populate"])
|
||||
}
|
||||
|
||||
fn register_remote(mut handle: Alpm, config: &AlpmConfigData) -> Alpm {
|
||||
for repo in &config.repos {
|
||||
let core = handle.register_syncdb_mut(repo.0.clone(),
|
||||
|
@ -187,8 +213,8 @@ fn synchronize_database(cache: &InstanceCache, force: bool) -> Result<()> {
|
|||
let db_path = format!("{}/pacman/", *DATA_DIR);
|
||||
let mut handle = alpm_handle(&ins.vars(), db_path, &*DEFAULT_ALPM_CONF);
|
||||
|
||||
println!("{} {}Synchronising package databases...{}", *BAR_GREEN, *BOLD, *RESET);
|
||||
handle.set_dl_cb(DownloadCallback::new(0, 0), download_event);
|
||||
println!("{} {}Synchronizing package databases...{}", *BAR_GREEN, *BOLD, *RESET);
|
||||
handle.set_dl_cb(DownloadEvent::new().style(&ProgressKind::Verbose), download::event);
|
||||
|
||||
if let Err(err) = handle.syncdbs_mut().update(force) {
|
||||
err!(SyncError::InitializationFailure(err.to_string()))?
|
||||
|
@ -226,7 +252,7 @@ fn signature(sigs: &Vec<String>, default: SigLevel) -> SigLevel {
|
|||
let mut sig = SigLevel::empty();
|
||||
|
||||
for level in sigs {
|
||||
sig = sig | if level == "PackageRequired" || level == "PackageTrustedOnly" {
|
||||
sig = sig | if level == "Required" || level == "PackageRequired" {
|
||||
SigLevel::PACKAGE
|
||||
} else if level == "DatabaseRequired" || level == "DatabaseTrustedOnly" {
|
||||
SigLevel::DATABASE
|
||||
|
|
|
@ -22,14 +22,25 @@ pub mod query;
|
|||
pub mod progress;
|
||||
|
||||
fn whitespace(total: usize, current: usize) -> String {
|
||||
let total = log10(total);
|
||||
let current = log10(current);
|
||||
let mut whitespace = String::new();
|
||||
let difference = total-current;
|
||||
|
||||
if difference > 0 {
|
||||
for _ in 0..difference {
|
||||
whitespace.push_str(" ");
|
||||
}
|
||||
}
|
||||
for _ in 0..difference {
|
||||
whitespace.push_str(" ");
|
||||
}
|
||||
|
||||
whitespace
|
||||
}
|
||||
|
||||
fn log10(mut value: usize) -> usize {
|
||||
let mut length = 0;
|
||||
|
||||
while value > 0 {
|
||||
value /= 10;
|
||||
length += 1;
|
||||
}
|
||||
|
||||
length
|
||||
}
|
||||
|
|
|
@ -19,16 +19,18 @@
|
|||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use alpm::{AnyDownloadEvent, DownloadEvent};
|
||||
use alpm::{AnyDownloadEvent, DownloadEvent as Event};
|
||||
use dialoguer::console::Term;
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
|
||||
use indicatif::{MultiProgress, ProgressBar, ProgressStyle, ProgressDrawTarget};
|
||||
use lazy_static::lazy_static;
|
||||
use simplebyteunit::simplebyteunit::*;
|
||||
|
||||
use crate::{constants::{ARROW_CYAN, BOLD, RESET}, config::global::ProgressKind, sync::transaction::TransactionMode};
|
||||
|
||||
use super::whitespace;
|
||||
|
||||
lazy_static!{
|
||||
static ref INIT: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg}")
|
||||
.unwrap();
|
||||
static ref INIT: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg}").unwrap();
|
||||
static ref UP_TO_DATE: ProgressStyle = ProgressStyle::with_template(" {spinner:.green} {msg} is up-to-date!")
|
||||
.unwrap()
|
||||
.progress_chars("#-")
|
||||
|
@ -36,104 +38,185 @@ lazy_static!{
|
|||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DownloadCallback {
|
||||
pub struct DownloadEvent {
|
||||
total: usize,
|
||||
position: usize,
|
||||
total_bar: Option<ProgressBar>,
|
||||
condensed: bool,
|
||||
progress: MultiProgress,
|
||||
prbar: HashMap<String, ProgressBar>,
|
||||
style: ProgressStyle,
|
||||
total_files: usize,
|
||||
total_files_len: usize,
|
||||
files_done: usize
|
||||
bars: HashMap<&'static str, ProgressBar>,
|
||||
style: Option<ProgressStyle>,
|
||||
}
|
||||
|
||||
impl DownloadCallback {
|
||||
pub fn new(total_bytes: u64, files: usize) -> Self {
|
||||
let mut bars = HashMap::new();
|
||||
let files_str_len = files.to_string().len();
|
||||
let size = Term::size(&Term::stdout());
|
||||
let width = ((size.1 / 2) - 14).to_string();
|
||||
let multiprogress = MultiProgress::new();
|
||||
let pb_style_tmpl = " {spinner:.green} {msg:<".to_owned()+width.as_str()
|
||||
+ "} {bytes:>11} {bytes_per_sec:>12} {elapsed_precise:>5} [{wide_bar}] {percent:<3}%";
|
||||
let pb_style = ProgressStyle::with_template(&(pb_style_tmpl))
|
||||
.unwrap()
|
||||
.progress_chars("#-")
|
||||
.tick_strings(&[" ", "✓"]);
|
||||
|
||||
if total_bytes > 0 {
|
||||
let pb_total = multiprogress.add(
|
||||
ProgressBar::new(total_bytes)
|
||||
.with_style(pb_style.clone())
|
||||
.with_message(format!("Total ({}0/{})", whitespace(files_str_len, 1), files)));
|
||||
|
||||
bars.insert("total".into(), pb_total);
|
||||
}
|
||||
|
||||
impl DownloadEvent {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
style: pb_style,
|
||||
total_files: files,
|
||||
total_files_len: files_str_len,
|
||||
files_done: 0,
|
||||
progress: multiprogress,
|
||||
prbar: bars,
|
||||
total: 0,
|
||||
position: 0,
|
||||
total_bar: None,
|
||||
condensed: false,
|
||||
progress: MultiProgress::new(),
|
||||
bars: HashMap::new(),
|
||||
style: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn style(mut self, kind: &ProgressKind) -> Self {
|
||||
self.style = match kind {
|
||||
ProgressKind::Simple => None,
|
||||
_ => Some({
|
||||
let size = Term::size(&Term::stdout());
|
||||
let width = ((size.1 / 2) - 14).to_string();
|
||||
|
||||
ProgressStyle::with_template(&(" {spinner:.green} {msg:<".to_owned()+&width
|
||||
+ "} {bytes:>11} {bytes_per_sec:>12} {elapsed_precise:>5} [{wide_bar}] {percent:<3}%"))
|
||||
.unwrap()
|
||||
.progress_chars("#-")
|
||||
.tick_strings(&[" ", "✓"])
|
||||
}),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
pub fn total(mut self, bytes: u64, files: usize) -> Self {
|
||||
self.total = files;
|
||||
self.total_bar = match (bytes > 0 && files > 1, self.style.as_ref()) {
|
||||
(true, Some(style)) => Some({
|
||||
let bar = self.progress.add(ProgressBar::new(bytes)
|
||||
.with_style(style.clone())
|
||||
.with_message(format!("Total ({}0/{})", whitespace(files, 1), files)));
|
||||
|
||||
bar.set_position(0);
|
||||
bar
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub fn configure(mut self, mode: &TransactionMode, progress: &ProgressKind) -> Self {
|
||||
self.condensed = match progress {
|
||||
ProgressKind::Condensed => true,
|
||||
ProgressKind::CondensedForeign => match mode {
|
||||
TransactionMode::Foreign => true,
|
||||
TransactionMode::Local => false,
|
||||
},
|
||||
ProgressKind::CondensedLocal => match mode {
|
||||
TransactionMode::Foreign => false,
|
||||
TransactionMode::Local => true,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
fn increment(&mut self, progress: u64) {
|
||||
let bar = match self.total_bar.as_mut() {
|
||||
Some(bar) => bar, None => return,
|
||||
};
|
||||
|
||||
self.position += 1;
|
||||
|
||||
let total = self.total;
|
||||
let pos = self.position;
|
||||
let whitespace = whitespace(total, pos);
|
||||
|
||||
bar.inc(progress);
|
||||
bar.set_message(format!("Total ({}{}/{})", whitespace, pos, total));
|
||||
|
||||
if total == pos {
|
||||
bar.finish();
|
||||
}
|
||||
}
|
||||
|
||||
fn insert(&mut self, file: &str) {
|
||||
let pb = match self.total_bar.as_mut() {
|
||||
Some(total) => match self.condensed {
|
||||
true => self.progress.insert_after(&total, ProgressBar::new(0)),
|
||||
false => self.progress.insert_before(&total, ProgressBar::new(0)),
|
||||
},
|
||||
None => self.progress.add(ProgressBar::new(0))
|
||||
};
|
||||
|
||||
pb.set_style(INIT.clone());
|
||||
pb.set_message(message(file));
|
||||
|
||||
/*
|
||||
* alpm-rs expects our callback signature to provide a struct bound by a 'static lifetime,
|
||||
* therefore allocate this literal and then leak it for use as a key.
|
||||
*/
|
||||
self.bars.insert(file.to_owned().leak(), pb);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn download_event(file: &str, download: AnyDownloadEvent, this: &mut DownloadCallback) {
|
||||
if file.ends_with(".sig") {
|
||||
pub fn simple(file: &str, download: AnyDownloadEvent, this: &mut DownloadEvent) {
|
||||
if file.ends_with(".sig") {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Event::Completed(progress) = download.event() {
|
||||
this.position += 1;
|
||||
|
||||
let size = progress.total.abs().to_byteunit(SI);
|
||||
let total = this.total;
|
||||
let pos = this.position;
|
||||
let whitespace = whitespace(total, pos);
|
||||
let message = message(file);
|
||||
|
||||
eprintln!("{} ({}{whitespace}{pos}{}/{}{total}{}) {message} downloaded ({size})", *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(file: &str, download: AnyDownloadEvent, this: &mut DownloadEvent) {
|
||||
if file.ends_with(".sig") {
|
||||
return;
|
||||
}
|
||||
|
||||
match download.event() {
|
||||
DownloadEvent::Progress(progress) => {
|
||||
if let Some(pb) = this.prbar.get_mut(&file.to_string()) {
|
||||
if pb.length().unwrap() == 0 {
|
||||
Event::Progress(progress) => {
|
||||
if let Some(pb) = this.bars.get_mut(file) {
|
||||
if pb.length().unwrap() == 0 {
|
||||
pb.set_length(progress.total.unsigned_abs());
|
||||
pb.set_style(this.style.clone());
|
||||
pb.set_style(this.style.as_ref().unwrap().clone());
|
||||
}
|
||||
|
||||
pb.set_position(progress.downloaded.unsigned_abs());
|
||||
|
||||
if let Some(total) = this.prbar.get("total") {
|
||||
total.inc(progress.downloaded.unsigned_abs());
|
||||
}
|
||||
pb.set_position(progress.downloaded.unsigned_abs());
|
||||
}
|
||||
},
|
||||
DownloadEvent::Completed(_) => {
|
||||
if let Some(pb) = this.prbar.remove(&file.to_string()) {
|
||||
Event::Completed(progress) => {
|
||||
if let Some(pb) = this.bars.remove(file) {
|
||||
if pb.length().unwrap() == 0 {
|
||||
pb.set_style(UP_TO_DATE.clone());
|
||||
}
|
||||
|
||||
pb.finish();
|
||||
|
||||
pb.finish();
|
||||
this.increment(progress.total.unsigned_abs());
|
||||
|
||||
if let Some(total) = this.prbar.get("total") {
|
||||
this.files_done += 1;
|
||||
|
||||
total.set_message(format!("Total ({}{}/{})",
|
||||
whitespace(this.total_files_len,
|
||||
this.files_done.to_string().len()),
|
||||
this.files_done,
|
||||
this.total_files));
|
||||
|
||||
if this.files_done == this.total_files {
|
||||
total.finish();
|
||||
}
|
||||
if this.condensed {
|
||||
pb.set_draw_target(ProgressDrawTarget::hidden());
|
||||
}
|
||||
}
|
||||
},
|
||||
DownloadEvent::Init(_) => {
|
||||
let pb = if let Some(total) = this.prbar.get("total") {
|
||||
this.progress.insert_before(&total, ProgressBar::new(0))
|
||||
} else {
|
||||
this.progress.add(ProgressBar::new(0))
|
||||
};
|
||||
Event::Init(progress) => {
|
||||
if progress.optional {
|
||||
return;
|
||||
}
|
||||
|
||||
pb.set_style(INIT.clone());
|
||||
pb.set_message(message(file));
|
||||
this.prbar.insert(file.into(), pb);
|
||||
this.insert(file);
|
||||
},
|
||||
_ => (),
|
||||
Event::Retry(_) => {
|
||||
if let Some(pb) = this.bars.get_mut(file) {
|
||||
pb.set_position(0);
|
||||
pb.set_style(INIT.clone());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn callback(progress: &ProgressKind) -> for<'a, 'b, 'c> fn(file: &'a str, AnyDownloadEvent<'b>, this: &'c mut DownloadEvent) {
|
||||
match progress {
|
||||
ProgressKind::Simple => simple, _ => event
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,114 +17,168 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use alpm::Progress;
|
||||
use alpm::Progress as Event;
|
||||
use dialoguer::console::Term;
|
||||
use indicatif::{ProgressBar, ProgressStyle};
|
||||
|
||||
use crate::{constants::{BOLD, RESET}, sync::transaction::{TransactionType, TransactionMode}};
|
||||
use super::whitespace;
|
||||
use crate::{constants::{BOLD, RESET, ARROW_CYAN},
|
||||
sync::{event::whitespace, transaction::{TransactionType, TransactionMode}},
|
||||
config::global::ProgressKind};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ProgressEvent {
|
||||
progress: ProgressBar,
|
||||
current: Option<String>,
|
||||
progress: Option<ProgressBar>,
|
||||
offset: usize,
|
||||
style: ProgressStyle,
|
||||
current: String,
|
||||
style: Option<ProgressStyle>,
|
||||
}
|
||||
|
||||
impl ProgressEvent {
|
||||
pub fn new(state: &TransactionType) -> Self {
|
||||
let size = Term::size(&Term::stdout());
|
||||
let width = (size.1 / 2).to_string();
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
offset: state.pr_offset(),
|
||||
style: ProgressStyle::with_template(&(" {spinner:.green} {msg:<".to_owned()+width.as_str()+"} [{wide_bar}] {percent:<3}%"))
|
||||
.unwrap().progress_chars("#-").tick_strings(&[" ", "✓"]),
|
||||
progress: ProgressBar::new(0),
|
||||
current: "".into(),
|
||||
current: None,
|
||||
progress: None,
|
||||
style: None,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure(mut self, state: &TransactionType) -> Self {
|
||||
self.offset = state.pr_offset();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn style(mut self, kind: &ProgressKind) -> Self {
|
||||
self.style = match kind {
|
||||
ProgressKind::Simple => None,
|
||||
_ => Some({
|
||||
let size = Term::size(&Term::stdout());
|
||||
let width = (size.1 / 2).to_string();
|
||||
|
||||
ProgressStyle::with_template(&(" {spinner:.green} {msg:<".to_owned()
|
||||
+ &width +"} [{wide_bar}] {percent:<3}%"))
|
||||
.unwrap().progress_chars("#-").tick_strings(&[" ", "✓"])
|
||||
}),
|
||||
};
|
||||
self
|
||||
}
|
||||
|
||||
fn bar(&mut self, ident: Option<String>, name: &str, howmany: usize, current: usize, size: usize) {
|
||||
let pos = current + self.offset;
|
||||
let total = howmany + self.offset;
|
||||
let whitespace = whitespace(total, pos);
|
||||
let progress = ProgressBar::new(size as u64);
|
||||
|
||||
progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {name}", *BOLD, *RESET, *BOLD, *RESET));
|
||||
progress.set_style(self.style.as_ref().unwrap().clone());
|
||||
|
||||
self.progress = Some(progress);
|
||||
self.current = ident
|
||||
}
|
||||
}
|
||||
|
||||
pub fn event(progress: Progress, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) {
|
||||
let ident = ident(progress,pkgname);
|
||||
pub fn event(event: Event, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) {
|
||||
let ident = ident(event,pkgname);
|
||||
|
||||
if ident != this.current {
|
||||
let pos = current + this.offset;
|
||||
let total = howmany + this.offset;
|
||||
let progress_name: String = name(progress,pkgname);
|
||||
let whitespace = whitespace(total.to_string().len(), pos.to_string().len());
|
||||
|
||||
this.progress = ProgressBar::new(100);
|
||||
this.progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET));
|
||||
this.progress.set_style(this.style.clone());
|
||||
this.current = ident;
|
||||
}
|
||||
|
||||
this.progress.set_position(percent as u64);
|
||||
match this.current.as_deref() {
|
||||
Some(current_ident) => if ident != current_ident {
|
||||
this.bar(Some(ident), &name(event,pkgname), howmany, current, 100)
|
||||
},
|
||||
None => this.bar(Some(ident), &name(event,pkgname), howmany, current, 100),
|
||||
};
|
||||
|
||||
let progress = match this.progress.as_mut() {
|
||||
Some(progress) => progress, None => return,
|
||||
};
|
||||
|
||||
progress.set_position(percent as u64);
|
||||
|
||||
if percent == 100 {
|
||||
this.progress.finish();
|
||||
progress.finish();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn condensed(progress: Progress, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) {
|
||||
if let Progress::AddStart | Progress::RemoveStart | Progress::UpgradeStart = progress {
|
||||
pub fn simple(progress: Event, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) {
|
||||
if percent == 0 {
|
||||
let pos = current + this.offset;
|
||||
let total = howmany + this.offset;
|
||||
let progress_name: String = name(progress,pkgname);
|
||||
let whitespace = whitespace(total.to_string().len(), pos.to_string().len());
|
||||
let whitespace = whitespace(total, pos);
|
||||
|
||||
if this.current != "" {
|
||||
this.progress = ProgressBar::new(howmany as u64);
|
||||
this.progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET));
|
||||
this.progress.set_style(this.style.clone());
|
||||
this.progress.set_position(current as u64);
|
||||
this.current = "".into();
|
||||
} else {
|
||||
this.progress.set_position(current as u64);
|
||||
this.progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET));
|
||||
}
|
||||
eprintln!("{} ({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET);
|
||||
|
||||
if current == howmany {
|
||||
this.progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) Foreign synchronization complete", *BOLD, *RESET, *BOLD, *RESET));
|
||||
this.progress.finish();
|
||||
eprintln!("{} ({}{whitespace}{pos}{}/{}{total}{}) Synchronization complete", *ARROW_CYAN, *BOLD, *RESET, *BOLD, *RESET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn condensed(kind: Event, pkgname: &str, percent: i32, howmany: usize, current: usize, this: &mut ProgressEvent) {
|
||||
if let Event::AddStart | Event::RemoveStart | Event::UpgradeStart = kind {
|
||||
let pos = current + this.offset;
|
||||
let total = howmany + this.offset;
|
||||
let progress_name: String = name(kind,pkgname);
|
||||
let whitespace = whitespace(total, pos);
|
||||
|
||||
if let Some(_) = this.current {
|
||||
this.bar(None, &progress_name, howmany, current, howmany);
|
||||
}
|
||||
|
||||
let progress = match this.progress.as_mut() {
|
||||
Some(progress) => progress, None => return,
|
||||
};
|
||||
|
||||
progress.set_position(current as u64);
|
||||
|
||||
if current != howmany {
|
||||
progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) {progress_name}", *BOLD, *RESET, *BOLD, *RESET));
|
||||
} else {
|
||||
progress.set_message(format!("({}{whitespace}{pos}{}/{}{total}{}) Synchronization complete", *BOLD, *RESET, *BOLD, *RESET));
|
||||
progress.finish();
|
||||
}
|
||||
} else {
|
||||
event(progress, pkgname, percent, howmany, current, this)
|
||||
event(kind, pkgname, percent, howmany, current, this)
|
||||
}
|
||||
}
|
||||
|
||||
fn name(progress: Progress, pkgname: &str) -> String {
|
||||
match progress {
|
||||
Progress::KeyringStart => "Loading keyring".into(),
|
||||
Progress::IntegrityStart => "Checking integrity".into(),
|
||||
Progress::LoadStart => "Loading packages".into(),
|
||||
Progress::ConflictsStart => "Checking conflicts".into(),
|
||||
Progress::DiskspaceStart => "Checking available diskspace".into(),
|
||||
Progress::UpgradeStart => format!("Upgrading {}", pkgname),
|
||||
Progress::AddStart => format!("Installing {}", pkgname),
|
||||
Progress::RemoveStart => format!("Removing {}", pkgname),
|
||||
Progress::DowngradeStart => format!("Downgrading {}", pkgname),
|
||||
Progress::ReinstallStart => format!("Reinstalling {}", pkgname)
|
||||
fn name(progress: Event, pkgname: &str) -> String {
|
||||
match progress {
|
||||
Event::UpgradeStart => format!("Upgrading {}", pkgname),
|
||||
Event::AddStart => format!("Installing {}", pkgname),
|
||||
Event::RemoveStart => format!("Removing {}", pkgname),
|
||||
Event::DowngradeStart => format!("Downgrading {}", pkgname),
|
||||
Event::ReinstallStart => format!("Reinstalling {}", pkgname),
|
||||
Event::KeyringStart => format!("Loading keyring"),
|
||||
Event::IntegrityStart => format!("Checking integrity"),
|
||||
Event::LoadStart => format!("Loading packages"),
|
||||
Event::ConflictsStart => format!("Checking conflicts"),
|
||||
Event::DiskspaceStart => format!("Checking available diskspace"),
|
||||
}
|
||||
}
|
||||
|
||||
fn ident(progress: Progress, pkgname: &str) -> String {
|
||||
fn ident(progress: Event, pkgname: &str) -> String {
|
||||
match progress {
|
||||
Progress::KeyringStart => "keyring",
|
||||
Progress::IntegrityStart => "integrity",
|
||||
Progress::LoadStart => "loadstart",
|
||||
Progress::ConflictsStart => "conflicts",
|
||||
Event::KeyringStart => "keyring",
|
||||
Event::IntegrityStart => "integrity",
|
||||
Event::LoadStart => "loadstart",
|
||||
Event::ConflictsStart => "conflicts",
|
||||
_ => pkgname
|
||||
|
||||
}.into()
|
||||
}.to_owned()
|
||||
}
|
||||
|
||||
pub fn callback(state: &TransactionMode) -> for<'a, 'b> fn(Progress, &'a str, i32, usize, usize, &'b mut ProgressEvent) {
|
||||
match state {
|
||||
TransactionMode::Local => event,
|
||||
TransactionMode::Foreign => condensed,
|
||||
pub fn callback(state: &TransactionMode, kind: &ProgressKind) -> for<'a, 'b> fn(Event, &'a str, i32, usize, usize, &'b mut ProgressEvent) {
|
||||
match kind {
|
||||
ProgressKind::Simple => simple,
|
||||
ProgressKind::CondensedForeign => match state {
|
||||
TransactionMode::Local => event,
|
||||
TransactionMode::Foreign => condensed,
|
||||
},
|
||||
ProgressKind::CondensedLocal => match state {
|
||||
TransactionMode::Local => condensed,
|
||||
TransactionMode::Foreign => event,
|
||||
},
|
||||
ProgressKind::Condensed => condensed,
|
||||
ProgressKind::Verbose => event,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ use alpm::{AnyQuestion, Question::*};
|
|||
|
||||
use crate::utils::prompt::prompt;
|
||||
|
||||
pub fn questioncb(question: AnyQuestion, _: &mut ()) {
|
||||
pub fn callback(question: AnyQuestion, _: &mut ()) {
|
||||
match question.question() {
|
||||
Conflict(mut x) => {
|
||||
let pkg_a = x.conflict().package1();
|
||||
|
@ -64,9 +64,6 @@ pub fn questioncb(question: AnyQuestion, _: &mut ()) {
|
|||
x.set_import(true);
|
||||
}
|
||||
},
|
||||
//TODO: Implement these questions.
|
||||
RemovePkgs(_) => (),
|
||||
SelectProvider(_) => (),
|
||||
InstallIgnorepkg(_) => (),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -147,11 +147,8 @@ impl <'a>FileSystemStateSync<'a> {
|
|||
continue;
|
||||
}
|
||||
|
||||