upc_checker/lib.rs
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
#![no_std]
/// Error enum for the UPCs. This is responsable for the Error return on
/// the `Result<T, E>` E (error) on the `UPC.check_upc()` method and
/// commonly implaments errors when users use standards implamented by the
/// `UPCStandard` wrongly.
///
/// # Error Types
///
/// - UPCOverflow: When the i8 array implamented in the standards defined
/// by the `UPCStandard` enum has been overflown with data that is not 0-9
/// (1 digit)
/// - CheckDigitOverflow: When the i8 `check_digit` value implamented in the
/// `UPC` has been overflown with data that is not 0-9 (1 digit)
#[derive(Debug, PartialEq, Clone)]
pub enum UPCError {
UPCOverflow,
CheckDigitOverflow,
}
/// The impamentations on the widely-used UPC code standards are simple `i8`
/// arrays with a defined length.
///
/// **NOTE: These arrays should **only** have int's that are 0-9 (1 digit)
/// otherwise `UPC.upc_check()` will throw an error defined as
/// `UPCError::UPCOverflow`.**
///
/// # Standards Implamented
///
/// - [UPC-A](https://en.wikipedia.org/wiki/Universal_Product_Code#Encoding)
#[derive(Debug, PartialEq, Clone)]
pub enum UPCStandard {
UPCA([i8; 11]),
}
/// Main UPC structure containing the base UPC code alonside it's
/// check digit. This is the core of the `upc_checker` library
///
/// # Params
///
/// - upc: A `UPCStandard` enum
/// - check_digit: An i8 int for the UPC code's check digit
///
/// # Examples
///
/// **NOTE: The below example is a demo and will not work with the given upc
/// code & check digit in practise.**
///
/// ```rust
/// extern crate upc_checker;
///
/// let my_code_vector = upc_checker::UPCStandard::UPCA(
/// [0,1,2,3,4,5,6,7,8,9,0]
/// ); // NOTE digits should be 0-9.
/// let my_check_digit: i8 = 2; // NOTE check digit should be 0-9
///
/// let my_upc_code = upc_checker::UPC {
/// upc: my_code_vector,
/// check_digit: my_check_digit,
/// };
///
/// match my_upc_code.check_upc() {
/// Ok(x) => println!("Is the code valid?: {}", x),
/// Err(upc_checker::UPCError::UPCOverflow) => {
/// println!("UPC code overflow! Please use only 0-9!");
/// },
/// Err(upc_checker::UPCError::CheckDigitOverflow) => {
/// println!("UPC check digit overflow! Please use only 0-9!");
/// },
/// };
/// ```
#[derive(Debug, PartialEq, Clone)]
pub struct UPC {
pub upc: UPCStandard,
pub check_digit: i8,
}
impl UPC {
/// The main frontend method for the `UPC` structure. This method uses
/// data from the super `UPC` struct and returns a Result enum with
/// either a `bool` (IF the check digit is valid) or an instance of the
/// `UPCError` enum.
///
/// **NOTE: For more documentation & examples, please view the `UPC`
/// documentation directly.**
pub fn check_upc(&self) -> Result<bool, UPCError> {
match self.validate_upc_overflow() {
Err(x) => return Err(x),
Ok(_) => (),
};
let (even_nums, odd_nums) = self.split_upc_even_odd();
let total: u16 = ((odd_nums * 3) + even_nums) % 10;
if (total == 0 && self.check_digit == 0) || (10 - total == self.check_digit as u16) {
return Ok(true);
}
Ok(false)
}
/// Converts any defined standards given in `UPCStandard` to an i8
/// slice and returns it.
///
/// **TODO: Make this automatically implament new standard from the
/// afformentioned enum instead of matching all values.**
fn get_upc_slice(&self) -> &[i8] {
match &self.upc {
UPCStandard::UPCA(x) => &x[..],
}
}
/// Validates that there has been no overflow of the `UPC` structure
/// by hooking onto the `is_1_digit` helper function. This is the main
/// source of the uses of `UPCError`.
fn validate_upc_overflow(&self) -> Result<(), UPCError> {
for upc_code in self.get_upc_slice() {
if !is_1_digit(*upc_code) {
return Err(UPCError::UPCOverflow);
}
}
if is_1_digit(self.check_digit) {
Ok(())
} else {
Err(UPCError::CheckDigitOverflow)
}
}
/// Splits the UPC codes depending if they are odd or even (defined by a
/// mod) into one of 2 values in a tuple of `([EVEN] u16, [ODD] u16)`.
fn split_upc_even_odd(&self) -> (u16, u16) {
let mut even_odd: (u16, u16) = (0, 0);
for upc_code in self.get_upc_slice() {
if upc_code % 2 == 0 {
even_odd.0 += *upc_code as u16;
} else {
even_odd.1 += *upc_code as u16;
}
}
even_odd
}
}
/// Checks if a given i8 is 1 digit/character (0-9) wide
fn is_1_digit(digit: i8) -> bool {
!(digit < 0 || digit > 9)
}