use core::fmt;
use core::hash::{Hash, Hasher};
use core::str::FromStr;
use std::error::Error;
use std::sync::Arc;
use indexmap::IndexMap;
use serde::de::Error as _;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_json::Value;
use super::{Did, DidDocument, DidError};
pub const APPLICATION_DID_JSON: &str = "application/did+json";
pub const APPLICATION_DID_LD_JSON: &str = "application/did+ld+json";
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ResolutionErrorKind {
InvalidDid,
NotFound,
MethodNotSupported,
RepresentationNotSupported,
InvalidDidDocument,
InternalError,
Other(String),
}
impl ResolutionErrorKind {
pub fn as_str(&self) -> &str {
match self {
Self::InvalidDid => "invalidDid",
Self::NotFound => "notFound",
Self::MethodNotSupported => "methodNotSupported",
Self::RepresentationNotSupported => "representationNotSupported",
Self::InvalidDidDocument => "invalidDidDocument",
Self::InternalError => "internalError",
Self::Other(keyword) => keyword,
}
}
}
impl fmt::Display for ResolutionErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::InvalidDid => f.write_str("invalid DID"),
Self::NotFound => f.write_str("DID not found"),
Self::MethodNotSupported => f.write_str("DID method not supported"),
Self::RepresentationNotSupported => f.write_str("representation not supported"),
Self::InvalidDidDocument => f.write_str("invalid DID document"),
Self::InternalError => f.write_str("internal resolver error"),
Self::Other(keyword) => write!(f, "resolution error `{keyword}`"),
}
}
}
impl FromStr for ResolutionErrorKind {
type Err = core::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"invalidDid" => Self::InvalidDid,
"notFound" => Self::NotFound,
"methodNotSupported" => Self::MethodNotSupported,
"representationNotSupported" => Self::RepresentationNotSupported,
"invalidDidDocument" => Self::InvalidDidDocument,
"internalError" => Self::InternalError,
other => Self::Other(other.to_owned()),
})
}
}
#[derive(Debug, Clone)]
pub struct ResolutionError {
kind: ResolutionErrorKind,
detail: Option<String>,
source: Option<Arc<dyn Error + Send + Sync + 'static>>,
}
impl ResolutionError {
pub const fn new(kind: ResolutionErrorKind) -> Self {
Self {
kind,
detail: None,
source: None,
}
}
#[must_use]
pub fn with_detail(mut self, detail: impl Into<String>) -> Self {
self.detail = Some(detail.into());
self
}
#[must_use]
pub fn with_source<E: Error + Send + Sync + 'static>(mut self, source: E) -> Self {
self.source = Some(Arc::new(source));
self
}
pub const fn kind(&self) -> &ResolutionErrorKind {
&self.kind
}
pub fn as_str(&self) -> &str {
self.kind.as_str()
}
pub fn detail(&self) -> Option<&str> {
self.detail.as_deref()
}
}
impl fmt::Display for ResolutionError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.kind, f)?;
if let Some(detail) = &self.detail {
write!(f, ": {detail}")?;
}
if let Some(source) = &self.source {
write!(f, ": {source}")?;
}
Ok(())
}
}
impl Error for ResolutionError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.source
.as_deref()
.map(|source| source as &(dyn Error + 'static))
}
}
impl PartialEq for ResolutionError {
fn eq(&self, other: &Self) -> bool {
self.kind == other.kind
}
}
impl Eq for ResolutionError {}
impl PartialEq<ResolutionErrorKind> for ResolutionError {
fn eq(&self, other: &ResolutionErrorKind) -> bool {
self.kind == *other
}
}
impl Hash for ResolutionError {
fn hash<H: Hasher>(&self, state: &mut H) {
self.kind.hash(state);
}
}
impl From<ResolutionErrorKind> for ResolutionError {
fn from(kind: ResolutionErrorKind) -> Self {
Self::new(kind)
}
}
impl FromStr for ResolutionError {
type Err = core::convert::Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse().map(Self::new)
}
}
impl From<DidError> for ResolutionError {
fn from(error: DidError) -> Self {
Self::new(ResolutionErrorKind::InvalidDid).with_source(error)
}
}
impl Serialize for ResolutionError {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.as_str())
}
}
impl<'de> Deserialize<'de> for ResolutionError {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let keyword = String::deserialize(deserializer)?;
if keyword.is_empty() || !keyword.is_ascii() {
return Err(D::Error::custom("error must be an ASCII keyword"));
}
keyword.parse().map_err(|never| match never {})
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ResolutionOptions {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub accept: Option<String>,
#[serde(flatten)]
pub properties: IndexMap<String, Value>,
}
impl ResolutionOptions {
pub fn accept(media_type: impl Into<String>) -> Self {
Self {
accept: Some(media_type.into()),
properties: IndexMap::new(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ResolutionMetadata {
#[serde(
rename = "contentType",
default,
skip_serializing_if = "Option::is_none"
)]
pub content_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub error: Option<ResolutionError>,
#[serde(flatten)]
pub properties: IndexMap<String, Value>,
}
impl ResolutionMetadata {
pub fn error(error: impl Into<ResolutionError>) -> Self {
Self {
content_type: None,
error: Some(error.into()),
properties: IndexMap::new(),
}
}
pub fn content_type(media_type: impl Into<String>) -> Self {
Self {
content_type: Some(media_type.into()),
error: None,
properties: IndexMap::new(),
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct DocumentMetadata {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub created: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub updated: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deactivated: Option<bool>,
#[serde(
rename = "nextUpdate",
default,
skip_serializing_if = "Option::is_none"
)]
pub next_update: Option<String>,
#[serde(rename = "versionId", default, skip_serializing_if = "Option::is_none")]
pub version_id: Option<String>,
#[serde(
rename = "nextVersionId",
default,
skip_serializing_if = "Option::is_none"
)]
pub next_version_id: Option<String>,
#[serde(
rename = "equivalentId",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub equivalent_id: Vec<Did>,
#[serde(
rename = "canonicalId",
default,
skip_serializing_if = "Option::is_none"
)]
pub canonical_id: Option<Did>,
#[serde(flatten)]
pub properties: IndexMap<String, Value>,
}
impl DocumentMetadata {
pub fn is_deactivated(&self) -> bool {
self.deactivated == Some(true)
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct ResolutionResult {
#[serde(
rename = "didDocument",
default,
skip_serializing_if = "Option::is_none"
)]
pub document: Option<DidDocument>,
#[serde(rename = "didResolutionMetadata", default)]
pub resolution_metadata: ResolutionMetadata,
#[serde(rename = "didDocumentMetadata", default)]
pub document_metadata: DocumentMetadata,
}
impl ResolutionResult {
pub fn new(document: DidDocument) -> Self {
Self {
document: Some(document),
resolution_metadata: ResolutionMetadata::default(),
document_metadata: DocumentMetadata::default(),
}
}
pub fn error(error: impl Into<ResolutionError>) -> Self {
Self {
document: None,
resolution_metadata: ResolutionMetadata::error(error),
document_metadata: DocumentMetadata::default(),
}
}
#[must_use]
pub fn with_document_metadata(mut self, metadata: DocumentMetadata) -> Self {
self.document_metadata = metadata;
self
}
#[must_use]
pub fn with_resolution_metadata(mut self, metadata: ResolutionMetadata) -> Self {
self.resolution_metadata = metadata;
self
}
pub fn error_kind(&self) -> Option<&ResolutionErrorKind> {
self.resolution_metadata
.error
.as_ref()
.map(ResolutionError::kind)
}
pub fn into_document(self) -> Result<DidDocument, ResolutionError> {
match (self.document, self.resolution_metadata.error) {
(_, Some(error)) => Err(error),
(Some(document), None) => Ok(document),
(None, None) => Err(ResolutionErrorKind::NotFound.into()),
}
}
}
impl fmt::Display for ResolutionResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match serde_json::to_string(self) {
Ok(json) => f.write_str(&json),
Err(e) => write!(f, "<unserializable resolution result: {e}>"),
}
}
}