Show [name] for optional positionals, allow filtering import by name too
The custom --help renderer showed every positional as <name> regardless of whether it was actually required, which was misleading for open and transfer (both take an optional profile name). positional_args() now checks Arg::is_required_set() and renders [name] for an optional one. transfer's name positional previously only worked with --export and was rejected outright when combined with --import. It now filters --import the same way: 'porthole transfer <name> -i file.toml' imports just that one profile out of the file instead of everything in it, erroring with a new TransferProfileNotFound if the file doesn't contain it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -188,7 +188,7 @@ pub struct WipeArgs {
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct TransferArgs {
|
||||
/// Name of the profile to export; every saved profile if omitted. Not used for import.
|
||||
/// Restrict export/import to just this profile; every profile in scope if omitted.
|
||||
pub name: Option<String>,
|
||||
|
||||
/// Export saved profiles to a file.
|
||||
|
||||
@@ -19,9 +19,8 @@ pub fn run(args: TransferArgs) -> Result<()> {
|
||||
match (&args.export, &args.import) {
|
||||
(Some(_), Some(_)) => Err(PortholeError::TransferConflictingMode),
|
||||
(None, None) => Err(PortholeError::TransferNoMode),
|
||||
(None, Some(_)) if args.name.is_some() => Err(PortholeError::TransferNameWithImport),
|
||||
(Some(path), None) => export(path, args.name.as_deref()),
|
||||
(None, Some(path)) => import(path),
|
||||
(None, Some(path)) => import(path, args.name.as_deref()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,22 +47,34 @@ fn export(path: &str, name: Option<&str>) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn import(path: &str) -> Result<()> {
|
||||
fn import(path: &str, name: Option<&str>) -> Result<()> {
|
||||
let text = std::fs::read_to_string(path)?;
|
||||
let file: TransferFile = toml::from_str(&text)?;
|
||||
|
||||
for p in &file.profiles {
|
||||
let selected = match name {
|
||||
Some(n) => {
|
||||
let n = profile::normalize(n);
|
||||
let found = file.profiles.into_iter().find(|p| p.name == n);
|
||||
vec![found.ok_or(PortholeError::TransferProfileNotFound(n))?]
|
||||
}
|
||||
None => file.profiles,
|
||||
};
|
||||
|
||||
for p in &selected {
|
||||
profile::require_valid_name(&p.name)?;
|
||||
if profile::exists(&p.name) {
|
||||
return Err(PortholeError::AlreadyExists(p.name.clone()));
|
||||
}
|
||||
}
|
||||
for p in &file.profiles {
|
||||
for p in &selected {
|
||||
profile::save(p)?;
|
||||
}
|
||||
|
||||
ui::ok(&format!("Imported {} profile(s) from '{path}'.", file.profiles.len()));
|
||||
warn_about_missing_identities(&file.profiles);
|
||||
match name {
|
||||
Some(n) => ui::ok(&format!("Imported profile '{n}' from '{path}'.")),
|
||||
None => ui::ok(&format!("Imported {} profile(s) from '{path}'.", selected.len())),
|
||||
}
|
||||
warn_about_missing_identities(&selected);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -35,8 +35,8 @@ pub enum PortholeError {
|
||||
#[error("only one of -i/--import, -e/--export may be given")]
|
||||
TransferConflictingMode,
|
||||
|
||||
#[error("a profile name only applies to -e/--export, not -i/--import")]
|
||||
TransferNameWithImport,
|
||||
#[error("'{0}' not found in the transfer file")]
|
||||
TransferProfileNotFound(String),
|
||||
|
||||
#[error("nothing to do: {0}")]
|
||||
NothingToDo(String),
|
||||
|
||||
@@ -139,10 +139,15 @@ fn print_full_help() {
|
||||
}
|
||||
}
|
||||
|
||||
/// `<name>` for each positional arg of `cmd`, space-joined and colored cyan.
|
||||
/// `<name>` for each required positional arg of `cmd`, `[name]` for an
|
||||
/// optional one, space-joined and colored cyan.
|
||||
fn positional_args(cmd: &clap::Command) -> String {
|
||||
cmd.get_positionals()
|
||||
.map(|a| ui::cyan(&format!("<{}>", a.get_id().as_str())))
|
||||
.map(|a| {
|
||||
let id = a.get_id().as_str();
|
||||
let text = if a.is_required_set() { format!("<{id}>") } else { format!("[{id}]") };
|
||||
ui::cyan(&text)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.join(" ")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user