use super::digits::decimal;
use super::{Error, Fault};
use core::fmt;
use core::str::FromStr;
pub(crate) const FILLER: u8 = 0x0f;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Plmn {
digits: [u8; 6],
mnc_length: u8,
}
impl Plmn {
pub fn new(mcc: &str, mnc: &str) -> Result<Self, Error> {
decimal("mcc", mcc, 3, 3)?;
decimal("mnc", mnc, 2, 3)?;
let mut all = [b'0'; 6];
all[..3].copy_from_slice(mcc.as_bytes());
all[3..3 + mnc.len()].copy_from_slice(mnc.as_bytes());
Ok(Self {
digits: all,
mnc_length: mnc.len() as u8,
})
}
pub fn from_digits(mcc: [u8; 3], mnc: &[u8]) -> Result<Self, Error> {
if !(2..=3).contains(&mnc.len()) {
return Err(Error::length("mnc", "digits", mnc.len(), 2, 3));
}
let mut all = [b'0'; 6];
let members = ["mcc", "mcc", "mcc", "mnc", "mnc", "mnc"];
for (index, digit) in mcc.iter().chain(mnc).enumerate() {
if *digit > 9 {
let fault = Fault::Digit { given: *digit };
return Err(Error::new(members[index], fault));
}
all[index] = b'0' + digit;
}
Ok(Self {
digits: all,
mnc_length: mnc.len() as u8,
})
}
pub fn mcc(&self) -> &str {
core::str::from_utf8(&self.digits[..3]).unwrap_or_default()
}
pub fn mnc(&self) -> &str {
let digits = &self.digits[3..3 + usize::from(self.mnc_length)];
core::str::from_utf8(digits).unwrap_or_default()
}
pub fn digits(&self) -> &str {
let digits = &self.digits[..3 + usize::from(self.mnc_length)];
core::str::from_utf8(digits).unwrap_or_default()
}
fn digit(&self, index: usize) -> u8 {
self.digits[index] - b'0'
}
pub fn to_nas_octets(self) -> [u8; 3] {
let mnc_3 = match self.mnc_length {
3 => self.digit(5),
_ => FILLER,
};
[
self.digit(1) << 4 | self.digit(0),
mnc_3 << 4 | self.digit(2),
self.digit(4) << 4 | self.digit(3),
]
}
pub fn from_nas_octets(octets: [u8; 3]) -> Result<Self, Error> {
let [first, second, third] = octets;
let mcc = [first & 0x0f, first >> 4, second & 0x0f];
let mnc = [third & 0x0f, third >> 4, second >> 4];
match mnc[2] {
FILLER => Self::from_digits(mcc, &mnc[..2]),
_ => Self::from_digits(mcc, &mnc),
}
}
pub fn to_ap_octets(self) -> [u8; 3] {
let [d4, d5, d6] = match self.mnc_length {
3 => [self.digit(3), self.digit(4), self.digit(5)],
_ => [FILLER, self.digit(3), self.digit(4)],
};
[
self.digit(1) << 4 | self.digit(0),
d4 << 4 | self.digit(2),
d6 << 4 | d5,
]
}
pub fn from_ap_octets(octets: [u8; 3]) -> Result<Self, Error> {
let [first, second, third] = octets;
let mcc = [first & 0x0f, first >> 4, second & 0x0f];
let mnc = [second >> 4, third & 0x0f, third >> 4];
match mnc[0] {
FILLER => Self::from_digits(mcc, &mnc[1..]),
_ => Self::from_digits(mcc, &mnc),
}
}
}
impl fmt::Display for Plmn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}-{}", self.mcc(), self.mnc())
}
}
impl fmt::Debug for Plmn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Plmn({self})")
}
}
impl FromStr for Plmn {
type Err = Error;
fn from_str(text: &str) -> Result<Self, Error> {
let expected = "an MCC of three digits, a hyphen and an MNC of two or three, as 208-93";
let (mcc, mnc) = text
.split_once('-')
.ok_or(Error::new("plmn", Fault::Form { expected }))?;
Self::new(mcc, mnc)
}
}