use std::collections::BTreeMap;
use iref::IriRefBuf;
use serde::{Deserialize, Serialize};
use crate::{
document::{self, representation, DIDVerificationMethod, InvalidData},
DIDMethod, Document, PrimaryDIDURL, VerificationMethodDIDResolver, DID, DIDURL,
};
mod composition;
mod dereference;
mod static_resolver;
pub use dereference::*;
pub use static_resolver::StaticDIDResolver;
#[cfg(feature = "http")]
mod http;
#[cfg(feature = "http")]
pub use http::*;
pub const MEDIA_TYPE_URL: &str = "text/url";
#[derive(Debug, thiserror::Error)]
pub enum Error {
#[error("DID method `{0}` not supported")]
MethodNotSupported(String),
#[error("DID document not found")]
NotFound,
#[error("no representation specified")]
NoRepresentation,
#[error("DID representation `{0}` not supported")]
RepresentationNotSupported(String),
#[error(transparent)]
InvalidData(InvalidData),
#[error("invalid method specific identifier: {0}")]
InvalidMethodSpecificId(String),
#[error("invalid options")]
InvalidOptions,
#[error("DID resolver internal error: {0}")]
Internal(String),
}
impl Error {
pub fn internal(error: impl ToString) -> Self {
Self::Internal(error.to_string())
}
pub fn kind(&self) -> ErrorKind {
match self {
Self::MethodNotSupported(_) => ErrorKind::MethodNotSupported,
Self::NotFound => ErrorKind::NotFound,
Self::NoRepresentation => ErrorKind::NoRepresentation,
Self::RepresentationNotSupported(_) => ErrorKind::RepresentationNotSupported,
Self::InvalidData(_) => ErrorKind::InvalidData,
Self::InvalidMethodSpecificId(_) => ErrorKind::InvalidMethodSpecificId,
Self::InvalidOptions => ErrorKind::InvalidOptions,
Self::Internal(_) => ErrorKind::Internal,
}
}
}
impl From<representation::Unknown> for Error {
fn from(value: representation::Unknown) -> Self {
Self::RepresentationNotSupported(value.0)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ErrorKind {
MethodNotSupported,
NotFound,
NoRepresentation,
RepresentationNotSupported,
InvalidData,
InvalidMethodSpecificId,
InvalidOptions,
Internal,
}
pub trait DIDResolverByMethod {
type MethodResolver: DIDMethodResolver;
fn get_method(&self, method_name: &str) -> Option<&Self::MethodResolver>;
fn supports_method(&self, method_name: &str) -> bool {
self.get_method(method_name).is_some()
}
}
impl<T: DIDResolverByMethod> DIDResolver for T {
async fn resolve_representation<'a>(
&'a self,
did: &'a DID,
options: Options,
) -> Result<Output<Vec<u8>>, Error> {
match self.get_method(did.method_name()) {
Some(m) => {
m.resolve_method_representation(did.method_specific_id(), options)
.await
}
None => Err(Error::MethodNotSupported(did.method_name().to_string())),
}
}
}
pub trait DIDResolver {
#[allow(async_fn_in_trait)]
async fn resolve_representation<'a>(
&'a self,
did: &'a DID,
options: Options,
) -> Result<Output<Vec<u8>>, Error>;
#[allow(async_fn_in_trait)]
async fn resolve_with<'a>(&'a self, did: &'a DID, options: Options) -> Result<Output, Error> {
let output = self.resolve_representation(did, options).await?;
match &output.metadata.content_type {
None => Err(Error::NoRepresentation),
Some(ty) => {
let ty: representation::MediaType = ty.parse()?;
output
.try_map(|bytes| Document::from_bytes(ty, &bytes))
.map_err(Error::InvalidData)
}
}
}
#[allow(async_fn_in_trait)]
async fn resolve<'a>(&'a self, did: &'a DID) -> Result<Output, Error> {
self.resolve_with(did, Options::default()).await
}
#[allow(async_fn_in_trait)]
async fn resolve_into_any_verification_method<'a>(
&'a self,
did: &'a DID,
) -> Result<Option<DIDVerificationMethod>, Error> {
Ok(self
.resolve(did)
.await?
.document
.into_document()
.into_any_verification_method())
}
#[allow(async_fn_in_trait)]
async fn dereference_primary<'a>(
&'a self,
primary_did_url: &'a PrimaryDIDURL,
) -> Result<DerefOutput<PrimaryContent>, DerefError> {
self.dereference_primary_with(primary_did_url, Options::default())
.await
}
#[allow(async_fn_in_trait)]
async fn dereference_primary_with<'a>(
&'a self,
primary_did_url: &'a PrimaryDIDURL,
mut resolve_options: Options,
) -> Result<DerefOutput<PrimaryContent>, DerefError> {
resolve_options.extend(match primary_did_url.query() {
Some(query) => serde_urlencoded::from_str(query.as_str()).unwrap(),
None => Options::default(),
});
let parameters = resolve_options.parameters.clone();
let resolution_output = self
.resolve_with(primary_did_url.did(), resolve_options)
.await?;
dereference_primary_resource(self, primary_did_url, parameters, resolution_output).await
}
#[allow(async_fn_in_trait)]
async fn dereference_primary_with_path_or_query<'a>(
&'a self,
_primary_did_url: &'a PrimaryDIDURL,
) -> Result<DerefOutput<PrimaryContent>, DerefError> {
Err(DerefError::NotFound)
}
#[allow(async_fn_in_trait)]
async fn dereference_with<'a>(
&'a self,
did_url: &'a DIDURL,
options: Options,
) -> Result<DerefOutput, DerefError> {
let (primary_did_url, fragment) = did_url.without_fragment();
let primary_deref_output = self
.dereference_primary_with(primary_did_url, options)
.await?;
match fragment {
Some(fragment) => {
dereference_secondary_resource(primary_did_url, fragment, primary_deref_output)
}
None => Ok(primary_deref_output.cast()),
}
}
#[allow(async_fn_in_trait)]
async fn dereference<'a>(&'a self, did_url: &'a DIDURL) -> Result<DerefOutput, DerefError> {
self.dereference_with(did_url, Options::default()).await
}
fn into_vm_resolver_with<M>(self, options: Options) -> VerificationMethodDIDResolver<Self, M>
where
Self: Sized,
{
VerificationMethodDIDResolver::new_with_options(self, options)
}
fn into_vm_resolver<M>(self) -> VerificationMethodDIDResolver<Self, M>
where
Self: Sized,
{
VerificationMethodDIDResolver::new(self)
}
}
pub trait DIDMethodResolver: DIDMethod {
fn method_name(&self) -> &str {
Self::DID_METHOD_NAME
}
#[allow(async_fn_in_trait)]
async fn resolve_method_representation<'a>(
&'a self,
method_specific_id: &'a str,
options: Options,
) -> Result<Output<Vec<u8>>, Error>;
}
impl<'a, T: DIDMethodResolver> DIDMethodResolver for &'a T {
fn method_name(&self) -> &str {
T::method_name(*self)
}
async fn resolve_method_representation<'b>(
&'b self,
method_specific_id: &'b str,
options: Options,
) -> Result<Output<Vec<u8>>, Error> {
T::resolve_method_representation(*self, method_specific_id, options).await
}
}
impl<T: DIDMethodResolver> DIDResolverByMethod for T {
type MethodResolver = Self;
fn get_method(&self, method_name: &str) -> Option<&Self> {
if self.method_name() == method_name {
Some(self)
} else {
None
}
}
}
#[derive(Debug, Clone)]
pub struct Output<T = document::Represented> {
pub document: T,
pub document_metadata: document::Metadata,
pub metadata: Metadata,
}
impl<T> Output<T> {
pub fn from_content(content: T, content_type: Option<String>) -> Self {
Self::new(
content,
document::Metadata::default(),
Metadata::from_content_type(content_type),
)
}
pub fn new(document: T, document_metadata: document::Metadata, metadata: Metadata) -> Self {
Self {
document,
document_metadata,
metadata,
}
}
pub fn try_map<U, E>(self, f: impl FnOnce(T) -> Result<U, E>) -> Result<Output<U>, E> {
Ok(Output {
document: f(self.document)?,
document_metadata: self.document_metadata,
metadata: self.metadata,
})
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
pub struct Options {
#[serde(skip_serializing_if = "Option::is_none")]
pub accept: Option<representation::MediaType>,
#[serde(flatten)]
pub parameters: Parameters,
}
impl Options {
pub fn extend(&mut self, other: Self) {
if let Some(value) = other.accept {
self.accept = Some(value)
}
self.parameters.extend(other.parameters)
}
}
#[derive(Debug, Default, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Parameters {
#[serde(skip_serializing_if = "Option::is_none")]
pub service: Option<String>, #[serde(skip_serializing_if = "Option::is_none", alias = "relative-ref")]
pub relative_ref: Option<IriRefBuf>, #[serde(skip_serializing_if = "Option::is_none")]
pub version_id: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
pub version_time: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
pub hl: Option<String>, #[serde(skip_serializing_if = "Option::is_none")]
pub public_key_format: Option<String>,
#[serde(flatten)]
pub additional: BTreeMap<String, Parameter>,
}
impl Parameters {
pub fn extend(&mut self, other: Self) {
if let Some(value) = other.service {
self.service = Some(value)
}
if let Some(value) = other.relative_ref {
self.relative_ref = Some(value)
}
if let Some(value) = other.version_id {
self.version_id = Some(value)
}
if let Some(value) = other.version_time {
self.version_time = Some(value)
}
if let Some(value) = other.hl {
self.hl = Some(value)
}
if let Some(value) = other.public_key_format {
self.public_key_format = Some(value)
}
self.additional.extend(other.additional);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Parameter {
Null,
String(String),
List(Vec<String>),
}
impl Parameter {
pub fn as_string(&self) -> Option<&str> {
match self {
Self::String(s) => Some(s),
_ => None,
}
}
pub fn into_string(self) -> Result<String, Self> {
match self {
Self::String(s) => Ok(s),
other => Err(other),
}
}
}
#[derive(Debug, Default, Clone, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Metadata {
pub content_type: Option<String>,
}
impl Metadata {
pub fn from_content_type(content_type: Option<String>) -> Self {
Self { content_type }
}
}