gtin_validate/gtin13/
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() != 13 {
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[12] - 48 {
39 return false;
40 }
41
42 true
43}
44
45pub fn fix(code: &str) -> Result<String, FixError> {
79 let mut fixed = code.trim().to_string();
80
81 if !fixed.is_ascii() {
82 return Err(FixError::NonAsciiString);
83 }
84 if fixed.len() > 13 {
85 return Err(FixError::TooLong);
86 }
87 fixed = utils::zero_pad(fixed, 13);
88 if !check(&fixed) {
89 return Err(FixError::CheckDigitIncorrect);
90 }
91
92 Ok(fixed)
93}
94
95#[cfg(test)]
96mod tests {
97 use super::check;
98 use super::fix;
99 use super::FixError;
100
101 #[test]
102 fn check_valid() {
103 assert_eq!(check("0000000000000"), true);
104 assert_eq!(check("8845791354268"), true);
105 assert_eq!(check("0334873614126"), true);
106 }
107
108 #[test]
109 fn check_invalid_length() {
110 assert_eq!(check("000"), false);
111 assert_eq!(check("00000000000000"), false);
112 }
113
114 #[test]
115 fn check_non_ascii() {
116 assert_eq!(check("❤"), false);
117 }
118
119 #[test]
120 fn check_non_numeric() {
121 assert_eq!(check("a"), false);
122 assert_eq!(check("abcdabcdabcda"), false); assert_eq!(check("000000000000a"), false); }
125
126 #[test]
127 fn check_invalid_check_digit() {
128 assert_eq!(check("0000000000001"), false);
129 assert_eq!(check("0000000000002"), false);
130 assert_eq!(check("0000000000003"), false);
131 assert_eq!(check("0000000000004"), false);
132 assert_eq!(check("0000000000005"), false);
133 assert_eq!(check("0000000000006"), false);
134 assert_eq!(check("0000000000007"), false);
135 assert_eq!(check("0000000000008"), false);
136 assert_eq!(check("0000000000009"), false);
137 }
138
139 #[test]
140 fn check_static_data() {
141 assert_eq!(check("0000000000000"), true);
142 assert_eq!(check("0123456789012"), true);
143 assert_eq!(check("0123456789013"), false);
144 assert_eq!(check("0999999999993"), true);
145 assert_eq!(check("0999999999999"), false);
146 assert_eq!(check("4459121265748"), true);
147 assert_eq!(check("4459121265747"), false);
148 }
149
150 #[test]
151 fn fix_non_ascii() {
152 assert!(fix("❤").is_err());
153 }
154
155
156 #[test]
157 fn fix_too_long() {
158 assert_eq!(fix("00000000000000"), Err(FixError::TooLong));
159 }
160
161 #[test]
162 fn fix_incorrect_check_digit() {
163 assert_eq!(fix("8845791354262"), Err(FixError::CheckDigitIncorrect));
164 }
165
166 #[test]
167 fn fix_needs_zero_padding() {
168 assert!(fix("0").is_ok());
169 assert_eq!(fix("0").unwrap(), "0000000000000");
170 assert_eq!(fix("123012301238").unwrap(), "0123012301238");
171 }
172
173 proptest! {
174 #[test]
175 fn doesnt_crash(ref s in ".*") {
176 check(s);
177 }
178 }
179}