gtin_validate/gtin12/
mod.rs1use utils;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7pub enum FixError {
8 NonAsciiString,
10 TooLong,
12 CheckDigitIncorrect,
14}
15
16pub fn check(code: &str) -> bool {
28 if code.len() != 12 {
29 return false;
30 }
31 if !utils::is_ascii_numeric(code) {
32 return false;
33 }
34
35 let bytes = code.as_bytes();
37 let check = utils::compute_check_digit(bytes);
38 if check != bytes[11] - 48 {
39 return false;
40 }
41
42 true
43}
44
45pub fn fix(code: &str) -> Result<String, FixError> {
76 let mut fixed = code.trim().to_string();
77
78 if !fixed.is_ascii() {
79 return Err(FixError::NonAsciiString);
80 }
81 if fixed.len() > 12 {
82 return Err(FixError::TooLong);
83 }
84 fixed = utils::zero_pad(fixed, 12);
85 if !check(&fixed) {
86 return Err(FixError::CheckDigitIncorrect);
87 }
88
89 Ok(fixed)
90}
91
92#[cfg(test)]
93mod tests {
94 use super::check;
95 use super::fix;
96 use super::FixError;
97
98 #[test]
99 fn check_valid() {
100 assert_eq!(check(&"000000000000"), true);
101 }
102
103 #[test]
104 fn check_invalid_length() {
105 assert_eq!(check("000"), false);
106 }
107
108 #[test]
109 fn check_non_ascii() {
110 assert_eq!(check("❤"), false);
111 }
112
113 #[test]
114 fn check_non_numeric() {
115 assert_eq!(check("a"), false);
116 assert_eq!(check("abcdabcdabcd"), false); assert_eq!(check("00000000000a"), false); }
119
120 #[test]
121 fn check_invalid_check_digit() {
122 assert_eq!(check("000000000001"), false);
123 assert_eq!(check("000000000002"), false);
124 assert_eq!(check("000000000003"), false);
125 assert_eq!(check("000000000004"), false);
126 assert_eq!(check("000000000005"), false);
127 assert_eq!(check("000000000006"), false);
128 assert_eq!(check("000000000007"), false);
129 assert_eq!(check("000000000008"), false);
130 assert_eq!(check("000000000009"), false);
131 }
132
133 #[test]
134 fn check_static_data() {
135 assert_eq!(check("000000000000"), true);
136 assert_eq!(check("123456789012"), true);
137 assert_eq!(check("123456789013"), false);
138 assert_eq!(check("999999999993"), true);
139 assert_eq!(check("999999999999"), false);
140 }
141
142 #[test]
143 fn fix_non_ascii() {
144 assert!(fix("❤").is_err());
145 }
146
147 #[test]
148 fn fix_too_long() {
149 assert_eq!(fix("0000000000000"), Err(FixError::TooLong));
150 }
151
152 #[test]
153 fn fix_incorrect_check_digit() {
154 assert_eq!(fix("123456789013"), Err(FixError::CheckDigitIncorrect));
155 }
156
157 #[test]
158 fn fix_needs_zero_padding() {
159 assert!(fix("0").is_ok());
160 assert_eq!(fix("0").unwrap(), "000000000000");
161 }
162
163 proptest! {
164 #[test]
165 fn doesnt_crash(ref s in ".*") {
166 check(s);
167 }
168 }
169}