// GS1 retail numbers. The GTIN family — EAN-8, UPC-A, EAN-13 and GTIN-14 —
// shares one mod-10 check digit computed with the alternating 3-1 weighting
// read from the right, and UPC-E is the zero-suppressed form of UPC-A.
///|
/// The GTIN check digit for a payload of digits: the digit that, appended,
/// makes the alternating-weight sum a multiple of ten. Weights are 3 and 1
/// starting with 3 at the rightmost payload digit, which is the weight the
/// position to the left of the check digit always carries. Returns None unless
/// every character is a digit.
pub fn gtin_check_digit(payload : String) -> Int? {
let cs = chars_of(payload)
let n = cs.length()
if n == 0 {
return None
}
let mut sum = 0
for i = 0; i < n; i = i + 1 {
let c = cs[i]
if !is_digit(c) {
return None
}
let d = c.to_int() - '0'.to_int()
// Distance from the check digit. Odd distances carry weight 3.
let weight = if (n - i) % 2 == 1 { 3 } else { 1 }
sum = sum + d * weight
}
Some((10 - sum % 10) % 10)
}
///|
/// Validate a numeric code of exactly `len` digits whose last digit is the GTIN
/// check digit for the preceding ones.
fn gtin_code_valid(s : String, len : Int) -> Bool {
let cs = chars_of(s)
if cs.length() != len {
return false
}
for c in cs {
if !is_digit(c) {
return false
}
}
let body = match substring(s, 0, len - 1) {
None => return false
Some(b) => b
}
match gtin_check_digit(body) {
None => false
Some(d) => d == (cs[len - 1].to_int() - '0'.to_int())
}
}
///|
/// Validate an eight-digit EAN-8, the short retail code used on small packages.
pub fn ean8_valid(s : String) -> Bool {
gtin_code_valid(s, 8)
}
///|
/// Validate a twelve-digit UPC-A, the North American retail code.
pub fn upca_valid(s : String) -> Bool {
gtin_code_valid(s, 12)
}
///|
/// Validate a thirteen-digit EAN-13, the standard international retail code.
/// ISBN-13 and ISSN are both restrictions of this shape.
pub fn ean13_valid(s : String) -> Bool {
gtin_code_valid(s, 13)
}
///|
/// Validate a fourteen-digit GTIN-14, the shipping-container level of the GTIN
/// hierarchy.
pub fn gtin14_valid(s : String) -> Bool {
gtin_code_valid(s, 14)
}
///|
/// Validate any GTIN by its own length: eight, twelve, thirteen or fourteen
/// digits. Any other length is invalid, since those are the four registered
/// GTIN sizes.
pub fn gtin_valid(s : String) -> Bool {
let n = match digit_count(s) {
None => return false
Some(n) => n
}
if n != 8 && n != 12 && n != 13 && n != 14 {
return false
}
gtin_code_valid(s, n)
}
///|
/// The eleven-digit UPC-A payload — number system, manufacturer, item — that a
/// UPC-E stands for. Reads the first seven characters of `s`; the check digit is
/// not part of the result. Returns None unless they are a number system 0 or 1
/// followed by six digits.
fn upce_payload(s : String) -> String? {
let cs = chars_of(s)
if cs.length() < 7 {
return None
}
let ns = cs[0]
if ns != '0' && ns != '1' {
return None
}
let d : Array[Char] = []
for i = 1; i <= 6; i = i + 1 {
let c = cs[i]
if !is_digit(c) {
return None
}
d.push(c)
}
let buf = StringBuilder()
buf.write_char(ns)
let last = d[5]
// The sixth data digit says where the suppressed zeroes were.
if last == '0' || last == '1' || last == '2' {
buf.write_char(d[0])
buf.write_char(d[1])
buf.write_char(last)
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char(d[2])
buf.write_char(d[3])
buf.write_char(d[4])
} else if last == '3' {
buf.write_char(d[0])
buf.write_char(d[1])
buf.write_char(d[2])
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char(d[3])
buf.write_char(d[4])
} else if last == '4' {
buf.write_char(d[0])
buf.write_char(d[1])
buf.write_char(d[2])
buf.write_char(d[3])
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char(d[4])
} else {
buf.write_char(d[0])
buf.write_char(d[1])
buf.write_char(d[2])
buf.write_char(d[3])
buf.write_char(d[4])
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char('0')
buf.write_char(last)
}
Some(buf.to_string())
}
///|
/// The UPC-A check digit implied by the first seven characters of a UPC-E.
pub fn upce_check_digit(first7 : String) -> Int? {
match upce_payload(first7) {
None => None
Some(p) => gtin_check_digit(p)
}
}
///|
/// Expand an eight-character UPC-E to the twelve-digit UPC-A it stands for. The
/// check digit is recomputed from the expanded payload, so a UPC-E whose own
/// check digit is wrong still expands to a well-formed UPC-A. Returns None
/// unless the input is eight digits with a number system of 0 or 1.
pub fn upce_expand(s : String) -> String? {
let cs = chars_of(s)
if cs.length() != 8 || !is_digit(cs[7]) {
return None
}
match upce_payload(s) {
None => None
Some(p) =>
match gtin_check_digit(p) {
None => None
Some(cd) => Some(p + char_str(digit_char(cd)))
}
}
}
///|
/// Validate an eight-character UPC-E. Its check digit must equal the one the
/// expanded UPC-A would carry.
pub fn upce_valid(s : String) -> Bool {
let cs = chars_of(s)
if cs.length() != 8 {
return false
}
match upce_check_digit(s) {
None => false
Some(cd) => cd == (cs[7].to_int() - '0'.to_int())
}
}