Shared Rust kernels

catalogue/host_facts

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

first_cell · 7 lines · calc-func/src/catalogue/host_facts.rs on GitHub
/// The first cell of a reference argument.
fn first_cell(site: &Site<'_>, a: &[CalcValue], index: usize) -> Step<CellKey> {
    match a[index].core_ref() {
        CoreRef::Reference(reference) => Ok(reference.areas()[0].first()),
        _ => Err(site.not_modelled(Some(index))),
    }
}

Used by 5 functions.

cell_value · 11 lines · calc-func/src/catalogue/host_facts.rs on GitHub
/// The value of one cell, read through the services' cell reader.
fn cell_value(site: &Site<'_>, cell: CellKey, index: usize, sv: &mut EvalServices<'_>) -> Step<CalcValue> {
    let one = [CalcValue::reference(ReferenceLike::cell(cell))];
    let g = grid(site, &one, 0, sv).map_err(|exit| match exit {
        Exit::Fault(crate::call::FunctionFault::ReferenceUnreadable { id, .. }) => {
            Exit::Fault(crate::call::FunctionFault::ReferenceUnreadable { id, index })
        }
        other => other,
    })?;
    Ok(g.cells.into_iter().next().unwrap_or_else(CalcValue::empty))
}

Used by 5 functions.

column_letters · 9 lines · calc-func/src/catalogue/host_facts.rs on GitHub
fn column_letters(mut column: u32) -> String {
    let mut letters = Vec::new();
    while column > 0 {
        let rem = (column - 1) % 26;
        letters.push((b'A' + rem as u8) as char);
        column = (column - 1) / 26;
    }
    letters.iter().rev().collect()
}

Used by 5 functions.

facts · 10 lines · calc-func/src/catalogue/host_facts.rs on GitHub
/// The host's workbook facts, or a refusal of the call naming argument `index`.
fn facts(
    site: &Site<'_>,
    sv: &mut EvalServices<'_>,
    index: Option<usize>,
) -> Step<Arc<dyn WorkbookFacts>> {
    sv.host()
        .and_then(|host| host.workbook_facts())
        .ok_or_else(|| site.not_modelled(index))
}

Used by 5 functions.

qualified_sheet · 19 lines · calc-func/src/catalogue/host_facts.rs on GitHub
/// `[file]Sheet` of `sheet`, quoted as a reference names it when it must be
/// (`'[my-book.xlsx]My Sheet'`).
fn qualified_sheet(
    site: &Site<'_>,
    sheet: SheetId,
    sv: &mut EvalServices<'_>,
    index: usize,
) -> Step<String> {
    let file = facts(site, sv, Some(index))?
        .file()
        .ok_or_else(|| site.not_modelled(Some(index)))?;
    let name = sv
        .host()
        .and_then(|host| host.sheet_name(sheet))
        .ok_or_else(|| site.not_modelled(Some(index)))?;
    let text = ExcelText::from_interop_assignment(&format!("[{}]{}", file.name, name));
    let quoted = address::quote_sheet_text_if_needed(&text).map_err(Exit::Error)?;
    Ok(String::from_utf16_lossy(&quoted))
}

Used by 5 functions.

