UPDATE: Adds name validation and exposes it in wasm

We do a trick I am not proud of. Because all of our errors are Strings,
we don't have a way to separate a name error from an index error,
for instance. What I do in prepend the error with a string that indicates
where it comes from.
This commit is contained in:
Nicolás Hatcher
2025-11-17 19:57:18 +01:00
committed by Nicolás Hatcher Andrés
parent 3db094c956
commit 1391f196b5
7 changed files with 108 additions and 49 deletions

View File

@@ -2068,21 +2068,7 @@ impl Model {
scope: Option<u32>,
formula: &str,
) -> Result<(), String> {
if !is_valid_identifier(name) {
return Err("Invalid defined name".to_string());
};
let name_upper = name.to_uppercase();
let defined_names = &self.workbook.defined_names;
let sheet_id = match scope {
Some(index) => Some(self.workbook.worksheet(index)?.sheet_id),
None => None,
};
// if the defined name already exist return error
for df in defined_names {
if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
return Err("Defined name already exists".to_string());
}
}
let sheet_id = self.is_valid_defined_name(name, scope, formula)?;
self.workbook.defined_names.push(DefinedName {
name: name.to_string(),
formula: formula.to_string(),
@@ -2093,6 +2079,48 @@ impl Model {
Ok(())
}
/// Validates if a defined name can be created
pub fn is_valid_defined_name(
&self,
name: &str,
scope: Option<u32>,
formula: &str,
) -> Result<Option<u32>, String> {
if !is_valid_identifier(name) {
return Err("Name: Invalid defined name".to_string());
}
let name_upper = name.to_uppercase();
let defined_names = &self.workbook.defined_names;
let sheet_id = match scope {
Some(index) => match self.workbook.worksheet(index) {
Ok(ws) => Some(ws.sheet_id),
Err(_) => return Err("Scope: Invalid sheet index".to_string()),
},
None => None,
};
// if the defined name already exist return error
for df in defined_names {
if df.name.to_uppercase() == name_upper && df.sheet_id == sheet_id {
return Err("Name: Defined name already exists".to_string());
}
}
// Make sure the formula is valid
match common::ParsedReference::parse_reference_formula(
None,
formula,
&self.locale,
|name| self.get_sheet_index_by_name(name),
) {
Ok(_) => {}
Err(_) => {
return Err("Formula: Invalid defined name formula".to_string());
}
};
Ok(sheet_id)
}
/// Delete defined name of name and scope
pub fn delete_defined_name(&mut self, name: &str, scope: Option<u32>) -> Result<(), String> {
let name_upper = name.to_uppercase();
@@ -2126,7 +2154,7 @@ impl Model {
new_formula: &str,
) -> Result<(), String> {
if !is_valid_identifier(new_name) {
return Err("Invalid defined name".to_string());
return Err("Name: Invalid defined name".to_string());
};
let name_upper = name.to_uppercase();
let new_name_upper = new_name.to_uppercase();
@@ -2134,18 +2162,28 @@ impl Model {
if name_upper != new_name_upper || scope != new_scope {
for key in self.parsed_defined_names.keys() {
if key.1.to_uppercase() == new_name_upper && key.0 == new_scope {
return Err("Defined name already exists".to_string());
return Err("Name: Defined name already exists".to_string());
}
}
}
let defined_names = &self.workbook.defined_names;
let sheet_id = match scope {
Some(index) => Some(self.workbook.worksheet(index)?.sheet_id),
Some(index) => Some(
self.workbook
.worksheet(index)
.map_err(|_| "Scope: Invalid sheet index")?
.sheet_id,
),
None => None,
};
let new_sheet_id = match new_scope {
Some(index) => Some(self.workbook.worksheet(index)?.sheet_id),
Some(index) => Some(
self.workbook
.worksheet(index)
.map_err(|_| "Scope: Invalid sheet index")?
.sheet_id,
),
None => None,
};