Release 0.4.1 - Compatibility bug and improved error handling.

- Disallow multiple type parameters applied to --create
- Provide the ROOT type in pacwrap-utils replicate function
- Updated help manual.
This commit is contained in:
Xavier Moffett 2023-10-22 07:13:41 -04:00
parent c14f6e0733
commit c0bb7f80d1
10 changed files with 91 additions and 50 deletions

2
Cargo.lock generated
View file

@ -362,7 +362,7 @@ dependencies = [
[[package]] [[package]]
name = "pacwrap" name = "pacwrap"
version = "0.4.0" version = "0.4.1"
dependencies = [ dependencies = [
"alpm", "alpm",
"bitflags 2.4.1", "bitflags 2.4.1",

View file

@ -2,7 +2,7 @@
[package] [package]
name = "pacwrap" name = "pacwrap"
version = "0.4.0" version = "0.4.1"
edition = "2021" edition = "2021"
[dependencies] [dependencies]

View file

@ -359,6 +359,7 @@ replicate_instance() {
[[ $type != BASE ]] && depend=$(return_dependency) [[ $type != BASE ]] && depend=$(return_dependency)
case $type in case $type in
ROOT) params+="r";;
BASE) params+="b";; BASE) params+="b";;
DEP) params+="d";; DEP) params+="d";;
LINK) ln -s "$INSTANCE_ROOT_DIR/$depend" "$INSTANCE_ROOT_DIR/$instance" LINK) ln -s "$INSTANCE_ROOT_DIR/$depend" "$INSTANCE_ROOT_DIR/$instance"

View file

@ -1,7 +1,7 @@
# Maintainer: Xavier R.M. (sapphirus at azorium dot net) # Maintainer: Xavier R.M. (sapphirus at azorium dot net)
pkgname=('pacwrap-base-dist') pkgname=('pacwrap-base-dist')
pkgver=0.4.0 pkgver=0.4.1
pkgrel=1 pkgrel=1
pkgdesc="" pkgdesc=""
arch=('any') arch=('any')

View file

@ -10,6 +10,7 @@ use crate::config::{self,
InsVars, InsVars,
InstanceType, InstanceType,
InstanceHandle}; InstanceHandle};
use crate::utils::print_help_error;
use crate::utils::{arguments::{Arguments, Operand}, use crate::utils::{arguments::{Arguments, Operand},
handle_process, handle_process,
env_var}; env_var};
@ -90,7 +91,7 @@ pub fn compat(mut args: Arguments) {
match args.next().unwrap_or_default() { match args.next().unwrap_or_default() {
Operand::Short('s') | Operand::Long("save") => save_configuration(args.target()), Operand::Short('s') | Operand::Long("save") => save_configuration(args.target()),
Operand::Short('l') | Operand::Long("load") => print_configuration(args.target()), Operand::Short('l') | Operand::Long("load") => print_configuration(args.target()),
_ => args.invalid_operand() _ => print_help_error(args.invalid_operand())
} }
} }

View file

@ -28,7 +28,6 @@ fn main() {
Operand::Short('h') | Operand::Long("help") => manual::help(arguments), Operand::Short('h') | Operand::Long("help") => manual::help(arguments),
Operand::Short('V') | Operand::Long("version") => manual::print_version(arguments), Operand::Short('V') | Operand::Long("version") => manual::print_version(arguments),
Operand::Long("compat") => compat::compat(arguments), Operand::Long("compat") => compat::compat(arguments),
Operand::None => print_help_error("Operation not specified."), _ => print_help_error(arguments.invalid_operand()),
_ => arguments.invalid_operand(),
} }
} }

View file

