use std::io;
use std::path::PathBuf;
#[derive(Debug, thiserror::Error)]
pub enum ProfileError {
#[error("failed to read {}: {source}", path.display())]
Io {
path: PathBuf,
#[source]
source: io::Error,
},
#[error("failed to parse profile: {0}")]
Parse(String),
#[error(
"unknown schema_version {found} (crate max {max}); upgrade wiremux or pin schema_version"
)]
SchemaVersion {
found: u32,
max: u32,
},
#[error("{}", missing_required_field(.0))]
MissingField(&'static str),
#[error("refused: forbidden key name `{0}`")]
ForbiddenKey(String),
#[error("refused: command interpolation in {field} ({trigger})")]
Interpolation {
field: String,
trigger: &'static str,
},
#[error(
"refused: disallowed URL in {field}: {} (https, or http only on 127.0.0.1 / localhost / ::1)",
crate::helpers::redact_url_origin(url)
)]
DisallowedUrl {
field: String,
url: String,
},
#[error("refused: native module path in {field}")]
NativeModule {
field: String,
},
#[error("{}; a .toml or .json path also works", not_found_message(id, known))]
NotFound {
id: String,
known: Vec<String>,
},
}
fn missing_required_field(field: &&str) -> String {
if *field == "schema_version" {
format!(
"missing required field schema_version (set to 1; crate max {})",
super::types::SCHEMA_VERSION_MAX
)
} else {
format!("missing required field {field}")
}
}
pub fn not_found_message(id: &str, known: &[String]) -> String {
let listed = if known.is_empty() {
"(none)".to_string()
} else {
known.join(", ")
};
let mut msg = format!("profile `{id}` not found (known: {listed})");
let refs: Vec<&str> = known.iter().map(String::as_str).collect();
if let Some(suggest) = super::types::suggest_kebab(id, &refs) {
msg.push_str(&format!("; did you mean `{suggest}`"));
}
msg
}