edge-schema 0.1.0

Shared schema types for Wasmer Edge.
Documentation
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),
                        }
                    }
                }
            }
        }
    }
}