1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
/*!
Provides tools for building different check-digit algorithms.
This package contains implementations of various check digit specifications,
including [ISO/IEC 7064:2003](https://www.iso.org/standard/31531.html)
Information technology — Security techniques — Check character systems.
# Example
```rust,ignore
use codes_check_digits::{luhn, Calculator};
let calculator = luhn::get_algorithm_instance();
assert!(calculator.is_valid("US0378331005"));
assert!(calculator.validate("US0378331005").is_ok());
assert_eq!(calculator.calculate("US037833100"), Ok(5));
```
# Features
* `gs1` - Adds the `gs1` module containing algorithms for various codes such as
EAN, GTIN, GLN, and UPC.
* `iso_7064` - Adds the `iso_7064` module containing implementations of the
variants defined in ISO/IEC 7064:2003.
* `luhn` - Adds the `luhn` module containing an implementation of the Luhn Algorithm.
* `sedol` - Adds the `sedol` module containing an implementation of the algorithm
used in SEDOL numbers.
*/
#![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 error::CheckDigitError;
use std::fmt::Display;
use tracing::trace;
use crate::error::invalid_check_digit;
// ------------------------------------------------------------------------------------------------
// Public Types
// ------------------------------------------------------------------------------------------------
///
/// ait for types that implement check digit algorithms.
///
pub trait Calculator<T>
where
T: Display + PartialEq,
{
///
/// Return the number of characters used as the check digit.
/// Currently it is assumed that these are the *n* right-most
/// characters in the input string.
///
fn number_of_check_digit_chars(&self) -> usize {
1
}
///
/// Return the name of this algorithm.
///
fn name(&self) -> &'static str;
///
/// Calculate a check digit for the provided string.
///
fn calculate(&self, s: &str) -> Result<T, CheckDigitError>;
///
/// Create a new string with the original data plus check digit.
///
fn create(&self, s: &str) -> Result<String, CheckDigitError> {
Ok(format!("{}{}", s, self.calculate(s)?))
}
///
/// Validate that the string is valid and that it contains a valid
/// check digit.
///
fn validate<S>(&self, s: S) -> Result<(), CheckDigitError>
where
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))
}
}
///
/// Returns `true` if the provided string includes a valid check digit,
/// else `false`. The default implementation relies on the `validate`
/// method.
///
fn is_valid<S>(&self, s: S) -> bool
where
S: AsRef<str>,
{
self.validate(s).is_ok()
}
}
// ------------------------------------------------------------------------------------------------
// Public Functions
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Implementations
// ------------------------------------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Modules
// ------------------------------------------------------------------------------------------------
#[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;