cell · 127 lines · calc-func/src/catalogue/host_facts.rs on GitHub
/// CELL (dc-uar). The info type is matched without regard to case and is not trimmed
/// (`CELL(" format ",B2)` is #VALUE!). Without a reference CELL reads the active cell of the
/// active sheet, as Excel does when it calculates a workbook it opened (measured: a file saved
/// with Second!D7 active gives `[o.xlsx]Second!$D$7` for `CELL("address")` on Sheet1). The
/// readings behind each type, Excel 16.0.20527 through COM, 2026-10-29 (the dcuar_cell rows):
/// - `address`: `$B$3` on the calling sheet, `[book.xlsx]Sheet2!$B$3` on another, quoted as a
///   reference to it would be.
/// - `filename`: the folder, `[file]` and the sheet's name; empty text for a workbook never
///   saved.
/// - `format`, `color`, `parentheses`: the number format's class ([`cell_info::classify`]).
/// - `prefix`: for a cell holding a text constant, `"` when right aligned, `^` when centred
///   (centre, centre across selection), `\` for fill, and `'` for any other alignment (general,
///   left, justify, distributed); empty text for any other cell (a formula, a number, a logical,
///   an error, a blank cell).
/// - `protect`: 1 for a locked cell, 0 otherwise (hidden does not count).
/// - `width`: the column's width rounded to a whole number of characters, 0 for a hidden column,
///   with whether it is the sheet's default width: the array {width, default}, which a legacy
///   formula intersects to the width.
fn cell(site: &Site<'_>, a: &[CalcValue], sv: &mut EvalServices<'_>) -> Step<CalcValue> {
    let info = text_arg(site, a, 0)?.to_string_lossy().to_ascii_lowercase();
    let known = [
        "address", "row", "col", "contents", "type", "filename", "format", "color",
        "parentheses", "prefix", "protect", "width",
    ];
    if !known.contains(&info.as_str()) {
        return fail(WorksheetErrorCode::Value);
    }
    let at = if a.len() < 2 || a[1].is_missing() {
        facts(site, sv, None)?
            .active_cell()
            .ok_or_else(|| site.not_modelled(None))?
    } else {
        let at = first_cell(site, a, 1)?;
        // dc-ylr: Excel gives CELL of a reference to a closed workbook #N/A.
        if sv.host().is_some_and(|host| host.is_external_sheet(at.sheet())) {
            return fail(WorksheetErrorCode::NA);
        }
        at
    };
    let format = |site: &Site<'_>, sv: &mut EvalServices<'_>| -> Step<CellFormatFacts> {
        facts(site, sv, Some(1))?
            .cell_format(at)
            .ok_or_else(|| site.not_modelled(Some(1)))
    };
    let class = |site: &Site<'_>, sv: &mut EvalServices<'_>| -> Step<cell_info::FormatClass> {
        let facts = format(site, sv)?;
        let profile = sv
            .environment()
            .and_then(|environment| environment.profile)
            .ok_or_else(|| site.not_modelled(Some(1)))?;
        Ok(cell_info::classify(&facts.number_format, facts.builtin, &profile))
    };
    let text = crate::catalogue::kit::text_value;
    match info.as_str() {
        "row" => s_number(site, at.row() as f64),
        "col" => s_number(site, at.column() as f64),
        "address" => {
            let caller = sv.host().and_then(|host| host.caller());
            let local = format!("${}${}", column_letters(at.column()), at.row());
            match caller {
                Some(caller) if caller.sheet() == at.sheet() => Ok(text(&local)),
                Some(_) => {
                    let sheet = qualified_sheet(site, at.sheet(), sv, 1)?;
                    Ok(text(&format!("{sheet}!{local}")))
                }
                None => Err(site.not_modelled(Some(1))),
            }
        }
        "filename" => {
            let file = facts(site, sv, Some(1))?
                .file()
                .ok_or_else(|| site.not_modelled(Some(1)))?;
            let Some(folder) = file.folder else {
                return Ok(text(""));
            };
            let name = sv
                .host()
                .and_then(|host| host.sheet_name(at.sheet()))
                .ok_or_else(|| site.not_modelled(Some(1)))?;
            Ok(text(&format!("{folder}[{}]{name}", file.name)))
        }
        "contents" => cell_value(site, at, 1, sv),
        "type" => {
            let value = cell_value(site, at, 1, sv)?;
            let kind = match value.core_ref() {
                CoreRef::Empty | CoreRef::Missing => "b",
                CoreRef::Text(_) => "l",
                _ => "v",
            };
            Ok(text(kind))
        }
        "format" => Ok(text(&class(site, sv)?.code)),
        "color" => s_number(site, if class(site, sv)?.color { 1.0 } else { 0.0 }),
        "parentheses" => s_number(site, if class(site, sv)?.parentheses { 1.0 } else { 0.0 }),
        "protect" => s_number(site, if format(site, sv)?.locked { 1.0 } else { 0.0 }),
        "prefix" => {
            let formula = sv
                .host()
                .and_then(|host| host.is_formula(at))
                .ok_or_else(|| site.not_modelled(Some(1)))?;
            let value = cell_value(site, at, 1, sv)?;
            if formula || !matches!(value.core_ref(), CoreRef::Text(_)) {
                return Ok(text(""));
            }
            let prefix = match format(site, sv)?.horizontal {
                HorizontalAlignment::Right => "\"",
                HorizontalAlignment::Center | HorizontalAlignment::CenterContinuous => "^",
                HorizontalAlignment::Fill => "\\",
                _ => "'",
            };
            Ok(text(prefix))
        }
        "width" => {
            let width = facts(site, sv, Some(1))?
                .column_width(at.sheet(), at.column())
                .ok_or_else(|| site.not_modelled(Some(1)))?;
            let rounded = (width.characters + 0.5).floor();
            CalcArray::from_rows(vec![vec![
                CalcValue::number(rounded),
                CalcValue::logical(width.default),
            ]])
            .map(CalcValue::array)
            .ok_or_else(|| site.not_modelled(None))
        }
        _ => Err(site.not_modelled(Some(0))),
    }
}

