refactor: replace CellValue::Empty with Option<CellValue>
Previously CellValue had three variants: Number, Text, and Empty. The Empty variant acted as a null sentinel, but the compiler could not distinguish between "this is a real value" and "this might be empty". Code that received a CellValue could use it without checking for Empty, because there was no type-level enforcement. Now CellValue has only Number and Text. The absence of a value is represented as None at every API boundary: DataStore::get() → Option<&CellValue> (was &CellValue / Empty) Model::get_cell() → Option<&CellValue> (was &CellValue / Empty) Model::evaluate() → Option<CellValue> (was CellValue::Empty) eval_formula() → Option<CellValue> (was CellValue::Empty) Model gains clear_cell() for explicit key removal; ClearCell dispatch calls it instead of set_cell(key, CellValue::Empty). The compiler now forces every caller of evaluate/get_cell to handle the None case explicitly — accidental use of an empty value as if it were real is caught at compile time rather than silently computing wrong results. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@ -97,7 +97,9 @@ impl<'a> GridWidget<'a> {
|
||||
};
|
||||
let value = self.model.evaluate(&key);
|
||||
|
||||
let cell_str = format_value(&value, fmt_comma, fmt_decimals);
|
||||
let cell_str = value.as_ref()
|
||||
.map(|v| format_value(v, fmt_comma, fmt_decimals))
|
||||
.unwrap_or_default();
|
||||
let is_selected = ri == sel_row && ci == sel_col;
|
||||
let is_search_match = !self.search_query.is_empty()
|
||||
&& cell_str.to_lowercase().contains(&self.search_query.to_lowercase());
|
||||
@ -106,7 +108,7 @@ impl<'a> GridWidget<'a> {
|
||||
Style::default().fg(Color::Black).bg(Color::Cyan).add_modifier(Modifier::BOLD)
|
||||
} else if is_search_match {
|
||||
Style::default().fg(Color::Black).bg(Color::Yellow)
|
||||
} else if matches!(value, CellValue::Empty) {
|
||||
} else if value.is_none() {
|
||||
Style::default().fg(Color::DarkGray)
|
||||
} else {
|
||||
Style::default()
|
||||
@ -149,7 +151,7 @@ impl<'a> GridWidget<'a> {
|
||||
if x >= area.x + area.width { break; }
|
||||
let total: f64 = (0..layout.row_count())
|
||||
.filter_map(|ri| layout.cell_key(ri, ci))
|
||||
.map(|key| self.model.evaluate(&key).as_f64().unwrap_or(0.0))
|
||||
.map(|key| self.model.evaluate(&key).and_then(|v| v.as_f64()).unwrap_or(0.0))
|
||||
.sum();
|
||||
let total_str = format_f64(total, fmt_comma, fmt_decimals);
|
||||
buf.set_string(x, y,
|
||||
@ -202,7 +204,6 @@ fn format_value(v: &CellValue, comma: bool, decimals: u8) -> String {
|
||||
match v {
|
||||
CellValue::Number(n) => format_f64(*n, comma, decimals),
|
||||
CellValue::Text(s) => s.clone(),
|
||||
CellValue::Empty => String::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user