use super::{sheet_prefix, RefersTo, Row, Sink, Value};
use crate::form::Form;
use crate::printer::write_datum;
fn quoted(s: &str) -> String {
write_datum(&Form::string(s))
}
pub fn target(to: &RefersTo) -> String {
match to {
RefersTo::Reference(text) => quoted(text),
RefersTo::Unreadable(name) => format!("(unreadable {})", quoted(name)),
}
}
pub fn datum(v: &Value) -> String {
match v {
Value::Number(text) => text.clone(),
Value::Text(text) => quoted(text),
Value::Bool(true) => "true".to_string(),
Value::Bool(false) => "false".to_string(),
Value::Error(text) => format!("(error {})", quoted(text)),
Value::Date(text) => format!("(date {})", quoted(text)),
}
}
pub fn line(row: &Row<'_>) -> String {
match row {
Row::Sheet { name, visibility } => {
format!("(sheet {} {})", quoted(name), visibility.word())
}
Row::Name { name, refers_to } => {
format!("(name {} {})", quoted(name), quoted(refers_to))
}
Row::Table { name, sheet, range } => {
format!(
"(table {} {} {})",
quoted(name),
quoted(sheet),
quoted(range)
)
}
Row::Cell { sheet, addr, value } => {
format!("(cell {} {} {})", quoted(sheet), quoted(addr), datum(value))
}
Row::Formula { sheet, addr, text } => {
format!(
"(formula {} {} {})",
quoted(sheet),
quoted(addr),
quoted(text)
)
}
Row::Refers { sheet, addr, to } => {
format!(
"(refers {} {})",
quoted(&format!("{}!{}", sheet_prefix(sheet), addr)),
target(to)
)
}
}
}
#[derive(Default)]
pub struct Printer {
pub out: String,
}
impl Sink for Printer {
fn row(&mut self, row: &Row<'_>) {
self.out.push_str(&line(row));
self.out.push('\n');
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::reflect::Visibility;
#[test]
fn each_row_prints_in_the_corpus_notation() {
assert_eq!(
line(&Row::Sheet {
name: "Q1 Data",
visibility: Visibility::VeryHidden
}),
"(sheet \"Q1 Data\" very-hidden)"
);
assert_eq!(
line(&Row::Name {
name: "Rate",
refers_to: "Model!$B$1"
}),
"(name \"Rate\" \"Model!$B$1\")"
);
assert_eq!(
line(&Row::Table {
name: "Sales",
sheet: "Data",
range: "A1:B4"
}),
"(table \"Sales\" \"Data\" \"A1:B4\")"
);
assert_eq!(
line(&Row::Cell {
sheet: "Model",
addr: "B1",
value: &Value::Number("1200".to_string())
}),
"(cell \"Model\" \"B1\" 1200)"
);
assert_eq!(
line(&Row::Cell {
sheet: "Model",
addr: "A1",
value: &Value::Text("say \"hi\"".to_string())
}),
"(cell \"Model\" \"A1\" \"say \\\"hi\\\"\")"
);
assert_eq!(datum(&Value::Bool(true)), "true");
assert_eq!(datum(&Value::Bool(false)), "false");
assert_eq!(
datum(&Value::Error("#DIV/0!".to_string())),
"(error \"#DIV/0!\")"
);
assert_eq!(
datum(&Value::Date("2026-10-03".to_string())),
"(date \"2026-10-03\")"
);
assert_eq!(
line(&Row::Formula {
sheet: "Model",
addr: "B3",
text: "=B1-B2"
}),
"(formula \"Model\" \"B3\" \"=B1-B2\")"
);
assert_eq!(
line(&Row::Refers {
sheet: "Q1 Data",
addr: "B3",
to: &RefersTo::Reference("Model!B1".to_string())
}),
"(refers \"'Q1 Data'!B3\" \"Model!B1\")"
);
assert_eq!(
line(&Row::Refers {
sheet: "Model",
addr: "D2",
to: &RefersTo::Unreadable("INDIRECT")
}),
"(refers \"Model!D2\" (unreadable \"INDIRECT\"))"
);
assert!(
target(&RefersTo::Reference("Model!B1".to_string()))
< target(&RefersTo::Unreadable("OFFSET"))
);
let mut p = Printer::default();
p.row(&Row::Sheet {
name: "A",
visibility: Visibility::Visible,
});
p.row(&Row::Sheet {
name: "B",
visibility: Visibility::Hidden,
});
assert_eq!(p.out, "(sheet \"A\" visible)\n(sheet \"B\" hidden)\n");
}
}