Functional Weave
Code in Python

retail.sku-generate@1.0.0

impl/rust.rs

2,082 bytes · the Rust implementation · view raw

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

// Keep ASCII letters and digits only, then uppercase. Filtering first matters:
// "ß".to_uppercase() is "SS", which would make the SKU depend on how each
// language's Unicode tables treat one letter.
fn clean(part: &str) -> String {
    part.chars()
        .filter(|c| c.is_ascii_alphanumeric())
        .map(|c| c.to_ascii_uppercase())
        .collect()
}

/// A deterministic SKU: the cleaned prefix and attributes, joined by hyphens.
/// Each attribute is cut to `segment_length` characters (0 keeps it whole).
///
/// # Panics
/// Panics on a negative segment length, no attributes, or a part with no
/// letters or digits.
pub fn generate_sku(prefix: &str, attributes: &[String], segment_length: i64) -> String {
    if segment_length < 0 {
        panic!("segmentLength must be 0 or more, received {}", segment_length);
    }
    if attributes.is_empty() {
        panic!("a SKU needs at least one attribute");
    }
    let mut parts: Vec<String> = Vec::new();
    if !prefix.is_empty() {
        let cleaned = clean(prefix);
        if cleaned.is_empty() {
            panic!("prefix (\"{}\") has no letters or digits", prefix);
        }
        parts.push(cleaned);
    }
    for (index, attribute) in attributes.iter().enumerate() {
        let cleaned = clean(attribute);
        if cleaned.is_empty() {
            panic!(
                "attribute {} (\"{}\") has no letters or digits",
                index + 1,
                attribute
            );
        }
        // Cleaned text is ASCII, so byte length is character count.
        if segment_length > 0 && cleaned.len() > segment_length as usize {
            parts.push(cleaned[..segment_length as usize].to_string());
        } else {
            parts.push(cleaned);
        }
    }
    parts.join("-")
}

pub fn fune_vector(args: &[Value]) -> Value {
    let attributes: Vec<String> = args[1]
        .as_arr()
        .iter()
        .map(|v| v.as_str().to_string())
        .collect();
    Value::Str(generate_sku(args[0].as_str(), &attributes, args[2].as_i64()))
}