1use serde::{Deserialize, Serialize};
2use std::{
3 fmt::Display,
4 net::{IpAddr, Ipv6Addr},
5 str::FromStr,
6};
7
8use crate::{NetworkId, NetworkIdEncodingMethod};
9
10#[derive(
11 Debug,
12 Clone,
13 Copy,
14 PartialEq,
15 Eq,
16 PartialOrd,
17 Ord,
18 Hash,
19 Serialize,
20 Deserialize,
21 schemars::JsonSchema,
22)]
23pub struct AppId(u64);
24
25impl AppId {
26 pub fn new(val: u64) -> Self {
27 assert!(val < 72_057_594_037_927_936); Self(val)
29 }
30
31 pub fn as_u64(&self) -> u64 {
32 self.0
33 }
34
35 pub fn from_ip(addr: &IpAddr, method: NetworkIdEncodingMethod) -> Option<(Self, u32)> {
36 let (network_id, remainder) = NetworkId::from_ip(addr, method)?;
37 network_id.as_app_id().map(|app_id| (app_id, remainder))
38 }
39
40 pub fn from_ipv6(addr: &Ipv6Addr, method: NetworkIdEncodingMethod) -> Option<(Self, u32)> {
41 let (network_id, remainder) = NetworkId::from_ipv6(addr, method);
42 network_id.as_app_id().map(|app_id| (app_id, remainder))
43 }
44
45 pub fn into_ip(self, base: IpAddr, remainder: u32, method: NetworkIdEncodingMethod) -> IpAddr {
46 match base {
47 IpAddr::V4(base) => IpAddr::V4(base),
48 IpAddr::V6(base) => self.into_ipv6(base, remainder, method).into(),
49 }
50 }
51
52 pub fn into_ipv6(
53 self,
54 base: Ipv6Addr,
55 remainder: u32,
56 method: NetworkIdEncodingMethod,
57 ) -> Ipv6Addr {
58 NetworkId::into_ipv6(self.into(), base, remainder, method)
59 }
60}
61
62impl Display for AppId {
63 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
64 write!(f, "{}", self.0)
65 }
66}
67
68impl TryFrom<&str> for AppId {
69 type Error = ();
70
71 fn try_from(value: &str) -> Result<Self, Self::Error> {
72 FromStr::from_str(value)
73 }
74}
75
76impl FromStr for AppId {
77 type Err = ();
78
79 fn from_str(s: &str) -> Result<Self, Self::Err> {
80 Ok(AppId(u64::from_str(s).map_err(|_| ())?))
81 }
82}
83
84impl From<u64> for AppId {
85 fn from(value: u64) -> Self {
86 assert!(value < 0x00ff_ffff_ffff_ffff);
88 Self(value)
89 }
90}
91
92impl From<AppId> for u64 {
93 fn from(value: AppId) -> Self {
94 value.0
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn test_app_id_cipher() {
104 let addrs = [
105 Ipv6Addr::LOCALHOST,
106 Ipv6Addr::UNSPECIFIED,
107 Ipv6Addr::new(0xff0e, 0, 0, 0, 0, 0, 0, 0),
108 Ipv6Addr::new(0, 0, 0x1c9, 0, 0, 0xafc8, 0, 0x1),
109 Ipv6Addr::new(0xff00, 0, 0, 0, 0, 0, 0, 0),
110 Ipv6Addr::new(0x2001, 0xdb8, 0, 0, 0, 0, 0, 0),
111 ];
112 let methods = [
113 NetworkIdEncodingMethod::PrivateProjection,
114 NetworkIdEncodingMethod::PublicProjection,
115 ];
116 let remainders = [0u32, 1u32, 128u32, 255u32, 16_777_215u32];
117
118 for method in methods {
119 for n in 0u64..1000u64 {
120 let n = n * 989;
121
122 let app_id = AppId::new(n);
123
124 for r in remainders {
125 for base in addrs.iter() {
126 let addr = app_id.into_ipv6(*base, r, method);
127
128 let s1 = addr.segments();
129 let s2 = base.segments();
130
131 match method {
132 NetworkIdEncodingMethod::PrivateProjection => {
133 assert_eq!(s1[0], s2[0]);
134 assert_eq!(s1[1], s2[1]);
135 assert_eq!(s1[2].to_be_bytes()[0], s2[2].to_be_bytes()[0]);
136 }
137 NetworkIdEncodingMethod::PublicProjection => {
138 assert_eq!(s1[0], s2[0]);
139 assert_eq!(s1[1], s2[1]);
140 assert_eq!(s1[2], s2[2]);
141 assert_eq!(s1[3], s2[3]);
142 }
143 }
144
145 let (ret_app_id, ret_r) = AppId::from_ipv6(&addr, method).unwrap();
146 assert_eq!(app_id, ret_app_id);
147
148 match method {
149 NetworkIdEncodingMethod::PrivateProjection => assert_eq!(r, ret_r),
150 NetworkIdEncodingMethod::PublicProjection => assert_eq!(0, ret_r),
151 }
152 }
153 }
154 }
155 }
156 }
157}