Used by 5 functions.

s_number · 3 lines · calc-func/src/catalogue/host_facts.rs on GitHub
fn s_number(site: &Site<'_>, n: f64) -> Step<CalcValue> {
    site.number(n)
}

Used by 5 functions.

info · 59 lines · calc-func/src/catalogue/host_facts.rs on GitHub
/// INFO (dc-uar). The type is matched without regard to case and is not trimmed. Measured in
/// Excel 16.0.20527 through COM, 2026-10-29: `directory` is the current folder; `numfile` the
/// number of worksheets (a hidden one counts, a chart sheet does not); `origin` `$A:` and the
/// top left cell of the calling sheet's window (`$A:$C$5` for a sheet saved scrolled to C5);
/// `osversion`, `release` and `system` the application's facts (`Windows (64-bit) NT 10.00`,
/// `16.0`, `pcdos`); `recalc` `Automatic` or `Manual` (automatic except tables gives
/// `Automatic`); `memavail`, `memused` and `totmem` are #N/A; any other text is #VALUE!.
fn info(site: &Site<'_>, a: &[CalcValue], sv: &mut EvalServices<'_>) -> Step<CalcValue> {
    let kind = text_arg(site, a, 0)?.to_string_lossy().to_ascii_lowercase();
    let text = |value: Option<String>| -> Step<CalcValue> {
        value
            .map(|value| crate::catalogue::kit::text_value(&value))
            .ok_or_else(|| site.not_modelled(Some(0)))
    };
    match kind.as_str() {
        "memavail" | "memused" | "totmem" => fail(WorksheetErrorCode::NA),
        "numfile" => match sv.host().and_then(|host| host.sheet_count()) {
            Some(count) => site.number(f64::from(count)),
            None => Err(site.not_modelled(Some(0))),
        },
        "origin" => {
            let sheet = sv
                .host()
                .and_then(|host| host.caller())
                .map(|caller| caller.sheet())
                .ok_or_else(|| site.not_modelled(Some(0)))?;
            let cell = facts(site, sv, Some(0))?
                .top_left_cell(sheet)
                .ok_or_else(|| site.not_modelled(Some(0)))?;
            text(Some(format!(
                "$A:${}${}",
                column_letters(cell.column()),
                cell.row()
            )))
        }
        "recalc" => {
            let mode = facts(site, sv, Some(0))?
                .calculation_mode()
                .ok_or_else(|| site.not_modelled(Some(0)))?;
            let name = match mode {
                CalculationMode::Manual => "Manual",
                CalculationMode::Automatic | CalculationMode::AutomaticExceptTables => "Automatic",
            };
            text(Some(name.to_owned()))
        }
        "directory" | "osversion" | "release" | "system" => {
            let application = facts(site, sv, Some(0))?
                .application()
                .ok_or_else(|| site.not_modelled(Some(0)))?;
            text(match kind.as_str() {
                "directory" => application.directory,
                "osversion" => application.os_version,
                "release" => application.release,
                _ => application.system,
            })
        }
        _ => fail(WorksheetErrorCode::Value),
    }
}

Used by 5 functions.

sheet · 27 lines · calc-func/src/catalogue/host_facts.rs on GitHub
fn sheet(site: &Site<'_>, a: &[CalcValue], sv: &mut EvalServices<'_>) -> Step<CalcValue> {
    let sheet = if a.is_empty() || a[0].is_missing() {
        sv.host().and_then(|host| host.caller()).map(|cell| cell.sheet())
    } else {
        match a[0].core_ref() {
            CoreRef::Reference(reference) => Some(reference.sheet()),
            CoreRef::Error(code) => return fail(code),
            CoreRef::Text(text) => {
                let name = text.to_string_lossy();
                match sv.host() {
                    Some(host) => match host.sheet_named(&name) {
                        Some(sheet) => Some(sheet),
                        None if host.sheet_count().is_some() => return fail(WorksheetErrorCode::NA),
                        None => None,
                    },
                    None => None,
                }
            }
            _ => return fail(WorksheetErrorCode::NA),
        }
    };
    let index = sheet.and_then(|sheet| sv.host().and_then(|host| host.sheet_index(sheet)));
    match index {
        Some(index) => site.number(f64::from(index)),
        None => Err(site.not_modelled(Some(0))),
    }
}

Used by 5 functions.