use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum PeerIdentity {
Network(SocketAddr),
Local(LocalPeer),
Unknown,
}
#[derive(Debug, Clone)]
pub struct LocalPeer {
pub pid: Option<u32>,
pub uid: Option<u32>,
pub attestation: Attestation,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum Attestation {
None,
Verified {
mechanism: &'static str,
subject: String,
},
}
impl Attestation {
pub fn is_verified(&self) -> bool {
matches!(self, Self::Verified { .. })
}
}
impl PeerIdentity {
pub fn ip_addr(&self) -> IpAddr {
match self {
Self::Network(addr) => addr.ip(),
Self::Local(_) | Self::Unknown => IpAddr::V4(Ipv4Addr::LOCALHOST),
}
}
pub fn socket_addr(&self) -> SocketAddr {
match self {
Self::Network(addr) => *addr,
Self::Local(_) | Self::Unknown => SocketAddr::from(([127, 0, 0, 1], 0)),
}
}
pub fn attestation(&self) -> Option<&Attestation> {
match self {
Self::Local(peer) => Some(&peer.attestation),
_ => None,
}
}
pub fn display(&self) -> String {
match self {
Self::Network(addr) => addr.to_string(),
Self::Local(peer) => {
let pid = peer
.pid
.map_or_else(|| "?".to_owned(), |pid| pid.to_string());
match &peer.attestation {
Attestation::Verified { mechanism, .. } => {
format!("local(pid={pid},{mechanism})")
}
Attestation::None => format!("local(pid={pid},unattested)"),
}
}
Self::Unknown => "unknown".to_owned(),
}
}
}
impl std::fmt::Display for PeerIdentity {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.display())
}
}
impl From<SocketAddr> for PeerIdentity {
fn from(addr: SocketAddr) -> Self {
Self::Network(addr)
}
}
trait CloneAny: Any + Send + Sync {
fn clone_box(&self) -> Box<dyn CloneAny>;
fn as_any(&self) -> &dyn Any;
fn as_any_mut(&mut self) -> &mut dyn Any;
fn into_any(self: Box<Self>) -> Box<dyn Any>;
}
impl<T: Any + Send + Sync + Clone> CloneAny for T {
fn clone_box(&self) -> Box<dyn CloneAny> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
fn into_any(self: Box<Self>) -> Box<dyn Any> {
self
}
}
impl Clone for Box<dyn CloneAny> {
fn clone(&self) -> Self {
(**self).clone_box()
}
}
#[derive(Default, Clone)]
pub struct Extensions {
map: HashMap<TypeId, Box<dyn CloneAny>>,
}
impl Extensions {
pub fn new() -> Self {
Self::default()
}
pub fn insert<T: Clone + Send + Sync + 'static>(&mut self, value: T) -> Option<T> {
self.map
.insert(TypeId::of::<T>(), Box::new(value))
.and_then(|prev| prev.into_any().downcast().ok().map(|boxed| *boxed))
}
pub fn get<T: Clone + Send + Sync + 'static>(&self) -> Option<&T> {
self.map
.get(&TypeId::of::<T>())
.and_then(|boxed| (**boxed).as_any().downcast_ref())
}
pub fn get_mut<T: Clone + Send + Sync + 'static>(&mut self) -> Option<&mut T> {
self.map
.get_mut(&TypeId::of::<T>())
.and_then(|boxed| (**boxed).as_any_mut().downcast_mut())
}
pub fn remove<T: Clone + Send + Sync + 'static>(&mut self) -> Option<T> {
self.map
.remove(&TypeId::of::<T>())
.and_then(|boxed| boxed.into_any().downcast().ok().map(|b| *b))
}
pub fn contains<T: Clone + Send + Sync + 'static>(&self) -> bool {
self.map.contains_key(&TypeId::of::<T>())
}
pub fn is_empty(&self) -> bool {
self.map.is_empty()
}
pub fn len(&self) -> usize {
self.map.len()
}
}
impl std::fmt::Debug for Extensions {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Extensions")
.field("len", &self.map.len())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn network_peer_reports_its_own_ip() {
let addr: SocketAddr = "203.0.113.7:9000".parse().unwrap();
let peer = PeerIdentity::Network(addr);
assert_eq!(peer.ip_addr(), addr.ip());
assert_eq!(peer.socket_addr(), addr);
assert_eq!(peer.display(), "203.0.113.7:9000");
assert!(peer.attestation().is_none());
}
#[test]
fn local_peer_reports_loopback_and_keeps_attestation() {
let peer = PeerIdentity::Local(LocalPeer {
pid: Some(42),
uid: Some(501),
attestation: Attestation::Verified {
mechanism: "xpc-codesign-requirement",
subject: "identifier agentone".to_owned(),
},
});
assert_eq!(peer.ip_addr(), IpAddr::V4(Ipv4Addr::LOCALHOST));
assert!(peer.attestation().unwrap().is_verified());
assert_eq!(peer.display(), "local(pid=42,xpc-codesign-requirement)");
}
#[test]
fn unattested_local_peer_is_visible_as_such() {
let peer = PeerIdentity::Local(LocalPeer {
pid: None,
uid: None,
attestation: Attestation::None,
});
assert!(!peer.attestation().unwrap().is_verified());
assert_eq!(peer.display(), "local(pid=?,unattested)");
}
#[test]
fn extensions_survive_a_clone() {
#[derive(Debug, Clone, PartialEq)]
struct Claims(&'static str);
let mut ext = Extensions::new();
ext.insert(Claims("mission-1"));
let copy = ext.clone();
assert_eq!(copy.get::<Claims>(), Some(&Claims("mission-1")));
}
#[test]
fn extensions_round_trip_by_type() {
#[derive(Debug, Clone, PartialEq)]
struct Claims(&'static str);
let mut ext = Extensions::new();
assert!(ext.is_empty());
assert!(ext.get::<Claims>().is_none());
assert!(ext.insert(Claims("mission-1")).is_none());
assert_eq!(ext.get::<Claims>(), Some(&Claims("mission-1")));
assert!(ext.contains::<Claims>());
let prev = ext.insert(Claims("mission-2"));
assert_eq!(prev, Some(Claims("mission-1")));
assert_eq!(ext.get::<Claims>(), Some(&Claims("mission-2")));
assert_eq!(ext.remove::<Claims>(), Some(Claims("mission-2")));
assert!(ext.is_empty());
}
}