use anyhow::{Result, bail};
use bc_envelope_pattern::{
FormatPathsOpts, Matcher, PathElementFormat, Pattern, format_paths_opt,
};
use clap::Args;
use crate::{EnvelopeArgs, EnvelopeArgsLike};
#[derive(Debug, Args)]
#[group(skip)]
pub struct CommandArgs {
pattern: String,
#[arg(long)]
no_indent: bool,
#[arg(long)]
last_only: bool,
#[arg(long, group = "format")]
envelopes: bool,
#[arg(long, group = "format")]
digests: bool,
#[arg(long, group = "format")]
summary: bool,
#[arg(long, requires = "summary")]
max_length: Option<usize>,
#[command(flatten)]
envelope_args: EnvelopeArgs,
}
impl EnvelopeArgsLike for CommandArgs {
fn envelope(&self) -> Option<&str> { self.envelope_args.envelope() }
}
impl crate::Exec for CommandArgs {
fn exec(&self) -> Result<String> {
let envelope = self.read_envelope()?;
let pattern = Pattern::parse(&self.pattern)
.map_err(|e| {
match e {
bc_envelope_pattern::Error::UnrecognizedToken(span) => {
let input = &self.pattern;
let start = span.start.min(input.len());
let end = span.end.min(input.len());
let error_text = if start < input.len() {
&input[start..end]
} else {
"<end of input>"
};
anyhow::anyhow!(
"Failed to parse pattern at position {}..{}: unrecognized token '{}'\nPattern: {}\n {}^",
start, end, error_text, input,
" ".repeat(start)
)
}
bc_envelope_pattern::Error::ExtraData(span) => {
let input = &self.pattern;
let start = span.start.min(input.len());
anyhow::anyhow!(
"Failed to parse pattern: extra data at position {}\nPattern: {}\n {}^",
start, input, " ".repeat(start)
)
}
bc_envelope_pattern::Error::UnexpectedToken(token, span) => {
let input = &self.pattern;
let start = span.start.min(input.len());
anyhow::anyhow!(
"Failed to parse pattern at position {}: unexpected token {:?}\nPattern: {}\n {}^",
start, token, input, " ".repeat(start)
)
}
_ => anyhow::anyhow!("Failed to parse pattern: {}", e),
}
})?;
let (paths, _captures) = pattern.paths_with_captures(&envelope);
let element_format = if self.envelopes {
PathElementFormat::EnvelopeUR
} else if self.digests {
PathElementFormat::DigestUR
} else if self.summary {
PathElementFormat::Summary(self.max_length)
} else {
PathElementFormat::Summary(None)
};
let format_options = FormatPathsOpts::new()
.indent(!self.no_indent)
.element_format(element_format)
.last_element_only(self.last_only);
if !paths.is_empty() {
Ok(format_paths_opt(&paths, format_options))
} else {
bail!("No match")
}
}
}