1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
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); // 2^56 bits
        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 {
        // The AppId must be less than 56bits
        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),
                        }
                    }
                }
            }
        }
    }
}