Functional Weave
Code in TypeScript

text.truncate@1.0.0

impl/rust.rs

2,382 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

/// The Unicode White_Space property, spelled out so all three languages agree.
/// It happens to match `char::is_whitespace`, but JavaScript's \s and Python's
/// str.isspace draw the line differently, so the list is explicit everywhere.
fn is_whitespace(ch: char) -> bool {
    let cp = ch as u32;
    (0x09..=0x0d).contains(&cp)
        || cp == 0x20
        || cp == 0x85
        || cp == 0xa0
        || cp == 0x1680
        || (0x2000..=0x200a).contains(&cp)
        || cp == 0x2028
        || cp == 0x2029
        || cp == 0x202f
        || cp == 0x205f
        || cp == 0x3000
}

/// Punctuation that reads badly directly before an ellipsis.
fn is_trailing_punctuation(ch: char) -> bool {
    matches!(ch, ',' | ';' | ':' | '-' | '.')
}

/// Shorten `value` to at most `max_length` code points, ellipsis included,
/// cutting at a word boundary where there is one.
///
/// # Panics
/// Panics if `max_length` is negative or shorter than the ellipsis.
pub fn truncate(value: &str, max_length: i64, ellipsis: &str) -> String {
    if max_length < 0 {
        panic!("maxLength must be 0 or greater, received {}", max_length);
    }
    let chars: Vec<char> = value.chars().collect();
    let mark_len = ellipsis.chars().count() as i64;
    if mark_len > max_length {
        panic!(
            "maxLength must be at least the length of the ellipsis, received {}",
            max_length
        );
    }
    if chars.len() as i64 <= max_length {
        return value.to_string();
    }

    let budget = (max_length - mark_len) as usize;
    let mut end = budget;
    // A cut just before whitespace is already a clean boundary.
    if !is_whitespace(chars[budget]) {
        // No boundary to move back to (none, or only at position 0): one long
        // word is cut hard rather than reduced to a bare ellipsis.
        if let Some(last_space) = (0..budget).rev().find(|&i| is_whitespace(chars[i])) {
            if last_space > 0 {
                end = last_space;
            }
        }
    }

    while end > 0 && (is_whitespace(chars[end - 1]) || is_trailing_punctuation(chars[end - 1])) {
        end -= 1;
    }
    let mut out: String = chars[..end].iter().collect();
    out.push_str(ellipsis);
    out
}

pub fn fune_vector(args: &[Value]) -> Value {
    Value::Str(truncate(args[0].as_str(), args[1].as_i64(), args[2].as_str()))
}