#![warn(
unknown_lints,
// ---------- Stylistic
absolute_paths_not_starting_with_crate,
elided_lifetimes_in_paths,
explicit_outlives_requirements,
macro_use_extern_crate,
nonstandard_style, /* group */
noop_method_call,
rust_2018_idioms,
single_use_lifetimes,
trivial_casts,
trivial_numeric_casts,
// ---------- Future
future_incompatible, /* group */
rust_2021_compatibility, /* group */
// ---------- Public
missing_debug_implementations,
// missing_docs,
unreachable_pub,
// ---------- Unsafe
unsafe_code,
unsafe_op_in_unsafe_fn,
// ---------- Unused
unused, /* group */
)]
#![deny(
// ---------- Public
exported_private_dependencies,
private_in_public,
// ---------- Deprecated
anonymous_parameters,
bare_trait_objects,
ellipsis_inclusive_range_patterns,
// ---------- Unsafe
deref_nullptr,
drop_bounds,
dyn_drop,
)]
use codes_common::Code;
use error::CheckDigitError;
use std::{borrow::Cow, fmt::Display};
use tracing::trace;
use crate::error::invalid_check_digit;
pub trait CodeWithCheckDigits: Code<String> + AsRef<str> {
type CheckDigit: Display + PartialEq;
type CheckDigitCalculator: Calculator<Self::CheckDigit> + Copy;
const CHECK_DIGIT_ALGORITHM: Self::CheckDigitCalculator;
fn data_no_check_digit(&self) -> Cow<'_, str> {
let s = self.as_ref();
s[..(s.len() - Self::CHECK_DIGIT_ALGORITHM.number_of_check_digit_chars())].into()
}
fn check_digit_as_str(&self) -> Cow<'_, str> {
let s = self.as_ref();
s[(s.len() - Self::CHECK_DIGIT_ALGORITHM.number_of_check_digit_chars())..].into()
}
}
pub trait Calculator<T>
where
T: Display + PartialEq,
{
fn number_of_check_digit_chars(&self) -> usize {
1
}
fn name(&self) -> &'static str;
fn calculate(&self, s: &str) -> Result<T, CheckDigitError>;
fn create(&self, s: &str) -> Result<String, CheckDigitError> {
Ok(format!(
"{}{:0>width$}",
s,
self.calculate(s)?,
width = self.number_of_check_digit_chars()
))
}
fn validate<S>(&self, s: S) -> Result<(), CheckDigitError>
where
Self: Sized,
S: AsRef<str>,
{
let s = s.as_ref();
trace!(
algorithm_name = self.name(),
num_check_digits = self.number_of_check_digit_chars(),
"Validating check digits for input {:?}",
s
);
let check_digit_index = s.len() - self.number_of_check_digit_chars();
let check = self.calculate(&s[0..check_digit_index])?;
if s[check_digit_index..] == check.to_string() {
Ok(())
} else {
Err(invalid_check_digit(&s[check_digit_index..], check))
}
}
fn is_valid<S>(&self, s: S) -> bool
where
Self: Sized,
S: AsRef<str>,
{
self.validate(s).is_ok()
}
}
#[macro_export]
macro_rules! check_digits_impl {
($type_name:ty, $error_type:ty, $algorithm_type:ty, $check_digit_type:ty, $algorithm_init:expr) => {
impl CodeWithCheckDigits for $type_name {
type CheckDigit = $check_digit_type;
type CheckDigitCalculator = $algorithm_type;
const CHECK_DIGIT_ALGORITHM: Self::CheckDigitCalculator = $algorithm_init;
}
impl ::std::str::FromStr for $type_name {
type Err = GlobalLocationNumberError;
fn from_str(s: &str) -> ::std::result::Result<Self, Self::Err> {
use codes_check_digits::Calculator;
Self::CHECK_DIGIT_ALGORITHM.validate(s)?;
Ok(Self(s.to_string()))
}
}
};
}
#[doc(hidden)]
mod common;
pub mod error;
#[cfg(feature = "gs1")]
pub mod gs1;
#[cfg(feature = "iso_7064")]
pub mod iso_7064;
#[cfg(feature = "luhn")]
pub mod luhn;
#[cfg(feature = "sedol")]
pub mod sedol;