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)
}