use core::fmt;
use std::error::Error;
use crate::cause::cause;
use crate::did::{DidDocument, DocumentError};
use crate::jose::Jwk;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum Hosting {
Embedded,
Web,
Registry,
}
impl Hosting {
pub const fn as_str(self) -> &'static str {
match self {
Self::Embedded => "embedded",
Self::Web => "web",
Self::Registry => "registry",
}
}
}
impl fmt::Display for Hosting {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub struct MethodCapabilities {
pub updatable: bool,
pub hosting: Hosting,
pub single_key: bool,
pub fixed_fragment: Option<&'static str>,
}
impl MethodCapabilities {
pub const fn new(hosting: Hosting) -> Self {
Self {
updatable: false,
hosting,
single_key: false,
fixed_fragment: None,
}
}
#[must_use]
pub const fn with_updatable(mut self, updatable: bool) -> Self {
self.updatable = updatable;
self
}
#[must_use]
pub const fn with_single_key(mut self, single_key: bool) -> Self {
self.single_key = single_key;
self
}
#[must_use]
pub const fn with_fixed_fragment(mut self, fragment: &'static str) -> Self {
self.fixed_fragment = Some(fragment);
self
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum ProduceError {
#[error("at least one key is required")]
NoKeys,
#[error("at most {max} key(s) can be used, got {actual}")]
TooManyKeys {
max: usize,
actual: usize,
},
#[error("key {index} cannot be used: {reason}{}", cause(.source.as_ref()))]
UnsupportedKey {
index: usize,
reason: String,
source: Option<Box<dyn Error + Send + Sync>>,
},
#[error("invalid parameters: {reason}{}", cause(.source.as_ref()))]
InvalidParams {
reason: String,
source: Option<Box<dyn Error + Send + Sync>>,
},
#[error(transparent)]
Document(#[from] DocumentError),
#[error("backend error: {0}")]
Backend(#[source] Box<dyn Error + Send + Sync>),
}
impl ProduceError {
pub fn unsupported_key(index: usize, reason: impl Into<String>) -> Self {
Self::UnsupportedKey {
index,
reason: reason.into(),
source: None,
}
}
pub fn invalid_params(reason: impl Into<String>) -> Self {
Self::InvalidParams {
reason: reason.into(),
source: None,
}
}
pub fn backend(error: impl Error + Send + Sync + 'static) -> Self {
Self::Backend(Box::new(error))
}
}
#[async_trait::async_trait]
pub trait DidProducer: Send + Sync {
type Params: Send;
fn method(&self) -> &'static str;
fn capabilities(&self) -> MethodCapabilities;
async fn produce(
&self,
keys: &[Jwk],
params: Self::Params,
) -> Result<DidDocument, ProduceError>;
}