use crate::error::{MeshCodeError, Result};
use crate::types::mesh_level::MeshLevel;
use alloc::format;
use alloc::string::{String, ToString};
use core::fmt;
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[cfg_attr(feature = "serde", serde(into = "String", try_from = "String"))]
pub struct MeshCode {
value: u64,
}
impl MeshCode {
pub fn new(level: MeshLevel, code: u64) -> Result<Self> {
Self::validate(level, code)?;
let level_bits = (level.as_u8() as u64) << 56;
let value = level_bits | (code & 0x00FF_FFFF_FFFF_FFFF);
Ok(MeshCode { value })
}
fn validate(level: MeshLevel, code: u64) -> Result<()> {
let len = level.code_length() as u32;
if code >= 10u64.pow(len) {
return Err(MeshCodeError::OutOfRange);
}
let digit = |i: u32| (code / 10u64.pow(len - 1 - i)) % 10;
if len >= 6 && (digit(4) > 7 || digit(5) > 7) {
return Err(MeshCodeError::OutOfRange);
}
let subdivision_digits: &[u32] = match level {
MeshLevel::FourthHalf => &[8],
MeshLevel::FourthQuarter => &[8, 9],
MeshLevel::FourthEighth => &[8, 9, 10],
_ => &[],
};
for &i in subdivision_digits {
if !(1..=4).contains(&digit(i)) {
return Err(MeshCodeError::OutOfRange);
}
}
Ok(())
}
#[allow(clippy::should_implement_trait)]
pub fn from_str(s: &str) -> Result<Self> {
if s.is_empty() {
return Err(MeshCodeError::InvalidFormat("Empty string".to_string()));
}
for (i, c) in s.chars().enumerate() {
if !c.is_ascii_digit() {
return Err(MeshCodeError::InvalidDigit {
position: i,
digit: c,
});
}
}
let level = MeshLevel::from_code_string(s)?;
let code = s.parse::<u64>().map_err(|_| {
MeshCodeError::InvalidFormat("Failed to parse numeric code".to_string())
})?;
Self::new(level, code)
}
pub fn level(&self) -> MeshLevel {
let level_byte = (self.value >> 56) as u8;
MeshLevel::from_u8(level_byte).expect("Invalid level stored in MeshCode")
}
pub fn code(&self) -> u64 {
self.value & 0x00FF_FFFF_FFFF_FFFF
}
pub fn as_string(&self) -> String {
let level = self.level();
let code = self.code();
let width = level.code_length();
format!("{code:0width$}")
}
}
impl core::str::FromStr for MeshCode {
type Err = MeshCodeError;
fn from_str(s: &str) -> Result<Self> {
MeshCode::from_str(s)
}
}
impl From<MeshCode> for String {
fn from(mesh: MeshCode) -> Self {
mesh.as_string()
}
}
impl TryFrom<String> for MeshCode {
type Error = MeshCodeError;
fn try_from(s: String) -> Result<Self> {
MeshCode::from_str(&s)
}
}
impl fmt::Debug for MeshCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MeshCode")
.field("level", &self.level())
.field("code", &self.as_string())
.finish()
}
}
impl fmt::Display for MeshCode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mesh_code_creation() {
let mesh = MeshCode::new(MeshLevel::First, 5339).unwrap();
assert_eq!(mesh.level(), MeshLevel::First);
assert_eq!(mesh.code(), 5339);
assert_eq!(mesh.as_string(), "5339");
}
#[test]
fn test_mesh_code_from_str() {
let mesh = MeshCode::from_str("5339").unwrap();
assert_eq!(mesh.level(), MeshLevel::First);
assert_eq!(mesh.code(), 5339);
let mesh = MeshCode::from_str("53393599").unwrap();
assert_eq!(mesh.level(), MeshLevel::Third);
assert_eq!(mesh.code(), 53393599);
}
#[test]
fn test_invalid_mesh_code() {
assert!(MeshCode::from_str("").is_err());
assert!(MeshCode::from_str("abc").is_err());
assert!(MeshCode::from_str("12345").is_err());
}
#[test]
fn test_mesh_code_display() {
let mesh = MeshCode::from_str("0001").unwrap();
assert_eq!(mesh.as_string(), "0001");
assert_eq!(format!("{mesh}"), "0001");
}
}