mirror of
https://github.com/storytold/gridcraft.git
synced 2026-11-09 20:09:57 -06:00
numfmt: TEXT returns #VALUE! when its result passes a cell's 32,767 characters
A text section repeats its input once per @ placeholder, and TEXT handed back whatever that built, so nested TEXT calls could multiply a long text until memory ran out. Results past the cell text limit are now #VALUE!.
This commit is contained in:
@@ -425,6 +425,9 @@ impl NumberFormat {
|
|||||||
/// The `TEXT()` worksheet function: formats a value with a format code. Numeric text is
|
/// The `TEXT()` worksheet function: formats a value with a format code. Numeric text is
|
||||||
/// converted to a number first; other text and booleans (as `TRUE`/`FALSE`) go through the text
|
/// converted to a number first; other text and booleans (as `TRUE`/`FALSE`) go through the text
|
||||||
/// section, or are returned as is.
|
/// section, or are returned as is.
|
||||||
|
/// Longest text a cell holds, in characters (Excel's limit), and so the longest `TEXT` result.
|
||||||
|
const MAX_CELL_TEXT_CHARS: usize = 32_767;
|
||||||
|
|
||||||
pub fn text_function(v: &Value, code: &str, sys: DateSystem) -> Result<String, CellError> {
|
pub fn text_function(v: &Value, code: &str, sys: DateSystem) -> Result<String, CellError> {
|
||||||
if code.is_empty() {
|
if code.is_empty() {
|
||||||
return Ok(String::new());
|
return Ok(String::new());
|
||||||
@@ -440,7 +443,15 @@ pub fn text_function(v: &Value, code: &str, sys: DateSystem) -> Result<String, C
|
|||||||
Value::Bool(b) => return Ok(fmt.format_text(if b { "TRUE" } else { "FALSE" }).text),
|
Value::Bool(b) => return Ok(fmt.format_text(if b { "TRUE" } else { "FALSE" }).text),
|
||||||
Value::Text(t) => match gridcraft_core::parse::parse_number_text(&t) {
|
Value::Text(t) => match gridcraft_core::parse::parse_number_text(&t) {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
None => return Ok(fmt.format_text(&t).text),
|
// Every `@` repeats the text: a result past a cell's limit is `#VALUE!`, so nested
|
||||||
|
// TEXT calls can't multiply it further.
|
||||||
|
None => {
|
||||||
|
let out = fmt.format_text(&t).text;
|
||||||
|
if out.len() > MAX_CELL_TEXT_CHARS && out.chars().count() > MAX_CELL_TEXT_CHARS {
|
||||||
|
return Err(CellError::Value);
|
||||||
|
}
|
||||||
|
return Ok(out);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Value::Array(_) => return Err(CellError::Value),
|
Value::Array(_) => return Err(CellError::Value),
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -396,6 +396,10 @@ fn text_fn() {
|
|||||||
assert_eq!(text_function(&Value::Number(1234.5), "$#,##0.00", S), Ok("$1,234.50".into()));
|
assert_eq!(text_function(&Value::Number(1234.5), "$#,##0.00", S), Ok("$1,234.50".into()));
|
||||||
assert_eq!(text_function(&Value::from("5"), "0.00", S), Ok("5.00".into()));
|
assert_eq!(text_function(&Value::from("5"), "0.00", S), Ok("5.00".into()));
|
||||||
assert_eq!(text_function(&Value::from("abc"), "0.00", S), Ok("abc".into()));
|
assert_eq!(text_function(&Value::from("abc"), "0.00", S), Ok("abc".into()));
|
||||||
|
// Each `@` repeats the text: up to a cell's 32,767 characters, then #VALUE!.
|
||||||
|
let at = "@".repeat(255);
|
||||||
|
assert_eq!(text_function(&Value::from("x".repeat(128).as_str()), &at, S).map(|s| s.len()), Ok(128 * 255));
|
||||||
|
assert_eq!(text_function(&Value::from("x".repeat(32_000).as_str()), &at, S), Err(CellError::Value));
|
||||||
assert_eq!(text_function(&Value::Error(CellError::NA), "0", S), Err(CellError::NA));
|
assert_eq!(text_function(&Value::Error(CellError::NA), "0", S), Err(CellError::NA));
|
||||||
assert_eq!(text_function(&Value::Number(46302.0), "yyyy", S), Ok("2026".into()));
|
assert_eq!(text_function(&Value::Number(46302.0), "yyyy", S), Ok("2026".into()));
|
||||||
assert_eq!(text_function(&Value::Number(0.5), "General", S), Ok("0.5".into()));
|
assert_eq!(text_function(&Value::Number(0.5), "General", S), Ok("0.5".into()));
|
||||||
|
|||||||
Reference in New Issue
Block a user