xdid-core 0.7.0

Core types for DID methods to implement.
Documentation
use jose_jwk::Jwk;
use serde::{Deserialize, Serialize};
use serde_with::{serde_as, skip_serializing_none};
use smol_str::SmolStr;

use crate::{
    did::Did,
    did_url::{DidUrl, RelativeDidUrl},
};

#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
#[serde_as]
pub struct Document {
    pub id: Did,
    pub also_known_as: Option<Vec<String>>,
    #[serde_as(as = "Option<OneOrMany<_>>")]
    pub controller: Option<Vec<Did>>,
    pub verification_method: Option<Vec<VerificationMethodMap>>,
    pub authentication: Option<Vec<VerificationMethod>>,
    pub assertion_method: Option<Vec<VerificationMethod>>,
    pub key_agreement: Option<Vec<VerificationMethod>>,
    pub capability_invocation: Option<Vec<VerificationMethod>>,
    pub capability_delegation: Option<Vec<VerificationMethod>>,
    pub service: Option<Vec<ServiceEndpoint>>,
}

impl Document {
    /// Returns the verification method that the provided [`DidUrl`] is
    /// referencing, restricted to a given [`VerificationRole`].
    #[must_use]
    pub fn resolve_verification_method_url(
        &self,
        url: &DidUrl,
        role: VerificationRole,
    ) -> Option<VerificationMethodMap> {
        let methods = match role {
            VerificationRole::Assertion => self.assertion_method.as_deref(),
            VerificationRole::Authentication => self.authentication.as_deref(),
            VerificationRole::CapabilityDelegation => self.capability_delegation.as_deref(),
            VerificationRole::CapabilityInvocation => self.capability_invocation.as_deref(),
            VerificationRole::KeyAgreement => self.key_agreement.as_deref(),
        }
        .unwrap_or_default();

        for method in methods {
            if let Some(resolved) = self.resolve_verification_method(method)
                && resolved.id == *url
            {
                return Some(resolved);
            }
        }

        None
    }

    /// Resolves a [`VerificationMethod`] to its [`VerificationMethodMap`].
    /// For embedded maps, returns the map directly. For URL references,
    /// resolves them against this document's `verification_method` array.
    #[must_use]
    pub fn resolve_verification_method(
        &self,
        method: &VerificationMethod,
    ) -> Option<VerificationMethodMap> {
        match method {
            VerificationMethod::Map(map) => Some(*map.clone()),
            VerificationMethod::RelativeUrl(relative_url) => {
                self.resolve_relative_url(relative_url)
            }
            VerificationMethod::Url(url) => {
                // Only resolve if the URL references this document.
                if url.did == self.id
                    && let Some(relative_url) = url.to_relative()
                {
                    return self.resolve_relative_url(&relative_url);
                }
                // TODO: Support additional DID resolution?
                None
            }
        }
    }

    fn resolve_relative_url(&self, url: &RelativeDidUrl) -> Option<VerificationMethodMap> {
        for method in self.verification_method.as_deref().unwrap_or_default() {
            if method.id.to_relative().as_ref() == Some(url) {
                return Some(method.clone());
            }
        }

        None
    }
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum VerificationRole {
    Assertion,
    Authentication,
    CapabilityDelegation,
    CapabilityInvocation,
    KeyAgreement,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde(untagged)]
pub enum VerificationMethod {
    Map(Box<VerificationMethodMap>),
    RelativeUrl(RelativeDidUrl),
    Url(DidUrl),
}

#[skip_serializing_none]
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
pub struct VerificationMethodMap {
    pub id: DidUrl,
    pub controller: Did,
    #[serde(rename = "type")]
    pub typ: SmolStr,
    pub public_key_jwk: Option<Jwk>,
    /// Multibase encoded public key.
    pub public_key_multibase: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
#[serde_as]
pub struct ServiceEndpoint {
    pub id: String,
    #[serde(rename = "type")]
    #[serde_as(as = "OneOrMany<_>")]
    pub typ: Vec<String>,
}