Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
use super::funejson::Value; ← the fune runtime: the JSON value the test vectors use; fune build keeps it only where a signature takes one
/// The Luhn (mod 10) checksum, as used by payment card numbers, IMEIs,
/// SIM ICCIDs and a long tail of national identifiers.
///
/// Luhn is a *transcription* check. It catches a single mistyped digit and
/// most adjacent transpositions before a request leaves the building. It says
/// nothing about whether the card exists, is open, belongs to the person
/// typing it, or has any money behind it. Only the acquirer can answer that,
/// so never render "card valid" on the strength of this function.
pub fn is_luhn(value: &str) -> bool {
let mut digits: Vec<u32> = Vec::new();
for ch in value.chars() {
// Card numbers are printed and pasted in four-digit groups, so
// tolerating the separators here saves every caller the same strip.
if ch == ' ' || ch == '-' {
continue;
}
if ch < '0' || ch > '9' {
return false;
}
digits.push(ch as u32 - 48);
}
// A lone digit satisfies the arithmetic whenever it is 0, which would make
// this function a rubber stamp for a stray keystroke. No identifier scheme
// issues one-digit numbers, so two is the honest floor.
if digits.len() < 2 {
return false;
}
let mut total: u32 = 0;
for (position, digit) in digits.iter().rev().enumerate() {
let mut d = *digit;
if position % 2 == 1 {
d *= 2;
// Doubling can only reach 18, so subtracting 9 is the same as
// summing the two decimal digits, without the string round trip.
if d > 9 {
d -= 9;
}
}
total += d;
}
total % 10 == 0
}
/// The digit that would make `prefix` pass the Luhn check.
///
/// Useful for generating test data and for completing a partially known
/// number; it is the inverse of the check above, not a second opinion on it.
/// Returns -1 when the prefix is not usable digits at all.
pub fn luhn_check_digit(prefix: &str) -> i64 {
let mut digits: Vec<u32> = Vec::new();
for ch in prefix.chars() {
if ch == ' ' || ch == '-' {
continue;
}
if ch < '0' || ch > '9' {
return -1;
}
digits.push(ch as u32 - 48);
}
if digits.is_empty() {
return -1;
}
let mut total: u32 = 0;
// The check digit will occupy position 0, so the prefix starts at 1.
for (position, digit) in digits.iter().rev().enumerate() {
let mut d = *digit;
if position % 2 == 0 {
d *= 2;
if d > 9 {
d -= 9;
}
}
total += d;
}
((10 - (total % 10)) % 10) as i64
}
pub fn fune_vector(args: &[Value]) -> Value {
// A non-string argument arrives here as an empty string, which is exactly
// the answer TypeScript and Python give for a non-string: not valid.
Value::Bool(is_luhn(args[0].as_str()))
}