use std::fmt;
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum Error {
#[error("no object at `{0}`")]
NotFound(String),
#[error("`{uri}` names {} objects; name the version: {}", candidates.len(), candidates.join(", "))]
Ambiguous {
uri: String,
candidates: Vec<String>,
},
#[error("`{uri}` is not usable here: {reason}")]
BadUri {
uri: String,
reason: String,
},
#[error("`{location}`: {reason}")]
OutOfBounds {
location: String,
reason: String,
},
#[error("`{location}`: {reason}")]
TooLarge {
location: String,
reason: String,
},
#[error("unknown type `{0}` - is the generated crate registered?")]
UnknownType(String),
#[error("package error: {0}")]
Package(String),
#[error("{operation} is not supported by {store}")]
Unsupported {
operation: &'static str,
store: &'static str,
},
#[error("`{source_type}`: the kind of its edges is not stated: {why}")]
UnstatedKind {
source_type: String,
why: &'static str,
},
#[error("{store}: {reason}")]
Transaction {
store: &'static str,
reason: String,
},
#[error("external data `{location}`: {reason}")]
ExternalData {
location: String,
reason: ExternalDataReason,
},
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("{store}: {reason}")]
Remote {
store: &'static str,
reason: String,
},
#[error("XML error: {0}")]
Xml(String),
#[error(transparent)]
Core(#[from] energyml_core::error::Error),
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ExternalDataReason {
FeatureNotEnabled {
feature: &'static str,
mime: Option<String>,
},
FileMissing,
PathMissing(String),
UuidMismatch {
expected: String,
found: String,
},
Backend(String),
ShapeDisagrees {
promised: i64,
held: usize,
},
}
impl fmt::Display for ExternalDataReason {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FeatureNotEnabled { feature, mime } => match mime {
Some(mime) => write!(f, "no support for `{mime}` (build with the `{feature}` feature)"),
None => write!(f, "no support compiled in (build with the `{feature}` feature)"),
},
Self::FileMissing => write!(f, "file not found"),
Self::PathMissing(path) => write!(f, "no dataset at `{path}` in the file"),
Self::UuidMismatch { expected, found } => {
write!(
f,
"the file carries uuid `{found}`, the package expects `{expected}`"
)
}
Self::Backend(reason) => write!(f, "{reason}"),
Self::ShapeDisagrees { promised, held } => write!(
f,
"the shape promises {promised} value(s) and the block holds {held}"
),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum Severity {
Warning,
Note,
}
#[derive(Debug, Clone)]
pub struct Diagnostic {
pub severity: Severity,
pub part: String,
pub message: String,
}
impl Diagnostic {
pub fn warning(part: impl Into<String>, message: impl Into<String>) -> Self {
Self {
severity: Severity::Warning,
part: part.into(),
message: message.into(),
}
}
pub(crate) fn nil_uuid(part: impl Into<String>, path: Option<&str>) -> Self {
let prefix = path.map(|p| format!("`{p}`: ")).unwrap_or_default();
Self::warning(
part,
format!(
"{prefix}uuid `00000000-0000-0000-0000-000000000000` is the nil UUID (RFC 4122 section 4.1.7): \
it identifies no instance, so objects that carry it cannot be told apart \
(Energistics Identifier Specification v5.0 section 2.3.1)"
),
)
}
pub fn note(part: impl Into<String>, message: impl Into<String>) -> Self {
Self {
severity: Severity::Note,
part: part.into(),
message: message.into(),
}
}
}
impl fmt::Display for Diagnostic {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let tag = match self.severity {
Severity::Warning => "warning",
Severity::Note => "note",
};
if self.part.is_empty() {
write!(f, "[{tag}] {}", self.message)
} else {
write!(f, "[{tag}] {}: {}", self.part, self.message)
}
}
}