1use crate::expressions::token::get_error_by_name;
2use crate::expressions::types::CellReferenceIndex;
3use crate::language::Language;
4
5use crate::{
6 expressions::{
7 lexer::{Lexer, LexerMode},
8 token::TokenType,
9 },
10 language::get_language,
11 locale::Locale,
12};
13
14#[derive(Debug, Eq, PartialEq)]
15pub enum ParsedReference {
16 CellReference(CellReferenceIndex),
17 Range(CellReferenceIndex, CellReferenceIndex),
18}
19
20impl ParsedReference {
21 pub(crate) fn parse_reference_formula<F: Fn(&str) -> Option<u32>>(
32 sheet_index_context: Option<u32>,
33 reference: &str,
34 locale: &Locale,
35 get_sheet_index_by_name: F,
36 ) -> Result<ParsedReference, String> {
37 #[allow(clippy::expect_used)]
38 let language = get_language("en").expect("");
39 let mut lexer = Lexer::new(reference, LexerMode::A1, locale, language);
40
41 let reference_token = lexer.next_token();
42 let eof_token = lexer.next_token();
43
44 if TokenType::EOF != eof_token {
45 return Err("Invalid reference. Expected only one token.".to_string());
46 }
47
48 match reference_token {
49 TokenType::Reference {
50 sheet: sheet_name,
51 column: column_id,
52 row: row_id,
53 ..
54 } => {
55 let sheet_index;
56 if let Some(name) = sheet_name {
57 match get_sheet_index_by_name(&name) {
58 Some(i) => sheet_index = i,
59 None => {
60 return Err(format!(
61 "Invalid reference. Sheet \"{}\" could not be found.",
62 name.as_str(),
63 ));
64 }
65 }
66 } else if let Some(sheet_index_context) = sheet_index_context {
67 sheet_index = sheet_index_context;
68 } else {
69 return Err(
70 "Reference doesn't contain sheet name and relative cell is not known."
71 .to_string(),
72 );
73 }
74
75 Ok(ParsedReference::CellReference(CellReferenceIndex {
76 sheet: sheet_index,
77 row: row_id,
78 column: column_id,
79 }))
80 }
81 TokenType::Range {
82 sheet: sheet_name,
83 left,
84 right,
85 } => {
86 let sheet_index;
87 if let Some(name) = sheet_name {
88 match get_sheet_index_by_name(&name) {
89 Some(i) => sheet_index = i,
90 None => {
91 return Err(format!(
92 "Invalid reference. Sheet \"{}\" could not be found.",
93 name.as_str(),
94 ));
95 }
96 }
97 } else if let Some(sheet_index_context) = sheet_index_context {
98 sheet_index = sheet_index_context;
99 } else {
100 return Err(
101 "Reference doesn't contain sheet name and relative cell is not known."
102 .to_string(),
103 );
104 }
105
106 Ok(ParsedReference::Range(
107 CellReferenceIndex {
108 sheet: sheet_index,
109 row: left.row,
110 column: left.column,
111 },
112 CellReferenceIndex {
113 sheet: sheet_index,
114 row: right.row,
115 column: right.column,
116 },
117 ))
118 }
119 _ => Err("Invalid reference. First token is not a reference.".to_string()),
120 }
121 }
122}
123
124pub(crate) fn value_needs_quoting(value: &str, language: &Language) -> bool {
130 value.starts_with(['=', '+', '-'])
131 || value.parse::<f64>().is_ok()
132 || value.to_lowercase().parse::<bool>().is_ok()
133 || get_error_by_name(&value.to_uppercase(), language).is_some()
134}
135
136pub fn get_all_timezones() -> Vec<String> {
138 crate::tz::get_all_timezone_names()
139}
140
141#[cfg(test)]
142mod tests {
143 #![allow(clippy::expect_used)]
144
145 use super::*;
146 use crate::language::get_language;
147 use crate::locale::{get_locale, Locale};
148
149 fn get_test_locale() -> &'static Locale {
150 #![allow(clippy::unwrap_used)]
151 get_locale("en").unwrap()
152 }
153
154 fn get_sheet_index_by_name(sheet_names: &[&str], name: &str) -> Option<u32> {
155 sheet_names
156 .iter()
157 .position(|&sheet_name| sheet_name == name)
158 .map(|index| index as u32)
159 }
160
161 #[test]
162 fn test_parse_cell_references() {
163 let locale = get_test_locale();
164 let sheet_names = vec!["Sheet1", "Sheet2", "Sheet3"];
165
166 assert_eq!(
167 ParsedReference::parse_reference_formula(Some(7), "A1", locale, |name| {
168 get_sheet_index_by_name(&sheet_names, name)
169 },),
170 Ok(ParsedReference::CellReference(CellReferenceIndex {
171 sheet: 7,
172 row: 1,
173 column: 1,
174 })),
175 );
176
177 assert_eq!(
178 ParsedReference::parse_reference_formula(None, "Sheet1!A1", locale, |name| {
179 get_sheet_index_by_name(&sheet_names, name)
180 },),
181 Ok(ParsedReference::CellReference(CellReferenceIndex {
182 sheet: 0,
183 row: 1,
184 column: 1,
185 })),
186 );
187
188 assert_eq!(
189 ParsedReference::parse_reference_formula(None, "Sheet1!$A$1", locale, |name| {
190 get_sheet_index_by_name(&sheet_names, name)
191 },),
192 Ok(ParsedReference::CellReference(CellReferenceIndex {
193 sheet: 0,
194 row: 1,
195 column: 1,
196 })),
197 );
198
199 assert_eq!(
200 ParsedReference::parse_reference_formula(None, "Sheet2!$A$1", locale, |name| {
201 get_sheet_index_by_name(&sheet_names, name)
202 },),
203 Ok(ParsedReference::CellReference(CellReferenceIndex {
204 sheet: 1,
205 row: 1,
206 column: 1,
207 })),
208 );
209 }
210
211 #[test]
212 fn test_parse_range_references() {
213 let locale = get_test_locale();
214 let sheet_names = vec!["Sheet1", "Sheet2", "Sheet3"];
215
216 assert_eq!(
217 ParsedReference::parse_reference_formula(Some(5), "A1:A2", locale, |name| {
218 get_sheet_index_by_name(&sheet_names, name)
219 },),
220 Ok(ParsedReference::Range(
221 CellReferenceIndex {
222 sheet: 5,
223 column: 1,
224 row: 1,
225 },
226 CellReferenceIndex {
227 sheet: 5,
228 column: 1,
229 row: 2,
230 },
231 )),
232 );
233
234 assert_eq!(
235 ParsedReference::parse_reference_formula(None, "Sheet1!$A$1:$B$10", locale, |name| {
236 get_sheet_index_by_name(&sheet_names, name)
237 },),
238 Ok(ParsedReference::Range(
239 CellReferenceIndex {
240 sheet: 0,
241 row: 1,
242 column: 1,
243 },
244 CellReferenceIndex {
245 sheet: 0,
246 row: 10,
247 column: 2,
248 },
249 )),
250 );
251
252 assert_eq!(
253 ParsedReference::parse_reference_formula(None, "Sheet2!AA1:E$11", locale, |name| {
254 get_sheet_index_by_name(&sheet_names, name)
255 },),
256 Ok(ParsedReference::Range(
257 CellReferenceIndex {
258 sheet: 1,
259 row: 1,
260 column: 27,
261 },
262 CellReferenceIndex {
263 sheet: 1,
264 row: 11,
265 column: 5,
266 },
267 )),
268 );
269 }
270
271 #[test]
272 fn test_error_reject_assignments() {
273 let locale = get_test_locale();
274 let sheet_index = Some(1);
275 assert_eq!(
276 ParsedReference::parse_reference_formula(sheet_index, "=A1", locale, |_| Some(1)),
277 Err("Invalid reference. Expected only one token.".to_string()),
278 );
279 assert_eq!(
280 ParsedReference::parse_reference_formula(sheet_index, "=$A$1", locale, |_| { Some(1) }),
281 Err("Invalid reference. Expected only one token.".to_string()),
282 );
283 assert_eq!(
284 ParsedReference::parse_reference_formula(None, "=Sheet1!A1", locale, |_| Some(1)),
285 Err("Invalid reference. Expected only one token.".to_string()),
286 );
287 }
288
289 #[test]
290 fn test_error_reject_formulas_without_equal_sign() {
291 let locale = get_test_locale();
292 assert_eq!(
293 ParsedReference::parse_reference_formula(None, "SUM", locale, |_| Some(1)),
294 Err("Invalid reference. First token is not a reference.".to_string()),
295 );
296 assert_eq!(
297 ParsedReference::parse_reference_formula(None, "SUM(A1:A2)", locale, |_| Some(1)),
298 Err("Invalid reference. Expected only one token.".to_string()),
299 );
300 }
301
302 #[test]
303 fn test_error_reject_without_sheet_and_relative_cell() {
304 let locale = get_test_locale();
305 assert_eq!(
306 ParsedReference::parse_reference_formula(None, "A1", locale, |_| Some(1)),
307 Err("Reference doesn't contain sheet name and relative cell is not known.".to_string()),
308 );
309 assert_eq!(
310 ParsedReference::parse_reference_formula(None, "A1:A2", locale, |_| Some(1)),
311 Err("Reference doesn't contain sheet name and relative cell is not known.".to_string()),
312 );
313 }
314
315 #[test]
316 fn test_error_unrecognized_sheet_name() {
317 let locale = get_test_locale();
318 assert_eq!(
319 ParsedReference::parse_reference_formula(None, "SheetName!A1", locale, |_| None),
320 Err("Invalid reference. Sheet \"SheetName\" could not be found.".to_string()),
321 );
322 assert_eq!(
323 ParsedReference::parse_reference_formula(None, "SheetName2!A1:A4", locale, |_| None),
324 Err("Invalid reference. Sheet \"SheetName2\" could not be found.".to_string()),
325 );
326 }
327
328 #[test]
329 fn test_value_needs_quoting() {
330 let en_language = get_language("en").expect("en language expected");
331
332 assert!(!value_needs_quoting("", en_language));
333 assert!(!value_needs_quoting("hello", en_language));
334
335 assert!(value_needs_quoting("12", en_language));
336 assert!(value_needs_quoting("true", en_language));
337 assert!(value_needs_quoting("False", en_language));
338
339 assert!(value_needs_quoting("=A1", en_language));
340
341 assert!(value_needs_quoting("#REF!", en_language));
342 assert!(value_needs_quoting("#NAME?", en_language));
343 }
344}