use std::{
fmt::{Debug, Display, Formatter},
hash::Hash,
ops::Deref,
str::FromStr,
};
use base64::{display::Base64Display, Engine};
#[derive(thiserror::Error, Debug, PartialEq)]
pub enum PeerPubkeyError {
#[error("Unable to parse PeerPubkey from provided string")]
ParseFailed,
}
pub(crate) const PEER_PUBKEY_PREFIX: &str = "pk";
pub(crate) const PEER_PUBKEY_B64_ENGINE: base64::engine::general_purpose::GeneralPurpose =
base64::engine::general_purpose::URL_SAFE_NO_PAD;
#[cfg(feature = "stabby")]
macro_rules! vec {
($value: expr; $size: expr) => {{
let mut buffer = Vec::with_capacity($size);
for _ in 0..buffer.capacity() {
buffer.push($value);
}
buffer
}};
}
use _impl::{ArcSlice, Vec};
#[cfg(feature = "stabby")]
mod _impl {
use std::ops::Not;
use safer_ffi::layout::ReprC;
pub use stabby::{sync::ArcSlice, vec::Vec};
use super::*;
unsafe impl ReprC for PeerPubkey {
type CLayout = <[*const (); 2] as ReprC>::CLayout;
fn is_valid(it: &'_ Self::CLayout) -> bool {
unsafe { <Self as stabby::IStable>::is_invalid(it as *const _ as *const u8).not() }
}
}
impl From<ArcSlice<u8>> for PeerPubkey {
fn from(vec: ArcSlice<u8>) -> Self {
Self(vec)
}
}
impl From<Vec<u8>> for PeerPubkey {
fn from(vec: Vec<u8>) -> Self {
Self(vec.into())
}
}
impl From<&std::sync::Arc<[u8]>> for PeerPubkey {
fn from(slice: &std::sync::Arc<[u8]>) -> Self {
Self((&**slice).into())
}
}
}
#[cfg(not(feature = "stabby"))]
mod _impl {
pub use std::vec::Vec;
use super::*;
pub type ArcSlice<T> = std::sync::Arc<[T]>;
impl From<&std::sync::Arc<[u8]>> for PeerPubkey {
fn from(slice: &std::sync::Arc<[u8]>) -> Self {
Self(slice.clone())
}
}
}
#[cfg_attr(feature = "stabby", stabby::stabby)]
#[repr(C)]
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PeerPubkey(pub(crate) ArcSlice<u8>);
impl Default for PeerPubkey {
fn default() -> Self {
Self::from([])
}
}
impl PeerPubkey {
pub fn truncated(&self) -> impl Display + '_ {
TruncatedPeerPubkey { orig: self }
}
}
impl FromStr for PeerPubkey {
type Err = PeerPubkeyError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let prefix_len = PEER_PUBKEY_PREFIX.len();
if !s.starts_with(PEER_PUBKEY_PREFIX) {
return Err(PeerPubkeyError::ParseFailed);
}
let s = &s[prefix_len..];
let encoded_len = s.len();
let decoded_len = (encoded_len / 4 + (encoded_len % 4 > 0) as usize) * 3;
let mut buffer = vec![0; decoded_len];
let pk = PEER_PUBKEY_B64_ENGINE
.decode_slice(s, buffer.as_mut())
.map_err(|_| PeerPubkeyError::ParseFailed)?;
buffer.truncate(pk);
Ok(Self(buffer.into()))
}
}
impl Debug for PeerPubkey {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Display::fmt(&self, f)
}
}
impl Display for PeerPubkey {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}{}",
PEER_PUBKEY_PREFIX,
Base64Display::new(&self.0, &PEER_PUBKEY_B64_ENGINE)
)
}
}
impl Deref for PeerPubkey {
type Target = [u8];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<const N: usize> From<[u8; N]> for PeerPubkey {
fn from(vec: [u8; N]) -> Self {
Self(Vec::from(vec.as_slice()).into())
}
}
impl<const N: usize> From<&[u8; N]> for PeerPubkey {
fn from(vec: &[u8; N]) -> Self {
Self(Vec::from(vec.as_slice()).into())
}
}
impl From<&[u8]> for PeerPubkey {
fn from(vec: &[u8]) -> Self {
Self(Vec::from(vec).into())
}
}
impl From<&Self> for PeerPubkey {
fn from(this: &Self) -> Self {
this.clone()
}
}
#[cfg(feature = "experimental-pk-serde")]
mod serde_impl {
use serde::{Deserialize, Serialize};
use super::PeerPubkey;
impl Serialize for PeerPubkey {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_bytes(&self.0)
}
}
impl<'de> Deserialize<'de> for PeerPubkey {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let v = deserializer.deserialize_bytes(ByteArrayVisitor)?;
Ok(PeerPubkey::from(v.as_slice()))
}
}
struct ByteArrayVisitor;
impl<'de> serde::de::Visitor<'de> for ByteArrayVisitor {
type Value = Vec<u8>;
fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
formatter.write_str("byte array")
}
fn visit_bytes<E>(self, v: &[u8]) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v.into())
}
fn visit_byte_buf<E>(self, v: Vec<u8>) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
Ok(v)
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: serde::de::SeqAccess<'de>,
{
let mut values = Vec::<u8>::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(value) = seq.next_element()? {
values.push(value);
}
Ok(values)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn check_serde_json() {
let pk = PeerPubkey::from([1]);
let as_json = serde_json::to_value(&pk).unwrap();
assert_eq!(as_json, serde_json::json!([1]));
let byte_slice_to_json = serde_json::to_value(&*pk.0).unwrap();
assert_eq!(as_json, byte_slice_to_json);
let round_trip = serde_json::from_value::<PeerPubkey>(as_json).unwrap();
assert_eq!(pk, round_trip);
let round_trip_byte_slice =
serde_json::from_value::<PeerPubkey>(byte_slice_to_json).unwrap();
assert_eq!(pk, round_trip_byte_slice);
}
#[test]
fn check_serde_cbor() {
use std::ops::Deref;
let pk = PeerPubkey::from([1]);
let as_cbor = serde_cbor::to_vec(&pk).unwrap();
let byte_slice_to_cbor = serde_cbor::to_vec(&pk.0.deref()).unwrap();
assert_ne!(as_cbor, byte_slice_to_cbor);
let round_trip = serde_cbor::from_slice::<PeerPubkey>(&as_cbor).unwrap();
assert_eq!(pk, round_trip);
let round_trip_byte_slice =
serde_cbor::from_slice::<PeerPubkey>(&byte_slice_to_cbor).unwrap();
assert_eq!(pk, round_trip_byte_slice);
assert!(serde_cbor::from_slice::<Vec<u8>>(&as_cbor).is_err());
serde_cbor::from_slice::<Vec<u8>>(&byte_slice_to_cbor).unwrap();
}
}
}
struct TruncatedPeerPubkey<'a> {
orig: &'a PeerPubkey,
}
impl<'a> Display for TruncatedPeerPubkey<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let mut displayed = format!("{}", self.orig);
if displayed.len() > 9 {
displayed.replace_range(2..displayed.len() - 7, ".+")
}
f.write_str(&displayed)
}
}