use serde::{Deserialize, Serialize};
use std::{
fmt::Display,
net::{IpAddr, Ipv6Addr},
str::FromStr,
};
use crate::{NetworkId, NetworkIdEncodingMethod};
#[derive(
Debug,
Clone,
Copy,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
Serialize,
Deserialize,
schemars::JsonSchema,
)]
pub struct AppId(u64);
impl AppId {
pub fn new(val: u64) -> Self {
assert!(val < 72_057_594_037_927_936); Self(val)
}
pub fn as_u64(&self) -> u64 {
self.0
}
pub fn from_ip(addr: &IpAddr, method: NetworkIdEncodingMethod) -> Option<(Self, u32)> {
let (network_id, remainder) = NetworkId::from_ip(addr, method)?;
network_id.as_app_id().map(|app_id| (app_id, remainder))
}
pub fn from_ipv6(addr: &Ipv6Addr, method: NetworkIdEncodingMethod) -> Option<(Self, u32)> {
let (network_id, remainder) = NetworkId::from_ipv6(addr, method);
network_id.as_app_id().map(|app_id| (app_id, remainder))
}
pub fn into_ip(self, base: IpAddr, remainder: u32, method: NetworkIdEncodingMethod) -> IpAddr {
match base {
IpAddr::V4(base) => IpAddr::V4(base),
IpAddr::V6(base) => self.into_ipv6(base, remainder, method).into(),
}
}
pub fn into_ipv6(
self,
base: Ipv6Addr,
remainder: u32,
method: NetworkIdEncodingMethod,
) -> Ipv6Addr {
NetworkId::into_ipv6(self.into(), base, remainder, method)
}
}
impl Display for AppId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl TryFrom<&str> for AppId {
type Error = ();
fn try_from(value: &str) -> Result<Self, Self::Error> {
FromStr::from_str(value)
}
}
impl FromStr for AppId {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(AppId(u64::from_str(s).map_err(|_| ())?))
}
}
impl From<u64> for AppId {
fn from(value: u64) -> Self {
assert!(value < 0x00ff_ffff_ffff_ffff);
Self(value)
}
}
impl From<AppId> for u64 {
fn from(value: AppId) -> Self {
value.0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_app_id_cipher() {
let addrs = [
Ipv6Addr::LOCALHOST,
Ipv6Addr::UNSPECIFIED,
Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0),
Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1),
Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0),
Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
];
let methods = [
NetworkIdEncodingMethod::PrivateProjection,
NetworkIdEncodingMethod::PublicProjection,
];
let remainders = [0u32, 1u32, 128u32, 255u32, 16_777_215u32];
for method in methods {
for n in 0u64..1000u64 {
let n = n * 989;
let app_id = AppId::new(n);
for r in remainders {
for base in addrs.iter() {
let addr = app_id.into_ipv6(*base, r, method);
let s1 = addr.segments();
let s2 = base.segments();
match method {
NetworkIdEncodingMethod::PrivateProjection => {
assert_eq!(s1[0], s2[0]);
assert_eq!(s1[1], s2[1]);
assert_eq!(s1[2].to_be_bytes()[0], s2[2].to_be_bytes()[0]);
}
NetworkIdEncodingMethod::PublicProjection => {
assert_eq!(s1[0], s2[0]);
assert_eq!(s1[1], s2[1]);
assert_eq!(s1[2], s2[2]);
assert_eq!(s1[3], s2[3]);
}
}
let (ret_app_id, ret_r) = AppId::from_ipv6(&addr, method).unwrap();
assert_eq!(app_id, ret_app_id);
match method {
NetworkIdEncodingMethod::PrivateProjection => assert_eq!(r, ret_r),
NetworkIdEncodingMethod::PublicProjection => assert_eq!(0, ret_r),
}
}
}
}
}
}
}