oas-document 1.1.1

OAS Identity Document types and validation for the Open Agent Specification
Documentation
// OAS Spec §5.1 — verificationMethod: Ed25519VerificationKey2020
//! Verification method types for OAS Identity Documents.

use serde::{Deserialize, Serialize};

/// The fixed verification method type per OAS Specification §5.1.
pub const VERIFICATION_METHOD_TYPE: &str = "Ed25519VerificationKey2020";

/// An Ed25519 verification method as defined in OAS Identity Documents.
///
/// See OAS Specification §5.1 for the complete structure.
///
/// # Examples
///
/// ```
/// use oas_document::verification_method::VerificationMethod;
///
/// let vm = VerificationMethod {
///     id: "did:oas:acme:hmr:alice#key-1".to_string(),
///     method_type: "Ed25519VerificationKey2020".to_string(),
///     controller: "did:oas:acme:hmr:alice".to_string(),
///     public_key_multibase: "z6Mktest123".to_string(),
/// };
/// assert_eq!(vm.key_id(), "key-1");
/// ```
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VerificationMethod {
    /// The full key ID (e.g., `did:oas:acme:hmr:alice#key-1`).
    pub id: String,

    /// The verification method type. MUST be `Ed25519VerificationKey2020`.
    #[serde(rename = "type")]
    pub method_type: String,

    /// The DID that controls this key.
    pub controller: String,

    /// The public key encoded as multibase base58btc (with `z` prefix).
    pub public_key_multibase: String,
}

impl VerificationMethod {
    /// Extracts the key fragment (the part after `#`) from the ID.
    ///
    /// # Returns
    ///
    /// The key fragment, or the full ID if no `#` is present.
    ///
    /// # Examples
    ///
    /// ```
    /// use oas_document::verification_method::VerificationMethod;
    ///
    /// let vm = VerificationMethod {
    ///     id: "did:oas:ns:hmr:a#key-1".to_string(),
    ///     method_type: "Ed25519VerificationKey2020".to_string(),
    ///     controller: "did:oas:ns:hmr:a".to_string(),
    ///     public_key_multibase: "zTest".to_string(),
    /// };
    /// assert_eq!(vm.key_id(), "key-1");
    /// ```
    pub fn key_id(&self) -> &str {
        self.id
            .rsplit_once('#')
            .map(|(_, frag)| frag)
            .unwrap_or(&self.id)
    }

    /// Creates a new verification method for an OAS entity.
    ///
    /// # Arguments
    ///
    /// * `did` - The entity's DID string.
    /// * `key_num` - The key number (used to form the fragment, e.g., `key-1`).
    /// * `public_key_multibase` - The multibase-encoded public key.
    pub fn new(did: &str, key_num: u32, public_key_multibase: &str) -> Self {
        Self {
            id: format!("{did}#key-{key_num}"),
            method_type: VERIFICATION_METHOD_TYPE.to_string(),
            controller: did.to_string(),
            public_key_multibase: public_key_multibase.to_string(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn new_creates_correct_structure() {
        let vm = VerificationMethod::new("did:oas:ns:hmr:alice", 1, "zPubKey");
        assert_eq!(vm.id, "did:oas:ns:hmr:alice#key-1");
        assert_eq!(vm.method_type, VERIFICATION_METHOD_TYPE);
        assert_eq!(vm.controller, "did:oas:ns:hmr:alice");
        assert_eq!(vm.public_key_multibase, "zPubKey");
    }

    #[test]
    fn key_id_extracts_fragment() {
        let vm = VerificationMethod::new("did:oas:ns:hmr:alice", 2, "zKey");
        assert_eq!(vm.key_id(), "key-2");
    }

    #[test]
    fn serde_round_trip() {
        let vm = VerificationMethod::new("did:oas:ns:agent:bot", 1, "zPubKey123");
        let json = serde_json::to_string(&vm).unwrap_or_default();
        let deserialized: VerificationMethod =
            serde_json::from_str(&json).unwrap_or_else(|_| vm.clone());
        assert_eq!(vm, deserialized);
    }

    #[test]
    fn json_uses_camel_case() {
        let vm = VerificationMethod::new("did:oas:ns:hmr:a", 1, "zK");
        let json = serde_json::to_string(&vm).unwrap_or_default();
        assert!(json.contains("publicKeyMultibase"));
        assert!(json.contains(r#""type":"Ed25519VerificationKey2020""#));
    }
}