@ -20,13 +20,16 @@ lazy_static! {
pub fn help(mut args: Arguments) { pub fn help(mut args: Arguments) {
let help = ascertain_help(&mut args); let help = ascertain_help(&mut args);
for topic in help.0 { match help {
topic.display(help.1); Ok(help) => for topic in help.0 {
topic.display(help.1);
}
Err(err) => print_help_error(err),
} }
} }
fn ascertain_help<'a>(args: &mut Arguments) -> (IndexSet<&'a HelpTopic>, &'a HelpLayout) { fn ascertain_help<'a>(args: &mut Arguments) -> Result<(IndexSet<&'a HelpTopic>, &'a HelpLayout), String> {
let mut layout = match is_color_terminal() { let mut layout = match is_color_terminal() {
true => &HelpLayout::Console, false => &HelpLayout::Dumb, true => &HelpLayout::Console, false => &HelpLayout::Dumb,
}; };
@ -75,8 +78,9 @@ fn ascertain_help<'a>(args: &mut Arguments) -> (IndexSet<&'a HelpTopic>, &'a Hel
| Operand::LongPos("help", "all") | Operand::LongPos("help", "all")
| Operand::Short('a') | Operand::Short('a')
| Operand::Long("all") => topic.extend(HELP_ALL.iter()), | Operand::Long("all") => topic.extend(HELP_ALL.iter()),
Operand::ShortPos('h', topic) | Operand::LongPos("help", topic) => print_help_error(format!("Topic '{topic}' is not available.")), Operand::ShortPos('h', topic)
_ => args.invalid_operand(), | Operand::LongPos("help", topic) => Err(format!("Topic '{topic}' is not available."))?,
_ => Err(args.invalid_operand())?,
} }
} }
@ -84,7 +88,7 @@ fn ascertain_help<'a>(args: &mut Arguments) -> (IndexSet<&'a HelpTopic>, &'a Hel
let start = if more || len == 1 || len > 7 { 0 } else { 1 }; let start = if more || len == 1 || len > 7 { 0 } else { 1 };
args.set_index(1); args.set_index(1);
(topic.drain(start..).collect(), layout) Ok((topic.drain(start..).collect(), layout))
} }
fn minimal(args: &mut Arguments) -> bool { fn minimal(args: &mut Arguments) -> bool {
@ -203,7 +207,6 @@ fn default(layout: &HelpLayout) {
println!("# {name} 1 \"{version}-{suffix} ({timestamp})\" {name} \"User Manual\"\n"); println!("# {name} 1 \"{version}-{suffix} ({timestamp})\" {name} \"User Manual\"\n");
} }
println!("{head}NAME{reset} println!("{head}NAME{reset}
{tab}pacwrap - Command-line application which facilitates the creation, management, and execution of unprivileged, {tab}pacwrap - Command-line application which facilitates the creation, management, and execution of unprivileged,
{tab}Sandboxed containers with bubblewrap and libalpm. {tab}Sandboxed containers with bubblewrap and libalpm.
@ -271,6 +274,7 @@ fn meta(layout: &HelpLayout) {
fn sync(layout: &HelpLayout) { fn sync(layout: &HelpLayout) {
let head = layout.head(); let head = layout.head();
let bold = layout.bold();
let sub = layout.sub(); let sub = layout.sub();
let sub_text = layout.sub_text(); let sub_text = layout.sub_text();
let reset = layout.reset(); let reset = layout.reset();
@ -287,6 +291,18 @@ fn sync(layout: &HelpLayout) {
{sub}-f, --filesystem{reset_bold} {sub}-f, --filesystem{reset_bold}
{sub_text}Force execution of filesystem synchronization coroutines on all or specified containers. {sub_text}Force execution of filesystem synchronization coroutines on all or specified containers.
{sub}-c, --create{reset_bold}
{sub_text}Create a container with the first specified target. Providing a container type argument is also required.
{sub}-b, --base{reset_bold}
{sub_text}Base container type. Specify alongside {bold}-c, --create{reset_bold} to assign container type during creation.
{sub}-d, --slice{reset_bold}
{sub_text}Slice container type. Specify alongside {bold}-c, --create{reset_bold} to to assign container type during creation.
{sub}-r, --root{reset_bold}
{sub_text}Root container type. Specify alongside {bold}-c, --create{reset_bold} to assign container type during creation.
{sub}--dbonly{reset_bold} {sub}--dbonly{reset_bold}
{sub_text}Transact on resident containers with a database-only transaction. {sub_text}Transact on resident containers with a database-only transaction.
@ -294,7 +310,11 @@ fn sync(layout: &HelpLayout) {
{sub_text}Force synchronization of foreign packages on resident container. {sub_text}Force synchronization of foreign packages on resident container.
{sub}--dbonly{reset_bold} {sub}--dbonly{reset_bold}
{sub_text}Override confirmation prompts and confirm all operations.\n"); {sub_text}Override confirmation prompts and confirm all operations.
{sub}-t, --target=TARGET{reset_bold}
{sub_text}Specify a target container for the specified operation.\n");
} }
fn process(layout: &HelpLayout) { fn process(layout: &HelpLayout) {
@ -346,6 +366,7 @@ fn copyright(layout: &HelpLayout) {
let tab = layout.tab(); let tab = layout.tab();
println!("{head}COPYRIGHT{reset} println!("{head}COPYRIGHT{reset}
{sub_text}Copyright (C) 2023 - Xavier R.M. {sub_text}Copyright (C) 2023 - Xavier R.M.
{tab}This program may be freely redistributed under {tab}This program may be freely redistributed under
@ -369,7 +390,7 @@ pub fn print_version(mut args: Arguments) {
ACCCCCCCC#####CCCCC######C######C###CCCCCCCCCP ACCCCCCCC#####CCCCC######C######C###CCCCCCCCCP
ACCCCCCCCCCC######CC####CCC####CCCCCCCCCCCCCCP ACCCCCCCCCCC######CC####CCC####CCCCCCCCCCCCCCP
ACCCCCCCCCCCCCC##CCCCCCCCCCCCCCCCCCCCCCCCCCCCP Website: https://pacwrap.sapphirus.org/ ACCCCCCCCCCCCCC##CCCCCCCCCCCCCCCCCCCCCCCCCCCCP Website: https://pacwrap.sapphirus.org/
ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP Github: https;//git.sapphirus.org/ ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP Github: https://github.com/sapphirusberyl/pacwrap
ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP
ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP ACCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP
RPCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP This program may be freely redistributed under RPCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCP This program may be freely redistributed under

View file

@ -63,19 +63,24 @@ pub fn synchronize(mut args: Arguments) {
TransactionType::Upgrade(u > 0, y > 0, y > 1) TransactionType::Upgrade(u > 0, y > 0, y > 1)
}; };
if let Some(instype) = create_type(&mut args) { match create_type(&mut args) {
if let TransactionType::Upgrade(upgrade, refresh, _) = action { Ok(option) => if let Some(instype) = option {
if ! upgrade { if let TransactionType::Upgrade(upgrade, refresh, _) = action {
print_help_error("--upgrade/-u not supplied with --create/-c."); if ! upgrade {
} else if ! refresh { print_help_error("--upgrade/-u not supplied with --create/-c.");
print_help_error("--refresh/-y not supplied with --create/-c."); } else if ! refresh {
print_help_error("--refresh/-y not supplied with --create/-c.");
}
} }
}
create(instype, args.targets()); create(instype, args.targets());
},
Err(error) => print_help_error(error),
} }
aggregator::upgrade(action, &mut args, &mut cache, &mut logger).aggregate(&mut InstanceCache::new()); match aggregator::upgrade(action, &mut args, &mut cache, &mut logger) {
Ok(ag) => ag.aggregate(&mut InstanceCache::new()), Err(e) => print_help_error(e)
}
} }
pub fn remove(mut args: Arguments) { pub fn remove(mut args: Arguments) {
@ -96,10 +101,12 @@ pub fn remove(mut args: Arguments) {
TransactionType::Remove(recursive > 0 , cascade, recursive > 1) TransactionType::Remove(recursive > 0 , cascade, recursive > 1)
}; };
aggregator::remove(action, &mut args, &mut cache, &mut logger).aggregate(&mut InstanceCache::new()); match aggregator::remove(action, &mut args, &mut cache, &mut logger) {
Ok(ag) => ag.aggregate(&mut InstanceCache::new()), Err(e) => print_help_error(e),
}
} }
fn create_type(args: &mut Arguments) -> Option<InstanceType> { fn create_type<'a>(args: &mut Arguments) -> Result<Option<InstanceType>, &'a str> {
let mut instype = None; let mut instype = None;
let mut create = false; let mut create = false;
@ -108,14 +115,28 @@ fn create_type(args: &mut Arguments) -> Option<InstanceType> {
while let Some(arg) = args.next() { while let Some(arg) = args.next() {
match arg { match arg {
Operand::Short('c') | Operand::Long("create") => create = true, Operand::Short('c') | Operand::Long("create") => create = true,
Operand::Short('b') | Operand::Long("base") => instype = Some(InstanceType::BASE), Operand::Short('b') | Operand::Long("base") => match instype {
Operand::Short('d') | Operand::Long("slice") => instype = Some(InstanceType::DEP), None => instype = Some(InstanceType::BASE),
Operand::Short('r') | Operand::Long("root") => instype = Some(InstanceType::ROOT), Some(_) => Err("Multiple container types cannot be assigned to a container.")?,
},
Operand::Short('d') | Operand::Long("slice") => match instype {
None => instype = Some(InstanceType::DEP),
Some(_) => Err("Multiple container types cannot be assigned to a container.")?,
},
Operand::Short('r') | Operand::Long("root") => match instype {
None => instype = Some(InstanceType::ROOT),
Some(_) => Err("Multiple container types cannot be assigned to a container.")?,
},
_ => continue, _ => continue,
} }
} }
if create { instype } else { None } match create {
true => match instype {
None => Err("Instance type not specified"), Some(_) => Ok(instype),
},
false => Ok(None)
}
} }
pub fn create(instype: InstanceType, mut targets: Vec<&str>) { pub fn create(instype: InstanceType, mut targets: Vec<&str>) {
@ -199,7 +220,7 @@ pub fn query(mut arguments: Arguments) {
Operand::Short('e') | Operand::Long("explicit") => explicit = true, Operand::Short('e') | Operand::Long("explicit") => explicit = true,
Operand::Short('q') | Operand::Long("quiet") => quiet = true, Operand::Short('q') | Operand::Long("quiet") => quiet = true,
Operand::LongPos("target", t) | Operand::ShortPos(_, t) => target = t, Operand::LongPos("target", t) | Operand::ShortPos(_, t) => target = t,
_ => arguments.invalid_operand(), _ => print_help_error(arguments.invalid_operand()),
} }
} }

View file

@ -11,8 +11,7 @@ use crate::sync::{self,
use crate::config::{InstanceHandle, use crate::config::{InstanceHandle,
InstanceType::ROOT, InstanceType::ROOT,
cache::InstanceCache}; cache::InstanceCache};
use crate::utils::{Arguments, print_help_error}; use crate::utils::{arguments::Operand, Arguments};
use crate::utils::arguments::Operand;
use super::{ use super::{
Transaction, Transaction,
TransactionHandle, TransactionHandle,
@ -190,7 +189,7 @@ impl <'a>TransactionAggregator<'a> {
} }
} }
pub fn remove<'a>(action_type: TransactionType, args: &'a mut Arguments, inscache: &'a mut InstanceCache, log: &'a mut Logger) -> TransactionAggregator<'a> { pub fn remove<'a>(action_type: TransactionType, args: &'a mut Arguments, inscache: &'a mut InstanceCache, log: &'a mut Logger) -> Result<TransactionAggregator<'a>, String> {
let mut action_flags = TransactionFlags::NONE; let mut action_flags = TransactionFlags::NONE;
let mut targets = Vec::new(); let mut targets = Vec::new();
let mut queue: HashMap<Rc<str>,Vec<Rc<str>>> = HashMap::new(); let mut queue: HashMap<Rc<str>,Vec<Rc<str>>> = HashMap::new();
@ -199,7 +198,7 @@ pub fn remove<'a>(action_type: TransactionType, args: &'a mut Arguments, inscach
args.set_index(1); args.set_index(1);
if let Operand::None = args.next().unwrap_or_default() { if let Operand::None = args.next().unwrap_or_default() {
print_help_error("Operation not specified."); Err("Operation not specified.")?
} }
while let Some(arg) = args.next() { while let Some(arg) = args.next() {
@ -228,12 +227,12 @@ pub fn remove<'a>(action_type: TransactionType, args: &'a mut Arguments, inscach
None => { queue.insert(current_target.into(), vec!(package.into())); }, None => { queue.insert(current_target.into(), vec!(package.into())); },
} }
}, },
_ => args.invalid_operand(), _ => Err(args.invalid_operand())?,
} }
} }
if current_target == "" { if current_target == "" {
print_help_error("Target not specified"); Err("Target not specified")?
} }
let current_target = Some(current_target); let current_target = Some(current_target);
@ -244,7 +243,7 @@ pub fn remove<'a>(action_type: TransactionType, args: &'a mut Arguments, inscach
inscache.populate(); inscache.populate();
} }
TransactionAggregator { Ok(TransactionAggregator {
queried: Vec::new(), queried: Vec::new(),
updated: Vec::new(), updated: Vec::new(),
pkg_queue: queue, pkg_queue: queue,
@ -255,10 +254,10 @@ pub fn remove<'a>(action_type: TransactionType, args: &'a mut Arguments, inscach
logger: log, logger: log,
flags: action_flags, flags: action_flags,
target: current_target, target: current_target,
} })
} }
pub fn upgrade<'a>(action_type: TransactionType, args: &'a mut Arguments, inscache: &'a mut InstanceCache, log: &'a mut Logger) -> TransactionAggregator<'a> { pub fn upgrade<'a>(action_type: TransactionType, args: &'a mut Arguments, inscache: &'a mut InstanceCache, log: &'a mut Logger) -> Result<TransactionAggregator<'a>, String> {
let mut action_flags = TransactionFlags::NONE; let mut action_flags = TransactionFlags::NONE;
let mut targets = Vec::new(); let mut targets = Vec::new();
let mut queue: HashMap<Rc<str>,Vec<Rc<str>>> = HashMap::new(); let mut queue: HashMap<Rc<str>,Vec<Rc<str>>> = HashMap::new();
@ -269,7 +268,7 @@ pub fn upgrade<'a>(action_type: TransactionType, args: &'a mut Arguments, inscac
args.set_index(2); args.set_index(2);
if let Operand::None = args.next().unwrap_or_default() { if let Operand::None = args.next().unwrap_or_default() {
print_help_error("Operation not specified."); Err("Operation not specified.")?
} }
while let Some(arg) = args.next() { while let Some(arg) = args.next() {
@ -307,15 +306,14 @@ pub fn upgrade<'a>(action_type: TransactionType, args: &'a mut Arguments, inscac
}, },
} }
}, },
Operand::None => println!("none"), _ => Err(args.invalid_operand())?,
_ => args.invalid_operand(),
} }
} }
let current_target = match target_only { let current_target = match target_only {
true => { true => {
if current_target == "" { if current_target == "" {
print_help_error("Target not specified"); Err("Target not specified")?
} }
Some(current_target) Some(current_target)
@ -329,7 +327,7 @@ pub fn upgrade<'a>(action_type: TransactionType, args: &'a mut Arguments, inscac
inscache.populate(); inscache.populate();
} }
TransactionAggregator { Ok(TransactionAggregator {
queried: Vec::new(), queried: Vec::new(),
updated: Vec::new(), updated: Vec::new(),
pkg_queue: queue, pkg_queue: queue,
@ -340,5 +338,5 @@ pub fn upgrade<'a>(action_type: TransactionType, args: &'a mut Arguments, inscac
logger: log, logger: log,
flags: action_flags, flags: action_flags,
target: current_target, target: current_target,
} })
} }

View file

@ -105,10 +105,10 @@ impl<'a> Arguments<'a> {
self.cur = index; self.cur = index;
} }
pub fn invalid_operand(&self) { pub fn invalid_operand(&self) -> String {
match self.operands.get(self.cur) { match self.operands.get(self.cur) {
Some(oper) => print_help_error(&format!("Invalid option -- '{}'", oper)), Some(oper) => format!("Invalid option -- '{}'", oper),
None => print_help_error(&format!("Operation not specified.")), None => format!("Operation not specified."),
} }
} }