use std::convert::TryInto;
use std::fmt::{self, Display};
use std::io::{Read, Write};
use std::net::Ipv4Addr;
use std::str::FromStr;
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use itertools::Itertools;
use zerocopy::{AsBytes, FromBytes, FromZeroes};
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Debug,
AsBytes, FromZeroes, FromBytes)]
#[repr(C)]
pub struct AmsNetId(pub [u8; 6]);
pub type AmsPort = u16;
impl AmsNetId {
pub const fn new(a: u8, b: u8, c: u8, d: u8, e: u8, f: u8) -> Self {
AmsNetId([a, b, c, d, e, f])
}
pub const fn local() -> Self {
AmsNetId([127, 0, 0, 1, 1, 1])
}
pub fn from_slice(slice: &[u8]) -> Option<Self> {
Some(AmsNetId(slice.try_into().ok()?))
}
pub fn from_ip(ip: Ipv4Addr, e: u8, f: u8) -> Self {
let [a, b, c, d] = ip.octets();
Self::new(a, b, c, d, e, f)
}
pub fn is_zero(&self) -> bool {
self.0 == [0, 0, 0, 0, 0, 0]
}
}
impl FromStr for AmsNetId {
type Err = &'static str;
fn from_str(s: &str) -> Result<AmsNetId, &'static str> {
let mut arr = [1; 6];
for (i, part) in s.split('.').enumerate() {
match (arr.get_mut(i), part.parse()) {
(Some(loc), Ok(byte)) => *loc = byte,
_ => return Err("invalid NetID string"),
}
}
Ok(AmsNetId(arr))
}
}
impl From<[u8; 6]> for AmsNetId {
fn from(array: [u8; 6]) -> Self {
Self(array)
}
}
impl Display for AmsNetId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0.iter().format("."))
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub struct AmsAddr(AmsNetId, AmsPort);
impl AmsAddr {
pub const fn new(netid: AmsNetId, port: AmsPort) -> Self {
Self(netid, port)
}
pub const fn netid(&self) -> AmsNetId {
self.0
}
pub const fn port(&self) -> AmsPort {
self.1
}
pub fn write_to<W: Write>(&self, w: &mut W) -> std::io::Result<()> {
w.write_all(&(self.0).0)?;
w.write_u16::<LE>(self.1)
}
pub fn read_from<R: Read>(r: &mut R) -> std::io::Result<Self> {
let mut netid = [0; 6];
r.read_exact(&mut netid)?;
let port = r.read_u16::<LE>()?;
Ok(Self(AmsNetId(netid), port))
}
}
impl FromStr for AmsAddr {
type Err = &'static str;
fn from_str(s: &str) -> Result<AmsAddr, &'static str> {
let (addr, port) = s.split(':').collect_tuple()
.ok_or("invalid AMS addr string")?;
Ok(Self(addr.parse()?, port.parse().map_err(|_| "invalid port number")?))
}
}
impl Display for AmsAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}:{}", self.0, self.1)
}
}