#![doc = include_str!("../README.md")]
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
use core::fmt::Display;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Error {
InvalidImei,
}
impl Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Error::InvalidImei => write!(f, "IMEI is invalid"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct Imei<I> {
inner: I,
}
impl<I: AsRef<str>> Imei<I> {
pub fn try_new(imei_str: I) -> Result<Self, Error> {
Imei::validate(imei_str.as_ref()).map(|_| Self { inner: imei_str })
}
pub fn into_inner(self) -> I {
self.inner
}
pub fn validate(imei: I) -> Result<(), Error> {
if valid(imei) {
Ok(())
} else {
Err(Error::InvalidImei)
}
}
}
impl<I: AsRef<str>> Display for Imei<I> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.inner.as_ref())
}
}
pub fn valid<A: AsRef<str>>(imei: A) -> bool {
let s = imei.as_ref();
if s.len() != 15 {
return false;
}
let mut sum: u8 = 0;
for (i, c) in s.chars().enumerate() {
let mut n: u8 = match c {
'0' => 0,
'1' => 1,
'2' => 2,
'3' => 3,
'4' => 4,
'5' => 5,
'6' => 6,
'7' => 7,
'8' => 8,
'9' => 9,
_ => return false,
};
if (i + 1) % 2 == 0 {
n *= 2;
if n > 9 {
n = match n {
10 => 1,
11 => 2,
12 => 3,
13 => 4,
14 => 5,
15 => 6,
16 => 7,
17 => 8,
18 => 9,
_ => 0,
}
}
}
sum += n;
}
sum % 10 == 0
}
#[cfg(feature = "serde")]
impl<I> serde::Serialize for Imei<I>
where
I: AsRef<str>,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.inner.as_ref())
}
}
#[cfg(feature = "serde")]
impl<'de, I> serde::Deserialize<'de> for Imei<I>
where
for<'s> I: From<&'s str>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
use std::marker::PhantomData;
struct ImeiVisitor<I> {
_marker: PhantomData<I>,
}
impl<'d, I> serde::de::Visitor<'d> for ImeiVisitor<I>
where
for<'s> I: From<&'s str>,
{
type Value = Imei<I>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a string representing a valid IMEI")
}
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
where
E: serde::de::Error,
{
if valid(v) {
Ok(Imei { inner: I::from(v) })
} else {
Err(E::custom(Error::InvalidImei))
}
}
}
let visitor: ImeiVisitor<I> = ImeiVisitor {
_marker: PhantomData,
};
deserializer.deserialize_any(visitor)
}
}