use super::funejson::Value; // 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 = 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 = 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())) }