Skip to content

Commit

Permalink
Started work on did-simple and did-chain
Browse files Browse the repository at this point in the history
  • Loading branch information
TheButlah committed May 8, 2024
1 parent e790104 commit 2fc704b
Show file tree
Hide file tree
Showing 11 changed files with 452 additions and 8 deletions.
32 changes: 24 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ members = [
"apps/social/networking",
"apps/social/server",
"crates/bevy_egui_keyboard",
"crates/did-chain",
"crates/did-simple",
"crates/egui-picking",
"crates/picking-xr",
"crates/replicate/client",
Expand Down
11 changes: 11 additions & 0 deletions crates/did-chain/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[package]
name = "did-chain"
version.workspace = true
license.workspace = true
repository.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "A chain of Decentralized Identifiers"

[dependencies]
did-simple.path = "../did-simple"
12 changes: 12 additions & 0 deletions crates/did-chain/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use did_simple::{methods::key::DidKey, methods::DidDyn};

/// This is like an account UUID, it provides a unique identifier for the
/// account. Changing it is impossible.
#[derive(Debug)]
pub struct DidRoot(DidKey);

#[derive(Debug)]
pub struct DidChain {
pub root: DidRoot,
pub chain: Vec<DidDyn>,
}
15 changes: 15 additions & 0 deletions crates/did-simple/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[package]
name = "did-simple"
version.workspace = true
license.workspace = true
repository.workspace = true
edition.workspace = true
rust-version.workspace = true
description = "Dead simple DIDs"

[dependencies]
thiserror = "1.0.60"
bytes = "1.6.0"

[dev-dependencies]
eyre = "0.6.12"
11 changes: 11 additions & 0 deletions crates/did-simple/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#![forbid(unsafe_code)]

use std::str::FromStr;

pub mod methods;
pub mod uri;
pub mod utf8bytes;

pub trait Did: FromStr {
fn uri(&self) -> self::uri::DidUri;
}
8 changes: 8 additions & 0 deletions crates/did-simple/src/methods/key.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! An implementation of the [did:key] method.
//!
//! [did:key]: https://w3c-ccg.github.io/did-method-key/
/// An implementation of the `did:key` method. See the [module](self) docs for more
/// info.
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub struct DidKey;
10 changes: 10 additions & 0 deletions crates/did-simple/src/methods/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
pub mod key;
pub mod web;

/// Dynamically typed did method.
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
#[non_exhaustive]
pub enum DidDyn {
Key(self::key::DidKey),
Web(self::web::DidWeb),
}
8 changes: 8 additions & 0 deletions crates/did-simple/src/methods/web.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
//! An implementation of the [did:web] method.
//!
//! [did:web]: https://w3c-ccg.github.io/did-method-web
/// An implementation of the `did:web` method. See the [module](self) docs for more
/// info.
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
pub struct DidWeb;
159 changes: 159 additions & 0 deletions crates/did-simple/src/uri.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
use std::str::FromStr;

use bytes::Bytes;

use crate::utf8bytes::Utf8Bytes;

#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
pub enum DidMethod {
Key,
Web,
}

impl FromStr for DidMethod {
type Err = ParseError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(match s {
"key" => Self::Key,
"web" => Self::Web,
"" => return Err(ParseError::MissingMethod),
_ => return Err(ParseError::UnknownMethod),
})
}
}

/// Helper type to access data in the method-specific-id of a [`DidUri`].
pub struct MethodSpecificId<'a>(&'a DidUri);

impl MethodSpecificId<'_> {
pub fn as_str(&self) -> &str {
&(self.0.as_str()[self.0.method_specific_id.clone()])
}

pub fn as_slice(&self) -> &[u8] {
&(self.0.s.as_slice()[self.0.method_specific_id.clone()])
}

pub fn utf8_bytes(&self) -> Utf8Bytes {
self.0.s.clone().split_off(self.0.method_specific_id.start)
}
}

#[derive(Debug, Eq, PartialEq, Hash)]
pub struct DidUri {
method: DidMethod,
/// 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<usize>,
}

impl DidUri {
/// 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 byte slice that is guaranteed to be utf8.
pub fn 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
}

/// Method-specific identity info.
pub fn method_specific_id(&self) -> MethodSpecificId {
MethodSpecificId(self)
}

pub fn into_inner(self) -> Utf8Bytes {
self.s
}
}

impl FromStr for DidUri {
type Err = ParseError;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let (method, remaining) = s
.strip_prefix("did:")
.ok_or(ParseError::InvalidScheme)?
.split_once(':')
.ok_or(ParseError::MissingMethod)?;
let method = DidMethod::from_str(method)?;
let start_idx = s.len() - remaining.len();

Ok(DidUri {
method,
s: Utf8Bytes::from(s.to_owned()),
method_specific_id: (start_idx..),
})
}
}

impl TryFrom<String> for DidUri {
type Error = ParseError;

fn try_from(s: String) -> Result<Self, Self::Error> {
let (method, remaining) = s
.strip_prefix("did:")
.ok_or(ParseError::InvalidScheme)?
.split_once(':')
.ok_or(ParseError::MissingMethod)?;
let method = DidMethod::from_str(method)?;
let start_idx = s.len() - remaining.len();

Ok(DidUri {
method,
s: Utf8Bytes::from(s),
method_specific_id: (start_idx..),
})
}
}

#[derive(Debug, thiserror::Error)]
pub enum ParseError {
#[error("expected the did: scheme")]
InvalidScheme,
#[error("expected did:method, but method was not present")]
MissingMethod,
#[error("encountered unknown did:method")]
UnknownMethod,
}

#[cfg(test)]
mod test {
use super::*;
use eyre::{Result, WrapErr};

#[test]
fn test_parse() -> Result<()> {
let test_cases = [DidUri {
method: DidMethod::Key,
s: String::from("did:key:123456").into(),
method_specific_id: (8..),
}];
for expected in 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")?;
assert_eq!(from_str, try_from);
assert_eq!(from_str, expected);
}
Ok(())
}
}
Loading

0 comments on commit 2fc704b

Please sign in to comment.