1use std::collections::HashMap;
6
7use quick_xml::de::from_reader as xml_from_reader;
8use quick_xml::se::to_string as xml_to_string;
9use serde::{Deserialize, Serialize};
10
11use crate::File;
12use crate::constants::{
13 EXT_URI_DATA_VALIDATIONS, MAX_FIELD_LENGTH, NAMESPACE_SPREADSHEET_EXCEL_2006_MAIN,
14 NAMESPACE_SPREADSHEET_X14,
15};
16use crate::errors::{
17 ErrDataValidationFormulaLength, ErrDataValidationRange, ErrParameterInvalid, ErrSheetNotExist,
18 Result,
19};
20use crate::lib_util::{
21 coordinates_to_cell_name, coordinates_to_range_ref, count_utf16_string, flat_sqref,
22 in_coordinates,
23};
24use crate::xml::common::XlsxInnerXml;
25use crate::xml::worksheet::{
26 DataValidation, XlsxDataValidation, XlsxDataValidations, XlsxWorksheet, XlsxX14DataValidation,
27 XlsxX14DataValidations,
28};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub struct DataValidationType(pub u8);
33
34impl DataValidationType {
35 pub const NONE: DataValidationType = DataValidationType(0);
36 pub const CUSTOM: DataValidationType = DataValidationType(1);
37 pub const DATE: DataValidationType = DataValidationType(2);
38 pub const DECIMAL: DataValidationType = DataValidationType(3);
39 pub const LIST: DataValidationType = DataValidationType(4);
40 pub const TEXT_LENGTH: DataValidationType = DataValidationType(5);
41 pub const TIME: DataValidationType = DataValidationType(6);
42 pub const WHOLE: DataValidationType = DataValidationType(7);
43}
44
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
47pub struct DataValidationErrorStyle(pub u8);
48
49impl DataValidationErrorStyle {
50 pub const STOP: DataValidationErrorStyle = DataValidationErrorStyle(1);
51 pub const WARNING: DataValidationErrorStyle = DataValidationErrorStyle(2);
52 pub const INFORMATION: DataValidationErrorStyle = DataValidationErrorStyle(3);
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub struct DataValidationOperator(pub u8);
58
59impl DataValidationOperator {
60 pub const BETWEEN: DataValidationOperator = DataValidationOperator(1);
61 pub const EQUAL: DataValidationOperator = DataValidationOperator(2);
62 pub const GREATER_THAN: DataValidationOperator = DataValidationOperator(3);
63 pub const GREATER_THAN_OR_EQUAL: DataValidationOperator = DataValidationOperator(4);
64 pub const LESS_THAN: DataValidationOperator = DataValidationOperator(5);
65 pub const LESS_THAN_OR_EQUAL: DataValidationOperator = DataValidationOperator(6);
66 pub const NOT_BETWEEN: DataValidationOperator = DataValidationOperator(7);
67 pub const NOT_EQUAL: DataValidationOperator = DataValidationOperator(8);
68}
69
70#[derive(Debug, Clone, PartialEq)]
72pub enum DataValidationValue {
73 None,
75 Integer(i64),
77 Float(f64),
79 Text(String),
81}
82
83impl From<i64> for DataValidationValue {
84 fn from(v: i64) -> Self {
85 DataValidationValue::Integer(v)
86 }
87}
88
89impl From<f64> for DataValidationValue {
90 fn from(v: f64) -> Self {
91 DataValidationValue::Float(v)
92 }
93}
94
95impl From<&str> for DataValidationValue {
96 fn from(v: &str) -> Self {
97 DataValidationValue::Text(v.to_string())
98 }
99}
100
101impl From<String> for DataValidationValue {
102 fn from(v: String) -> Self {
103 DataValidationValue::Text(v)
104 }
105}
106
107fn dv_type_str(t: DataValidationType) -> &'static str {
108 match t {
109 DataValidationType::NONE => "none",
110 DataValidationType::CUSTOM => "custom",
111 DataValidationType::DATE => "date",
112 DataValidationType::DECIMAL => "decimal",
113 DataValidationType::LIST => "list",
114 DataValidationType::TEXT_LENGTH => "textLength",
115 DataValidationType::TIME => "time",
116 DataValidationType::WHOLE => "whole",
117 _ => "",
118 }
119}
120
121fn dv_error_style_str(s: DataValidationErrorStyle) -> &'static str {
122 match s {
123 DataValidationErrorStyle::WARNING => "warning",
124 DataValidationErrorStyle::INFORMATION => "information",
125 _ => "stop",
126 }
127}
128
129fn dv_operator_str(o: DataValidationOperator) -> &'static str {
130 match o {
131 DataValidationOperator::BETWEEN => "between",
132 DataValidationOperator::EQUAL => "equal",
133 DataValidationOperator::GREATER_THAN => "greaterThan",
134 DataValidationOperator::GREATER_THAN_OR_EQUAL => "greaterThanOrEqual",
135 DataValidationOperator::LESS_THAN => "lessThan",
136 DataValidationOperator::LESS_THAN_OR_EQUAL => "lessThanOrEqual",
137 DataValidationOperator::NOT_BETWEEN => "notBetween",
138 DataValidationOperator::NOT_EQUAL => "notEqual",
139 _ => "",
140 }
141}
142
143fn escape_formula(s: &str) -> String {
144 s.replace('&', "&")
145 .replace('<', "<")
146 .replace('>', ">")
147}
148
149fn unescape_formula(s: &str) -> String {
150 s.replace("&", "&")
151 .replace("<", "<")
152 .replace(">", ">")
153}
154
155fn unescape_data_validation_formula(val: &str) -> String {
156 let s = unescape_formula(val);
157 if s.starts_with('"') {
158 s.replace("\"\"", "\"")
159 } else {
160 s
161 }
162}
163
164pub fn new_data_validation(allow_blank: bool) -> DataValidation {
166 DataValidation {
167 allow_blank,
168 ..Default::default()
169 }
170}
171
172impl DataValidation {
173 pub fn set_error(&mut self, style: DataValidationErrorStyle, title: &str, msg: &str) {
175 self.error = Some(msg.to_string());
176 self.error_title = Some(title.to_string());
177 self.show_error_message = true;
178 self.error_style = Some(dv_error_style_str(style).to_string());
179 }
180
181 pub fn set_input(&mut self, title: &str, msg: &str) {
183 self.show_input_message = true;
184 self.prompt_title = Some(title.to_string());
185 self.prompt = Some(msg.to_string());
186 }
187
188 pub fn set_drop_list(&mut self, keys: &[&str]) -> Result<()> {
190 let formula = keys.join(",");
191 if MAX_FIELD_LENGTH < count_utf16_string(&formula) {
192 return Err(Box::new(ErrDataValidationFormulaLength));
193 }
194 self.r#type = dv_type_str(DataValidationType::LIST).to_string();
195 if formula.starts_with('=') {
196 self.formula1 = escape_formula(&formula);
197 } else {
198 let escaped = escape_formula(&formula).replace('"', "\"\"");
199 self.formula1 = format!("\"{escaped}\"");
200 }
201 Ok(())
202 }
203
204 pub fn set_range(
206 &mut self,
207 f1: DataValidationValue,
208 f2: DataValidationValue,
209 t: DataValidationType,
210 o: DataValidationOperator,
211 ) -> Result<()> {
212 fn gen_formula(val: &DataValidationValue) -> Result<String> {
213 match val {
214 DataValidationValue::Integer(v) => Ok(format!("{}", v)),
215 DataValidationValue::Float(v) => {
216 if v.abs() > f32::MAX as f64 {
217 return Err(Box::new(ErrDataValidationRange));
218 }
219 Ok(format!("{}", v))
220 }
221 DataValidationValue::Text(s) => Ok(s.clone()),
222 DataValidationValue::None => Err(Box::new(ErrParameterInvalid)),
223 }
224 }
225 self.formula1 = gen_formula(&f1)?;
226 self.formula2 = gen_formula(&f2)?;
227 self.r#type = dv_type_str(t).to_string();
228 self.operator = dv_operator_str(o).to_string();
229 Ok(())
230 }
231
232 pub fn set_sqref_drop_list(&mut self, sqref: &str) {
234 self.formula1 = sqref.to_string();
235 self.r#type = dv_type_str(DataValidationType::LIST).to_string();
236 }
237
238 pub fn set_sqref(&mut self, sqref: &str) {
240 if self.sqref.is_empty() {
241 self.sqref = sqref.to_string();
242 } else {
243 self.sqref = format!("{} {}", self.sqref, sqref);
244 }
245 }
246}
247
248impl File {
249 pub fn add_data_validation(&self, sheet: &str, dv: &DataValidation) -> Result<()> {
251 let path = self
252 .get_sheet_xml_path(sheet)
253 .ok_or_else(|| ErrSheetNotExist {
254 sheet_name: sheet.to_string(),
255 })?;
256 let mut ws = self.work_sheet_reader(sheet)?;
257 if ws.data_validations.is_none() {
258 ws.data_validations = Some(XlsxDataValidations::default());
259 }
260 let dvs = ws.data_validations.as_mut().unwrap();
261 let xdv = XlsxDataValidation {
262 allow_blank: Some(dv.allow_blank),
263 error: dv.error.clone(),
264 error_style: dv.error_style.clone(),
265 error_title: dv.error_title.clone(),
266 operator: Some(dv.operator.clone()).filter(|s| !s.is_empty()),
267 prompt: dv.prompt.clone(),
268 prompt_title: dv.prompt_title.clone(),
269 show_drop_down: Some(dv.show_drop_down),
270 show_error_message: Some(dv.show_error_message),
271 show_input_message: Some(dv.show_input_message),
272 sqref: dv.sqref.clone(),
273 r#type: Some(dv.r#type.clone()).filter(|s| !s.is_empty()),
274 formula1: if dv.formula1.is_empty() {
275 None
276 } else {
277 Some(XlsxInnerXml {
278 content: dv.formula1.clone(),
279 })
280 },
281 formula2: if dv.formula2.is_empty() {
282 None
283 } else {
284 Some(XlsxInnerXml {
285 content: dv.formula2.clone(),
286 })
287 },
288 xm_sqref: None,
289 };
290 dvs.data_validation.push(xdv);
291 dvs.count = Some(dvs.data_validation.len() as i64);
292 self.sheet.insert(path, ws);
293 Ok(())
294 }
295
296 pub fn get_data_validations(&self, sheet: &str) -> Result<Vec<DataValidation>> {
298 let ws = self.work_sheet_reader(sheet)?;
299 let mut out = Vec::new();
300 if let Some(dvs) = &ws.data_validations {
301 out.extend(get_data_validations(dvs));
302 }
303 if let Some(ext_lst) = &ws.ext_lst {
304 for ext in &ext_lst.ext {
305 if ext.uri.as_deref() != Some(EXT_URI_DATA_VALIDATIONS) {
306 continue;
307 }
308 let dvs = parse_x14_data_validations(&ext.content)?;
309 for dv in &dvs.data_validation {
310 out.push(x14_dv_to_public(dv));
311 }
312 }
313 }
314 Ok(out)
315 }
316
317 pub fn delete_data_validation(&self, sheet: &str, sqref: &[&str]) -> Result<()> {
319 let path = self
320 .get_sheet_xml_path(sheet)
321 .ok_or_else(|| ErrSheetNotExist {
322 sheet_name: sheet.to_string(),
323 })?;
324 let mut ws = self.work_sheet_reader(sheet)?;
325 if ws.data_validations.is_none() && ws.ext_lst.is_none() {
326 return Ok(());
327 }
328 if sqref.is_empty() {
329 ws.data_validations = None;
330 self.sheet.insert(path, ws);
331 return Ok(());
332 }
333 let del_cells = flat_sqref(&sqref.join(" "))?;
334 if let Some(dvs) = ws.data_validations.as_mut() {
335 delete_data_validation(dvs, &del_cells)?;
336 if dvs.data_validation.is_empty() {
337 ws.data_validations = None;
338 }
339 }
340 if ws.ext_lst.is_some() {
341 self.delete_x14_data_validation(&mut ws, &del_cells)?;
342 }
343 self.sheet.insert(path, ws);
344 Ok(())
345 }
346}
347
348fn xlsx_dv_to_public(dv: &XlsxDataValidation) -> DataValidation {
349 let mut formula1 = dv
350 .formula1
351 .as_ref()
352 .map(|f| unescape_data_validation_formula(&f.content))
353 .unwrap_or_default();
354 let mut formula2 = dv
355 .formula2
356 .as_ref()
357 .map(|f| unescape_data_validation_formula(&f.content))
358 .unwrap_or_default();
359 let mut sqref = dv.sqref.clone();
360 if let Some(xm) = &dv.xm_sqref {
361 sqref = xm.clone();
362 formula1 = formula1
363 .trim_start_matches("<xm:f>")
364 .trim_end_matches("</xm:f>")
365 .to_string();
366 formula2 = formula2
367 .trim_start_matches("<xm:f>")
368 .trim_end_matches("</xm:f>")
369 .to_string();
370 }
371 DataValidation {
372 allow_blank: dv.allow_blank.unwrap_or(false),
373 error: dv.error.clone(),
374 error_style: dv.error_style.clone(),
375 error_title: dv.error_title.clone(),
376 operator: dv.operator.clone().unwrap_or_default(),
377 prompt: dv.prompt.clone(),
378 prompt_title: dv.prompt_title.clone(),
379 show_drop_down: dv.show_drop_down.unwrap_or(false),
380 show_error_message: dv.show_error_message.unwrap_or(false),
381 show_input_message: dv.show_input_message.unwrap_or(false),
382 sqref,
383 r#type: dv.r#type.clone().unwrap_or_default(),
384 formula1,
385 formula2,
386 }
387}
388
389fn get_data_validations(dvs: &XlsxDataValidations) -> Vec<DataValidation> {
390 dvs.data_validation.iter().map(xlsx_dv_to_public).collect()
391}
392
393fn delete_data_validation(
394 dvs: &mut XlsxDataValidations,
395 del_cells: &HashMap<i32, Vec<Vec<i32>>>,
396) -> Result<()> {
397 let mut i = 0;
398 while i < dvs.data_validation.len() {
399 let new_sqref = delete_cells_from_sqref(&dvs.data_validation[i].sqref, del_cells)?;
400 dvs.data_validation[i].sqref = new_sqref;
401 if dvs.data_validation[i].sqref.is_empty() {
402 dvs.data_validation.remove(i);
403 } else {
404 i += 1;
405 }
406 }
407 dvs.count = Some(dvs.data_validation.len() as i64);
408 Ok(())
409}
410
411impl File {
412 fn delete_x14_data_validation(
413 &self,
414 ws: &mut XlsxWorksheet,
415 del_cells: &HashMap<i32, Vec<Vec<i32>>>,
416 ) -> Result<()> {
417 let mut x14 = crate::xml::common::XlsxExtLst::default();
418 if let Some(ext_lst) = ws.ext_lst.take() {
419 for ext in ext_lst.ext {
420 if ext.uri.as_deref() != Some(EXT_URI_DATA_VALIDATIONS) {
421 x14.ext.push(ext);
422 continue;
423 }
424 let dvs = parse_x14_data_validations(&ext.content)?;
425 let mut new_dvs = XlsxX14DataValidations {
426 xmlns_xm: Some(NAMESPACE_SPREADSHEET_EXCEL_2006_MAIN.to_string()),
427 count: None,
428 disable_prompts: dvs.disable_prompts,
429 x_window: dvs.x_window,
430 y_window: dvs.y_window,
431 data_validation: Vec::new(),
432 };
433 for dv in &dvs.data_validation {
434 let sqref_to_delete = dv.xm_sqref.as_deref().unwrap_or(&dv.sqref);
435 let new_sqref = delete_cells_from_sqref(sqref_to_delete, del_cells)?;
436 if new_sqref.is_empty() {
437 continue;
438 }
439 new_dvs.data_validation.push(build_x14_dv(dv, &new_sqref));
440 }
441 new_dvs.count = Some(new_dvs.data_validation.len() as i64);
442 if new_dvs.data_validation.is_empty() {
443 continue;
444 }
445 let content = xml_to_string(&new_dvs)?;
446 x14.ext.push(crate::xml::common::XlsxExt {
447 uri: Some(EXT_URI_DATA_VALIDATIONS.to_string()),
448 xmlns_x14: Some(NAMESPACE_SPREADSHEET_X14.to_string()),
449 xmlns_xm: None,
450 content,
451 });
452 }
453 }
454 if x14.ext.is_empty() {
455 ws.ext_lst = None;
456 } else {
457 ws.ext_lst = Some(x14);
458 }
459 Ok(())
460 }
461}
462
463pub(crate) fn squash_sqref(cells: &[Vec<i32>]) -> Vec<String> {
464 if cells.is_empty() {
465 return Vec::new();
466 }
467 if cells.len() == 1 {
468 if let Ok(cell) = coordinates_to_cell_name(cells[0][0], cells[0][1], false) {
469 return vec![cell];
470 }
471 return Vec::new();
472 }
473 let mut refs = Vec::new();
474 let mut l = 0usize;
475 let mut r = 0usize;
476 for i in 1..cells.len() {
477 if cells[i][0] == cells[r][0] && cells[i][1] - cells[r][1] > 1 {
478 let mut coords = cells[l].clone();
479 coords.extend_from_slice(&cells[r]);
480 let ref_str = if l == r {
481 coordinates_to_cell_name(cells[l][0], cells[l][1], false).unwrap_or_default()
482 } else {
483 coordinates_to_range_ref(&coords, false).unwrap_or_default()
484 };
485 if !ref_str.is_empty() {
486 refs.push(ref_str);
487 }
488 l = i;
489 r = i;
490 } else {
491 r += 1;
492 }
493 }
494 let mut coords = cells[l].clone();
495 coords.extend_from_slice(&cells[r]);
496 let ref_str = if l == r {
497 coordinates_to_cell_name(cells[l][0], cells[l][1], false).unwrap_or_default()
498 } else {
499 coordinates_to_range_ref(&coords, false).unwrap_or_default()
500 };
501 if !ref_str.is_empty() {
502 refs.push(ref_str);
503 }
504 refs
505}
506
507pub(crate) fn delete_cells_from_sqref(
508 sqref: &str,
509 del_cells: &HashMap<i32, Vec<Vec<i32>>>,
510) -> Result<String> {
511 let mut col_cells = flat_sqref(sqref)?;
512 for (col, cells) in del_cells {
513 for cell in cells {
514 if let Some(col_vec) = col_cells.get_mut(col) {
515 let idx = in_coordinates(col_vec, cell);
516 if idx != -1 {
517 col_vec.remove(idx as usize);
518 }
519 }
520 }
521 }
522 let mut apply_sqref = Vec::new();
523 for col in col_cells.values() {
524 apply_sqref.extend(squash_sqref(col));
525 }
526 Ok(apply_sqref.join(" "))
527}
528
529#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
534#[serde(rename = "dataValidations")]
535struct DecodeX14DataValidations {
536 #[serde(rename = "@count", default)]
537 count: Option<i64>,
538 #[serde(rename = "@disablePrompts", default)]
539 disable_prompts: Option<bool>,
540 #[serde(rename = "@xWindow", default)]
541 x_window: Option<i64>,
542 #[serde(rename = "@yWindow", default)]
543 y_window: Option<i64>,
544 #[serde(rename = "dataValidation", default)]
545 data_validation: Vec<DecodeX14DataValidation>,
546}
547
548#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
549#[serde(rename = "dataValidation")]
550struct DecodeX14DataValidation {
551 #[serde(rename = "@allowBlank", default)]
552 allow_blank: Option<bool>,
553 #[serde(rename = "@error", default)]
554 error: Option<String>,
555 #[serde(rename = "@errorStyle", default)]
556 error_style: Option<String>,
557 #[serde(rename = "@errorTitle", default)]
558 error_title: Option<String>,
559 #[serde(rename = "@operator", default)]
560 operator: Option<String>,
561 #[serde(rename = "@prompt", default)]
562 prompt: Option<String>,
563 #[serde(rename = "@promptTitle", default)]
564 prompt_title: Option<String>,
565 #[serde(rename = "@showDropDown", default)]
566 show_drop_down: Option<bool>,
567 #[serde(rename = "@showErrorMessage", default)]
568 show_error_message: Option<bool>,
569 #[serde(rename = "@showInputMessage", default)]
570 show_input_message: Option<bool>,
571 #[serde(rename = "@sqref", default)]
572 sqref: String,
573 #[serde(rename = "@type", default)]
574 r#type: Option<String>,
575 #[serde(rename = "formula1", default)]
576 formula1: Option<DecodeX14Formula>,
577 #[serde(rename = "formula2", default)]
578 formula2: Option<DecodeX14Formula>,
579 #[serde(rename = "sqref", default)]
580 xm_sqref: Option<String>,
581}
582
583#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
584struct DecodeX14Formula {
585 #[serde(rename = "f", default)]
586 f: Option<String>,
587}
588
589fn parse_x14_data_validations(content: &str) -> Result<DecodeX14DataValidations> {
590 let stripped = content.replace("<x14:", "<").replace("</x14:", "</");
591 Ok(xml_from_reader(stripped.as_bytes())?)
592}
593
594fn x14_dv_to_public(dv: &DecodeX14DataValidation) -> DataValidation {
595 DataValidation {
596 allow_blank: dv.allow_blank.unwrap_or(false),
597 error: dv.error.clone(),
598 error_style: dv.error_style.clone(),
599 error_title: dv.error_title.clone(),
600 operator: dv.operator.clone().unwrap_or_default(),
601 prompt: dv.prompt.clone(),
602 prompt_title: dv.prompt_title.clone(),
603 show_drop_down: dv.show_drop_down.unwrap_or(false),
604 show_error_message: dv.show_error_message.unwrap_or(false),
605 show_input_message: dv.show_input_message.unwrap_or(false),
606 sqref: dv.xm_sqref.clone().unwrap_or_else(|| dv.sqref.clone()),
607 r#type: dv.r#type.clone().unwrap_or_default(),
608 formula1: dv
609 .formula1
610 .as_ref()
611 .and_then(|f| f.f.as_ref())
612 .map(|s| unescape_data_validation_formula(s))
613 .unwrap_or_default(),
614 formula2: dv
615 .formula2
616 .as_ref()
617 .and_then(|f| f.f.as_ref())
618 .map(|s| unescape_data_validation_formula(s))
619 .unwrap_or_default(),
620 }
621}
622
623fn build_x14_dv(dv: &DecodeX14DataValidation, new_sqref: &str) -> XlsxX14DataValidation {
624 XlsxX14DataValidation {
625 allow_blank: dv.allow_blank,
626 error: dv.error.clone(),
627 error_style: dv.error_style.clone(),
628 error_title: dv.error_title.clone(),
629 operator: dv.operator.clone(),
630 prompt: dv.prompt.clone(),
631 prompt_title: dv.prompt_title.clone(),
632 show_drop_down: dv.show_drop_down,
633 show_error_message: dv.show_error_message,
634 show_input_message: dv.show_input_message,
635 sqref: dv.sqref.clone(),
636 r#type: dv.r#type.clone(),
637 formula1: dv
638 .formula1
639 .as_ref()
640 .and_then(|f| f.f.as_ref())
641 .map(|formula| XlsxInnerXml {
642 content: format!("<xm:f>{formula}</xm:f>"),
643 }),
644 formula2: dv
645 .formula2
646 .as_ref()
647 .and_then(|f| f.f.as_ref())
648 .map(|formula| XlsxInnerXml {
649 content: format!("<xm:f>{formula}</xm:f>"),
650 }),
651 xm_sqref: Some(new_sqref.to_string()),
652 }
653}
654
655#[cfg(test)]
660mod tests {
661 use super::*;
662 use crate::xml::common::XlsxExt;
663
664 #[test]
665 fn test_new_data_validation() {
666 let dv = new_data_validation(true);
667 assert!(dv.allow_blank);
668 assert!(!dv.show_error_message);
669 assert!(!dv.show_input_message);
670 }
671
672 #[test]
673 fn test_set_error_styles() {
674 let mut dv = new_data_validation(true);
675 dv.set_error(DataValidationErrorStyle::STOP, "title", "msg");
676 assert_eq!(dv.error_style, Some("stop".to_string()));
677 dv.set_error(DataValidationErrorStyle::WARNING, "title", "msg");
678 assert_eq!(dv.error_style, Some("warning".to_string()));
679 dv.set_error(DataValidationErrorStyle::INFORMATION, "title", "msg");
680 assert_eq!(dv.error_style, Some("information".to_string()));
681 }
682
683 #[test]
684 fn test_set_drop_list() {
685 let mut dv = new_data_validation(true);
686 dv.set_drop_list(&["1", "2", "3"]).unwrap();
687 assert_eq!(dv.formula1, "\"1,2,3\"");
688
689 dv.set_drop_list(&["=A1"]).unwrap();
690 assert_eq!(dv.formula1, "=A1");
691
692 dv.set_drop_list(&["A<", "B>", "C\"", "D\t", "E'", "F"])
693 .unwrap();
694 assert_eq!(dv.formula1, "\"A<,B>,C\"\",D\t,E',F\"");
695
696 let long = vec!["s"; 256];
697 assert!(
698 dv.set_drop_list(&long.iter().map(|s| *s).collect::<Vec<_>>())
699 .is_err()
700 );
701 }
702
703 #[test]
704 fn test_set_range() {
705 let mut dv = new_data_validation(true);
706 dv.set_range(
707 10.into(),
708 20.into(),
709 DataValidationType::WHOLE,
710 DataValidationOperator::BETWEEN,
711 )
712 .unwrap();
713 assert_eq!(dv.formula1, "10");
714 assert_eq!(dv.formula2, "20");
715 assert_eq!(dv.r#type, "whole");
716 assert_eq!(dv.operator, "between");
717
718 dv.set_range(
719 (-f32::MAX as f64).into(),
720 (f32::MAX as f64).into(),
721 DataValidationType::WHOLE,
722 DataValidationOperator::GREATER_THAN,
723 )
724 .unwrap();
725
726 assert!(
727 dv.set_range(
728 (-f64::MAX).into(),
729 20.into(),
730 DataValidationType::WHOLE,
731 DataValidationOperator::GREATER_THAN,
732 )
733 .is_err()
734 );
735 assert!(
736 dv.set_range(
737 DataValidationValue::None,
738 20.into(),
739 DataValidationType::WHOLE,
740 DataValidationOperator::BETWEEN,
741 )
742 .is_err()
743 );
744 }
745
746 #[test]
747 fn test_set_sqref() {
748 let mut dv = new_data_validation(true);
749 dv.set_sqref("A1:B2");
750 assert_eq!(dv.sqref, "A1:B2");
751 dv.set_sqref("A1:B2");
752 assert_eq!(dv.sqref, "A1:B2 A1:B2");
753 }
754
755 #[test]
756 fn test_add_get_delete_data_validation() {
757 let f = File::new_with_options(crate::options::Options::default());
758
759 let mut dv = new_data_validation(true);
760 dv.sqref = "A1:B2".to_string();
761 dv.set_range(
762 10.into(),
763 20.into(),
764 DataValidationType::WHOLE,
765 DataValidationOperator::BETWEEN,
766 )
767 .unwrap();
768 dv.set_error(DataValidationErrorStyle::STOP, "error title", "error body");
769 f.add_data_validation("Sheet1", &dv).unwrap();
770
771 let dvs = f.get_data_validations("Sheet1").unwrap();
772 assert_eq!(dvs.len(), 1);
773 assert_eq!(dvs[0].sqref, "A1:B2");
774 assert_eq!(dvs[0].r#type, "whole");
775
776 f.delete_data_validation("Sheet1", &["A1:B2"]).unwrap();
777 let dvs = f.get_data_validations("Sheet1").unwrap();
778 assert!(dvs.is_empty());
779 }
780
781 #[test]
782 fn test_delete_partial_sqref() {
783 let f = File::new_with_options(crate::options::Options::default());
784
785 let mut dv = new_data_validation(true);
786 dv.sqref = "C2:C5".to_string();
787 dv.set_range(
788 10.into(),
789 20.into(),
790 DataValidationType::WHOLE,
791 DataValidationOperator::BETWEEN,
792 )
793 .unwrap();
794 f.add_data_validation("Sheet1", &dv).unwrap();
795
796 f.delete_data_validation("Sheet1", &["C4"]).unwrap();
797 let dvs = f.get_data_validations("Sheet1").unwrap();
798 assert_eq!(dvs.len(), 1);
799 assert_eq!(dvs[0].sqref, "C2:C3 C5");
800 }
801
802 #[test]
803 fn test_delete_unordered_sqref() {
804 let f = File::new_with_options(crate::options::Options::default());
805 let path = f.get_sheet_xml_path("Sheet1").unwrap();
806 let mut ws = f.work_sheet_reader("Sheet1").unwrap();
807 ws.data_validations = Some(XlsxDataValidations {
808 count: Some(1),
809 data_validation: vec![XlsxDataValidation {
810 allow_blank: Some(true),
811 show_input_message: Some(true),
812 show_error_message: Some(true),
813 r#type: Some("whole".to_string()),
814 operator: Some("between".to_string()),
815 sqref: "A5:A10 A15:A20 A3:A4".to_string(),
816 formula1: Some(XlsxInnerXml {
817 content: "1".to_string(),
818 }),
819 formula2: Some(XlsxInnerXml {
820 content: "100".to_string(),
821 }),
822 ..Default::default()
823 }],
824 ..Default::default()
825 });
826 f.sheet.insert(path, ws);
827
828 f.delete_data_validation("Sheet1", &["A7"]).unwrap();
829 let dvs = f.get_data_validations("Sheet1").unwrap();
830 assert_eq!(dvs.len(), 1);
831 assert_eq!(dvs[0].sqref, "A3:A6 A8:A10 A15:A20");
832 }
833
834 #[test]
835 fn test_get_data_validations_ext_lst() {
836 let f = File::new_with_options(crate::options::Options::default());
837 let path = f.get_sheet_xml_path("Sheet1").unwrap();
838 let mut ws = f.work_sheet_reader("Sheet1").unwrap();
839 ws.ext_lst = Some(crate::xml::common::XlsxExtLst {
840 ext: vec![XlsxExt {
841 uri: Some(EXT_URI_DATA_VALIDATIONS.to_string()),
842 xmlns_x14: Some(NAMESPACE_SPREADSHEET_X14.to_string()),
843 xmlns_xm: Some(NAMESPACE_SPREADSHEET_EXCEL_2006_MAIN.to_string()),
844 content: r#"<x14:dataValidations xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"><x14:dataValidation type="list" allowBlank="1"><x14:formula1><xm:f>Sheet1!$B$1:$B$5</xm:f></x14:formula1><xm:sqref>A7:B8</xm:sqref></x14:dataValidation></x14:dataValidations>"#.to_string(),
845 }],
846 });
847 f.sheet.insert(path, ws);
848
849 let dvs = f.get_data_validations("Sheet1").unwrap();
850 assert_eq!(dvs.len(), 1);
851 assert_eq!(dvs[0].allow_blank, true);
852 assert_eq!(dvs[0].r#type, "list");
853 assert_eq!(dvs[0].formula1, "Sheet1!$B$1:$B$5");
854 assert_eq!(dvs[0].sqref, "A7:B8");
855 }
856
857 #[test]
858 fn test_delete_data_validation_ext_lst() {
859 let f = File::new_with_options(crate::options::Options::default());
860 let path = f.get_sheet_xml_path("Sheet1").unwrap();
861 let mut ws = f.work_sheet_reader("Sheet1").unwrap();
862 ws.ext_lst = Some(crate::xml::common::XlsxExtLst {
863 ext: vec![XlsxExt {
864 uri: Some(EXT_URI_DATA_VALIDATIONS.to_string()),
865 xmlns_x14: Some(NAMESPACE_SPREADSHEET_X14.to_string()),
866 xmlns_xm: Some(NAMESPACE_SPREADSHEET_EXCEL_2006_MAIN.to_string()),
867 content: format!(
868 r#"<x14:dataValidations xmlns:xm="{}"><x14:dataValidation allowBlank="true" showErrorMessage="true" showInputMessage="true" sqref="" type="list"><xm:sqref>A1:A2</xm:sqref><x14:formula1><xm:f>Sheet1!$A$2:$A$4</xm:f></x14:formula1></x14:dataValidation><x14:dataValidation allowBlank="true" showErrorMessage="true" showInputMessage="true" sqref="" type="list"><xm:sqref>B1:B2</xm:sqref><x14:formula1><xm:f>Sheet1!$B$2:$B$3</xm:f></x14:formula1></x14:dataValidation></x14:dataValidations>"#,
869 NAMESPACE_SPREADSHEET_EXCEL_2006_MAIN
870 ),
871 }],
872 });
873 f.sheet.insert(path, ws);
874
875 f.delete_data_validation("Sheet1", &["A1:A2"]).unwrap();
876 let dvs = f.get_data_validations("Sheet1").unwrap();
877 assert_eq!(dvs.len(), 1);
878 assert_eq!(dvs[0].sqref, "B1:B2");
879
880 f.delete_data_validation("Sheet1", &["B1:B2"]).unwrap();
881 let dvs = f.get_data_validations("Sheet1").unwrap();
882 assert!(dvs.is_empty());
883 }
884
885 #[test]
886 fn test_squash_sqref() {
887 let cells = vec![vec![1, 1], vec![1, 2], vec![1, 4], vec![1, 5], vec![1, 6]];
888 let refs = squash_sqref(&cells);
889 assert_eq!(refs, vec!["A1:A2", "A4:A6"]);
890 }
891
892 #[test]
893 fn test_data_validation_round_trip() {
894 use std::fs;
895
896 let tmp = std::env::temp_dir().join("excelize_rust_data_validation.xlsx");
897 let _ = fs::remove_file(&tmp);
898
899 let mut f = File::new_with_options(crate::options::Options::default());
900 let mut dv = new_data_validation(true);
901 dv.sqref = "A1:B2".to_string();
902 dv.set_range(
903 10.into(),
904 20.into(),
905 DataValidationType::WHOLE,
906 DataValidationOperator::BETWEEN,
907 )
908 .unwrap();
909 dv.set_error(DataValidationErrorStyle::STOP, "error title", "error body");
910 f.add_data_validation("Sheet1", &dv).unwrap();
911
912 let mut dv2 = new_data_validation(true);
913 dv2.sqref = "A3:B4".to_string();
914 dv2.set_drop_list(&["1", "2", "3"]).unwrap();
915 dv2.set_input("input title", "input body");
916 f.add_data_validation("Sheet1", &dv2).unwrap();
917
918 f.save_as(tmp.to_str().unwrap()).unwrap();
919
920 let f2 =
921 File::open_file(tmp.to_str().unwrap(), crate::options::Options::default()).unwrap();
922 let dvs = f2.get_data_validations("Sheet1").unwrap();
923 assert_eq!(dvs.len(), 2);
924 assert_eq!(dvs[0].sqref, "A1:B2");
925 assert_eq!(dvs[1].sqref, "A3:B4");
926
927 let _ = fs::remove_file(&tmp);
928 }
929}