use anyhow::Result;
use globset::GlobBuilder;
use std::collections::HashSet;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExitStatus {
NotFound = 2,
StoreFull = 3,
Integrity = 4,
}
#[derive(Debug)]
pub struct StatusError {
pub status: ExitStatus,
message: String,
}
impl std::fmt::Display for StatusError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.message)
}
}
impl std::error::Error for StatusError {}
impl StatusError {
pub fn new(status: ExitStatus, message: impl Into<String>) -> Self {
Self {
status,
message: message.into(),
}
}
}
pub fn exit_status(err: &anyhow::Error) -> i32 {
if let Some(e) = err.chain().find_map(|e| e.downcast_ref::<StatusError>()) {
return e.status as i32;
}
#[cfg(not(windows))]
if err.chain().any(|e| {
e.downcast_ref::<yb_core::orchestrator::post_quantum::IntegrityError>()
.is_some()
}) {
return ExitStatus::Integrity as i32;
}
1
}
pub use yb_core::orchestrator::{check_blob_signature, SigVerdict};
fn needs_quoting(name: &str) -> bool {
name.bytes().any(|b| !matches!(b, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'.' | b'-' | b'_' | b'+' | b',' | b'/' | b':'))
}
pub fn quote_name(name: &str) -> String {
if !needs_quoting(name) {
return name.to_owned();
}
let escaped = name.replace('\'', r"'\''");
format!("'{escaped}'")
}
pub fn resolve_patterns(
patterns: &[String],
blob_names: &[String],
ignore_missing: bool,
) -> Result<Vec<String>> {
let mut result: Vec<String> = Vec::new();
let mut seen: HashSet<String> = HashSet::new();
for pattern in patterns {
let is_glob = pattern.chars().any(|c| matches!(c, '*' | '?' | '['));
if is_glob {
let glob = GlobBuilder::new(pattern)
.case_insensitive(false)
.build()?
.compile_matcher();
let hits: Vec<&str> = blob_names
.iter()
.filter(|n| glob.is_match(n.as_str()))
.map(|n| n.as_str())
.collect();
if hits.is_empty() {
if ignore_missing {
continue;
}
return Err(StatusError::new(
ExitStatus::NotFound,
format!("pattern '{pattern}' matched no blobs"),
)
.into());
}
for name in hits {
if seen.insert(name.to_owned()) {
result.push(name.to_owned());
}
}
} else {
if !blob_names.iter().any(|n| n == pattern) {
if ignore_missing {
continue;
}
return Err(StatusError::new(
ExitStatus::NotFound,
format!("blob '{pattern}' not found"),
)
.into());
}
if seen.insert(pattern.clone()) {
result.push(pattern.clone());
}
}
}
Ok(result)
}
pub fn read_back_hint() -> String {
format!(
"To read it back if ever needed: \
yubico-piv-tool -a verify-pin -a read-object --id 0x{:06x}",
yb_core::auxiliaries::OBJ_PRINTED
)
}