use std::{ cmp::Ordering, convert::{TryFrom, TryInto}, fmt, hash::{Hash, Hasher}, marker::PhantomData, num::NonZeroU8, str::FromStr, }; use crate::{crypto_algorithms::SigningKeyAlgorithm, DeviceId, KeyName}; /// A key algorithm and key name delimited by a colon pub struct KeyId { full_id: Box, colon_idx: NonZeroU8, _phantom: PhantomData<(A, K)>, } impl KeyId { /// Creates a new `KeyId` from an algorithm and key name. pub fn from_parts(algorithm: A, key_name: &K) -> Self where A: AsRef, K: AsRef, { let algorithm = algorithm.as_ref(); let key_name = key_name.as_ref(); let mut res = String::with_capacity(algorithm.len() + 1 + key_name.len()); res.push_str(algorithm); res.push(':'); res.push_str(key_name); let colon_idx = NonZeroU8::new(algorithm.len().try_into().expect("no algorithm name len > 255")) .expect("no empty algorithm name"); KeyId { full_id: res.into(), colon_idx, _phantom: PhantomData } } /// Returns key algorithm of the key ID. pub fn algorithm(&self) -> A where A: FromStr, { A::from_str(&self.full_id[..self.colon_idx.get() as usize]) .unwrap_or_else(|_| unreachable!()) } /// Returns the key name of the key ID. pub fn key_name<'a>(&'a self) -> &'a K where &'a K: From<&'a str>, { self.full_id[self.colon_idx.get() as usize + 1..].into() } } fn try_from(key_identifier: S) -> Result, crate::Error> where S: AsRef + Into>, { let colon_idx = ruma_identifiers_validation::key_id::validate(key_identifier.as_ref())?; Ok(KeyId { full_id: key_identifier.into(), colon_idx, _phantom: PhantomData }) } impl KeyId { /// Creates a string slice from this `KeyId` pub fn as_str(&self) -> &str { &self.full_id } /// Creates a byte slice from this `KeyId` pub fn as_bytes(&self) -> &[u8] { self.full_id.as_bytes() } } impl Clone for KeyId { fn clone(&self) -> Self { Self { full_id: self.full_id.clone(), colon_idx: self.colon_idx, _phantom: PhantomData } } } impl AsRef for KeyId { fn as_ref(&self) -> &str { self.as_str() } } impl From> for String { fn from(id: KeyId) -> Self { id.full_id.into() } } impl FromStr for KeyId { type Err = crate::Error; fn from_str(s: &str) -> Result { try_from(s) } } impl TryFrom<&str> for KeyId { type Error = crate::Error; fn try_from(s: &str) -> Result { try_from(s) } } impl TryFrom for KeyId where A: FromStr, K: From, { type Error = crate::Error; fn try_from(s: String) -> Result { try_from(s) } } impl fmt::Debug for KeyId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { // Using struct debug format for consistency with other ID types. // FIXME: Change all ID types to have just a string debug format? f.debug_struct("KeyId") .field("full_id", &self.full_id) .field("colon_idxs", &self.colon_idx) .finish() } } impl fmt::Display for KeyId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.as_str()) } } impl PartialEq for KeyId { fn eq(&self, other: &Self) -> bool { self.as_str() == other.as_str() } } impl Eq for KeyId {} impl PartialOrd for KeyId { fn partial_cmp(&self, other: &Self) -> Option { self.as_str().partial_cmp(other.as_str()) } } impl Ord for KeyId { fn cmp(&self, other: &Self) -> Ordering { self.as_str().cmp(other.as_str()) } } impl Hash for KeyId { fn hash(&self, state: &mut H) { self.as_str().hash(state); } } #[cfg(feature = "serde")] impl serde::Serialize for KeyId { fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { serializer.serialize_str(self.as_str()) } } #[cfg(feature = "serde")] impl<'de, A, K: ?Sized> serde::Deserialize<'de> for KeyId { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { crate::deserialize_id(deserializer, "Key name with algorithm and key identifier") } } #[rustfmt::skip] partial_eq_string!(KeyId [A, K]); /// Algorithm + key name for signing keys. pub type SigningKeyId = KeyId; /// Algorithm + key name for homeserver signing keys. pub type ServerSigningKeyId = SigningKeyId; /// Algorithm + key name for device keys. pub type DeviceSigningKeyId = SigningKeyId;