From 0957e1e93d58b5ea74bd611587eaeee9dc56b98d Mon Sep 17 00:00:00 2001 From: Ryan Butler Date: Mon, 13 May 2024 23:59:55 -0400 Subject: [PATCH] Added display impls and started filling out DidKey struct --- crates/did-simple/src/methods/key.rs | 99 +++++++++++++++++++++++++++- crates/did-simple/src/uri.rs | 46 +++++++------ 2 files changed, 124 insertions(+), 21 deletions(-) diff --git a/crates/did-simple/src/methods/key.rs b/crates/did-simple/src/methods/key.rs index 6bbd911..0f40eca 100644 --- a/crates/did-simple/src/methods/key.rs +++ b/crates/did-simple/src/methods/key.rs @@ -2,7 +2,104 @@ //! //! [did:key]: https://w3c-ccg.github.io/did-method-key/ +use std::fmt::Display; + +use crate::{ + uri::{DidMethod, DidUri}, + utf8bytes::Utf8Bytes, +}; + /// An implementation of the `did:key` method. See the [module](self) docs for more /// info. #[derive(Debug, Eq, PartialEq, Hash, Clone)] -pub struct DidKey; +pub struct DidKey { + /// The string representation of the DID. + s: Utf8Bytes, + /// The substring for method-specific-id. This is a range index into `s`. + method_specific_id: std::ops::RangeFrom, +} + +impl DidKey { + const PREFIX: &'static str = "did:key:"; + + /// Gets the buffer representing the uri as a str. + pub fn as_str(&self) -> &str { + self.s.as_str() + } + + /// Gets the buffer representing the uri as a byte slice. + pub fn as_slice(&self) -> &[u8] { + self.s.as_slice() + } + + /// Gets the buffer representing the uri as a reference counted slice that + /// is guaranteed to be utf8. + pub fn as_utf8_bytes(&self) -> &Utf8Bytes { + &self.s + } +} + +impl TryFrom for DidKey { + type Error = FromUriError; + + fn try_from(value: DidUri) -> Result { + let m = value.method(); + if m != DidMethod::Key { + return Err(FromUriError::WrongMethod(m)); + } + debug_assert_eq!( + value.as_slice().len() - value.method_specific_id().as_slice().len(), + Self::PREFIX.len(), + "sanity check that prefix has expected length" + ); + + Ok(Self { + s: value.as_utf8_bytes().clone(), + method_specific_id: (Self::PREFIX.len()..), + }) + } +} + +#[derive(thiserror::Error, Debug)] +pub enum FromUriError { + #[error("Expected \"key\" method but got {0:?}")] + WrongMethod(DidMethod), +} + +impl Display for DidKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_str().fmt(f) + } +} + +#[cfg(test)] +mod test { + use super::*; + + use eyre::WrapErr; + use std::str::FromStr; + + fn common_examples() -> Vec { + let p = DidKey::PREFIX; + let make_example = |s| DidKey { + s: format!("{p}{s}").into(), + method_specific_id: (p.len()..), + }; + ["deadbeef123", "yeet"] + .into_iter() + .map(make_example) + .collect() + } + + #[test] + fn test_try_from_uri() -> eyre::Result<()> { + for example in common_examples() { + let uri = DidUri::from_str(example.as_str()) + .wrap_err_with(|| format!("failed to parse DidUri from {example}"))?; + let key_from_uri = DidKey::try_from(uri.clone()) + .wrap_err_with(|| format!("failed to parse DidKey from {uri}"))?; + assert_eq!(uri.as_str(), key_from_uri.as_str()); + } + Ok(()) + } +} diff --git a/crates/did-simple/src/uri.rs b/crates/did-simple/src/uri.rs index cdccce6..726de29 100644 --- a/crates/did-simple/src/uri.rs +++ b/crates/did-simple/src/uri.rs @@ -1,6 +1,4 @@ -use std::str::FromStr; - -use bytes::Bytes; +use std::{fmt::Display, str::FromStr}; use crate::utf8bytes::Utf8Bytes; @@ -40,7 +38,7 @@ impl MethodSpecificId<'_> { } } -#[derive(Debug, Eq, PartialEq, Hash)] +#[derive(Debug, Eq, PartialEq, Hash, Clone)] pub struct DidUri { method: DidMethod, /// The string representation of the DID. @@ -60,16 +58,12 @@ impl DidUri { self.s.as_slice() } - /// Gets the buffer representing the uri as a byte slice that is guaranteed to be utf8. - pub fn utf8_bytes(&self) -> &Utf8Bytes { + /// Gets the buffer representing the uri as a reference counted slice that + /// is guaranteed to be utf8. + pub fn as_utf8_bytes(&self) -> &Utf8Bytes { &self.s } - /// Gets the buffer representing the uri as bytes. - pub fn bytes(&self) -> &Bytes { - self.s.bytes() - } - /// The method of the did. pub fn method(&self) -> DidMethod { self.method @@ -79,10 +73,6 @@ impl DidUri { pub fn method_specific_id(&self) -> MethodSpecificId { MethodSpecificId(self) } - - pub fn into_inner(self) -> Utf8Bytes { - self.s - } } impl FromStr for DidUri { @@ -135,19 +125,28 @@ pub enum ParseError { UnknownMethod, } +impl Display for DidUri { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + self.as_str().fmt(f) + } +} + #[cfg(test)] mod test { use super::*; use eyre::{Result, WrapErr}; - #[test] - fn test_parse() -> Result<()> { - let test_cases = [DidUri { + fn common_test_cases() -> Vec { + vec![DidUri { method: DidMethod::Key, s: String::from("did:key:123456").into(), method_specific_id: (8..), - }]; - for expected in test_cases { + }] + } + + #[test] + fn test_parse() -> Result<()> { + for expected in common_test_cases() { let s = expected.s.as_str().to_owned(); let from_str = DidUri::from_str(&s).wrap_err("failed to from_str")?; let try_from = DidUri::try_from(s).wrap_err("failed to try_from")?; @@ -156,4 +155,11 @@ mod test { } Ok(()) } + + #[test] + fn test_display() { + for example in common_test_cases() { + assert_eq!(example.as_str(), format!("{example}")); + } + } }