#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ResolvedData {
pub bytes: Vec<u8>,
pub content_type: String,
}
impl ResolvedData {
pub fn new(bytes: Vec<u8>, content_type: String) -> Self {
ResolvedData {
bytes,
content_type,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ResolveOutcome {
Success(ResolvedData),
IntegrityFailure,
Unreachable,
}
impl ResolveOutcome {
pub fn is_success(&self) -> bool {
matches!(self, ResolveOutcome::Success(_))
}
pub fn data(&self) -> Option<&ResolvedData> {
match self {
ResolveOutcome::Success(d) => Some(d),
_ => None,
}
}
pub fn kind(&self) -> &'static str {
match self {
ResolveOutcome::Success(_) => "success",
ResolveOutcome::IntegrityFailure => "integrity_failure",
ResolveOutcome::Unreachable => "unreachable",
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ResolveOptions {
pub endpoint: Option<String>,
pub connect_url: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum ResolveError {
#[error("invalid DIG URN: {0}")]
Parse(String),
#[error("transport error: {0}")]
Transport(String),
#[error("rpc error: {0}")]
Rpc(String),
#[error("resource not found")]
NotFound,
#[error(
"a root-pinned URN is required to verify over the public gateway \
(rootless URNs are not chain-verified there)"
)]
RootRequired,
#[error("inclusion verification failed: {0}")]
VerifyFailed(String),
#[error("decryption failed (wrong key/salt or corrupt ciphertext)")]
DecryptFailed,
}
pub type Result<T> = core::result::Result<T, ResolveError>;
#[allow(async_fn_in_trait)] pub trait UrnResolver {
async fn resolve(&self, urn: &str, opts: &ResolveOptions) -> Result<ResolveOutcome>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn outcome_accessors_are_exhaustive() {
let ok = ResolveOutcome::Success(ResolvedData::new(vec![1, 2], "text/plain".into()));
assert!(ok.is_success());
assert_eq!(ok.kind(), "success");
assert_eq!(ok.data().unwrap().bytes, vec![1, 2]);
assert_eq!(ResolveOutcome::IntegrityFailure.kind(), "integrity_failure");
assert!(!ResolveOutcome::IntegrityFailure.is_success());
assert!(ResolveOutcome::IntegrityFailure.data().is_none());
assert_eq!(ResolveOutcome::Unreachable.kind(), "unreachable");
assert!(ResolveOutcome::Unreachable.data().is_none());
}
#[test]
fn errors_render_stable_messages() {
assert!(ResolveError::RootRequired
.to_string()
.contains("root-pinned"));
assert_eq!(ResolveError::NotFound.to_string(), "resource not found");
}
}