use base32::{decode, Alphabet};
use serde::{Deserialize, Serialize};
use std::fmt;
#[cfg(target_arch = "wasm32")]
use wasm_bindgen::prelude::*;
#[cfg(feature = "openapi")]
use utoipa::ToSchema;
use crate::{ParsedUri, Resource};
#[cfg_attr(target_arch = "wasm32", wasm_bindgen)]
#[derive(Serialize, Deserialize, Default, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "openapi", derive(ToSchema))]
pub struct PubkyId(String);
impl PubkyId {
pub fn try_from(s: &str) -> Result<Self, String> {
if s.len() != 52 {
return Err("Validation Error: the string is not 52 utf chars".to_string());
}
match decode(Alphabet::Z, s) {
Some(_) => (),
None => return Err("Validation Error: invalid public key encoding".to_string()),
};
Ok(PubkyId(s.to_string()))
}
pub fn to_uri(&self) -> ParsedUri {
ParsedUri {
user_id: self.clone(),
resource: Resource::User,
}
}
}
#[cfg(not(target_arch = "wasm32"))]
impl From<pubky::PublicKey> for PubkyId {
fn from(pk: pubky::PublicKey) -> Self {
Self(pk.to_z32())
}
}
#[cfg(not(target_arch = "wasm32"))]
impl From<pubky::Keypair> for PubkyId {
fn from(keypair: pubky::Keypair) -> Self {
Self::from(keypair.public_key())
}
}
impl fmt::Display for PubkyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl std::ops::Deref for PubkyId {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl AsRef<str> for PubkyId {
fn as_ref(&self) -> &str {
&self.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(target_arch = "wasm32"))]
use pubky::Keypair;
#[test]
fn test_try_from_valid() {
let valid_key = "operrr8wsbpr3ue9d4qj41ge1kcc6r7fdiy6o3ugjrrhi4y77rdo";
let result = PubkyId::try_from(valid_key);
assert!(result.is_ok());
assert_eq!(result.unwrap().as_ref(), valid_key);
}
#[test]
fn test_try_from_invalid_length() {
let invalid_key = "short";
let result = PubkyId::try_from(invalid_key);
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
"Validation Error: the string is not 52 utf chars"
);
}
#[test]
fn test_try_from_invalid_encoding() {
let invalid_key = "0perrr8wsbpr3ue9d4qj41ge1kcc6r7fdiy6o3ugjrrhi4y77rd0";
let result = PubkyId::try_from(invalid_key);
assert!(result.is_err());
assert_eq!(
result.unwrap_err(),
"Validation Error: invalid public key encoding"
);
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_from_public_key() {
let keypair = Keypair::random();
let public_key = keypair.public_key();
let expected_z32 = public_key.to_z32();
let pubky_id = PubkyId::from(public_key);
assert_eq!(pubky_id.as_ref(), expected_z32);
assert_eq!(pubky_id.to_string(), expected_z32);
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_from_keypair() {
let keypair = Keypair::random();
let expected_z32 = keypair.public_key().to_z32();
let pubky_id = PubkyId::from(keypair);
assert_eq!(pubky_id.as_ref(), expected_z32);
assert_eq!(pubky_id.to_string(), expected_z32);
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_from_public_key_produces_valid_pubky_id() {
let keypair = Keypair::random();
let public_key = keypair.public_key();
let pubky_id = PubkyId::from(public_key);
assert_eq!(pubky_id.as_ref().len(), 52);
let result = PubkyId::try_from(pubky_id.as_ref());
assert!(result.is_ok());
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_from_keypair_produces_valid_pubky_id() {
let keypair = Keypair::random();
let pubky_id = PubkyId::from(keypair);
assert_eq!(pubky_id.as_ref().len(), 52);
let result = PubkyId::try_from(pubky_id.as_ref());
assert!(result.is_ok());
}
#[test]
#[cfg(not(target_arch = "wasm32"))]
fn test_from_keypair_and_public_key_produce_same_result() {
let keypair = Keypair::random();
let public_key = keypair.public_key();
let expected_z32 = public_key.to_z32();
let pubky_id_from_keypair = PubkyId::from(keypair);
let pubky_id_from_public_key = PubkyId::from(public_key);
assert_eq!(pubky_id_from_keypair, pubky_id_from_public_key);
assert_eq!(pubky_id_from_keypair.as_ref(), expected_z32);
}
}