Shared Rust kernels

ported/a1_refs

All modules · quoted at commit 9e1884369a67 of DnaCalc/DnaCalc. Comments are the source's own.

split_prefix · 13 lines · calc-func/src/ported/a1_refs.rs on GitHub
fn split_prefix(target: &str) -> (Option<String>, &str) {
    if let Some(idx) = target.rfind('!') {
        let prefix = target[..idx].trim();
        let rest = target[idx + 1..].trim();
        if prefix.is_empty() {
            (None, rest)
        } else {
            (Some(prefix.to_string()), rest)
        }
    } else {
        (None, target.trim())
    }
}

Used by 6 functions.

parse_cell_token · 40 lines · calc-func/src/ported/a1_refs.rs on GitHub
fn parse_cell_token(token: &str) -> Option<(usize, usize)> {
    let trimmed = token.trim();
    if trimmed.is_empty() {
        return None;
    }

    let mut letters = String::new();
    let mut digits = String::new();
    for ch in trimmed.chars() {
        if ch == '$' {
            continue;
        }
        if ch.is_ascii_alphabetic() && digits.is_empty() {
            letters.push(ch.to_ascii_uppercase());
            continue;
        }
        if ch.is_ascii_digit() {
            digits.push(ch);
            continue;
        }
        return None;
    }

    if letters.is_empty() || digits.is_empty() {
        return None;
    }

    let mut col = 0usize;
    for ch in letters.chars() {
        let value = usize::from((ch as u8) - b'A' + 1);
        col = col.checked_mul(26)?.checked_add(value)?;
    }

    let row = digits.parse::<usize>().ok()?;
    if row == 0 || col == 0 {
        return None;
    }

    Some((row, col))
}

Used by 6 functions.

parse_column_token · 34 lines · calc-func/src/ported/a1_refs.rs on GitHub
fn parse_column_token(token: &str) -> Option<usize> {
    let trimmed = token.trim();
    if trimmed.is_empty() {
        return None;
    }

    let mut letters = String::new();
    for ch in trimmed.chars() {
        if ch == '$' {
            continue;
        }
        if ch.is_ascii_alphabetic() {
            letters.push(ch.to_ascii_uppercase());
            continue;
        }
        return None;
    }

    if letters.is_empty() {
        return None;
    }

    let mut col = 0usize;
    for ch in letters.chars() {
        let value = usize::from((ch as u8) - b'A' + 1);
        col = col.checked_mul(26)?.checked_add(value)?;
    }

    if col == 0 || col > EXCEL_MAX_COLS {
        return None;
    }

    Some(col)
}

Used by 6 functions.

parse_row_token · 18 lines · calc-func/src/ported/a1_refs.rs on GitHub
fn parse_row_token(token: &str) -> Option<usize> {
    let trimmed = token.trim();
    if trimmed.is_empty() {
        return None;
    }

    let digits: String = trimmed.chars().filter(|ch| *ch != '$').collect();
    if digits.is_empty() || !digits.chars().all(|ch| ch.is_ascii_digit()) {
        return None;
    }

    let row = digits.parse::<usize>().ok()?;
    if row == 0 || row > EXCEL_MAX_ROWS {
        return None;
    }

    Some(row)
}

Used by 6 functions.

parse_a1_reference · 59 lines · calc-func/src/ported/a1_refs.rs on GitHub
pub fn parse_a1_reference(target: impl AsRef<str>) -> Option<A1Reference> {
    let (prefix, body) = split_prefix(target.as_ref());
    if body.is_empty() {
        return None;
    }

    let parts: Vec<&str> = body.split(':').collect();
    if parts.len() > 2 {
        return None;
    }

    let parsed = if let Some((start_row, start_col)) = parse_cell_token(parts[0]) {
        let (end_row, end_col) = if parts.len() == 2 {
            parse_cell_token(parts[1])?
        } else {
            (start_row, start_col)
        };
        A1Reference {
            prefix,
            start_row: start_row.min(end_row),
            start_col: start_col.min(end_col),
            end_row: start_row.max(end_row),
            end_col: start_col.max(end_col),
            notation: A1ReferenceNotation::Rect,
        }
    } else if let Some(start_col) = parse_column_token(parts[0]) {
        let end_col = if parts.len() == 2 {
            parse_column_token(parts[1])?
        } else {
            start_col
        };
        A1Reference {
            prefix,
            start_row: 1,
            start_col: start_col.min(end_col),
            end_row: EXCEL_MAX_ROWS,
            end_col: start_col.max(end_col),
            notation: A1ReferenceNotation::WholeColumn,
        }
    } else if let Some(start_row) = parse_row_token(parts[0]) {
        let end_row = if parts.len() == 2 {
            parse_row_token(parts[1])?
        } else {
            start_row
        };
        A1Reference {
            prefix,
            start_row: start_row.min(end_row),
            start_col: 1,
            end_row: start_row.max(end_row),
            end_col: EXCEL_MAX_COLS,
            notation: A1ReferenceNotation::WholeRow,
        }
    } else {
        return None;
    };

    Some(parsed)
}

Used by 6 functions.