Merge pull request #636 from MarekSpodBiedry:issue-519
This commit is contained in:
commit
1fa38e583e
|
|
@ -336,10 +336,46 @@ fn num_data(tag: &str, f: Option<&str>, wb: &Workbook, si: usize) -> String {
|
|||
s
|
||||
}
|
||||
|
||||
/// Splits the inside of an array constant on `,` and `;` outside double quotes; inside quotes `""` is a literal quote.
|
||||
/// Unquoted items are trimmed; quoted ones keep the text between the quotes and drop the spaces around them.
|
||||
fn split_array_items(inner: &str) -> Vec<String> {
|
||||
let mut items = Vec::new();
|
||||
let mut cur = String::new();
|
||||
let mut quoted = false;
|
||||
let mut in_quote = false;
|
||||
let mut chars = inner.chars().peekable();
|
||||
let finish = |cur: &mut String, quoted: &mut bool| {
|
||||
let text = std::mem::take(cur);
|
||||
let item = if *quoted { text } else { text.trim().to_string() };
|
||||
*quoted = false;
|
||||
item
|
||||
};
|
||||
while let Some(c) = chars.next() {
|
||||
match c {
|
||||
'"' if in_quote && chars.peek() == Some(&'"') => {
|
||||
cur.push('"');
|
||||
chars.next();
|
||||
}
|
||||
'"' => {
|
||||
if !quoted && cur.trim().is_empty() {
|
||||
cur.clear();
|
||||
}
|
||||
in_quote = !in_quote;
|
||||
quoted = true;
|
||||
}
|
||||
',' | ';' if !in_quote => items.push(finish(&mut cur, &mut quoted)),
|
||||
c if quoted && !in_quote && c.is_whitespace() => {}
|
||||
_ => cur.push(c),
|
||||
}
|
||||
}
|
||||
items.push(finish(&mut cur, &mut quoted));
|
||||
items
|
||||
}
|
||||
|
||||
fn str_data(tag: &str, f: &str, wb: &Workbook, si: usize) -> String {
|
||||
let f = f.trim();
|
||||
if f.starts_with('{') {
|
||||
let vals: Vec<String> = f.trim_matches(|c| c == '{' || c == '}').split([',', ';']).map(|v| v.trim().trim_matches('"').to_string()).collect();
|
||||
let vals = split_array_items(f.trim_matches(|c| c == '{' || c == '}'));
|
||||
let mut s = format!("<c:{tag}><c:strLit><c:ptCount val=\"{}\"/>", vals.len());
|
||||
for (i, v) in vals.iter().enumerate() {
|
||||
let _ = write!(s, "<c:pt idx=\"{i}\"><c:v>{}</c:v></c:pt>", esc(v));
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
//! Literal chart categories that contain a comma, a semicolon or a quote stay one category on save.
|
||||
|
||||
use gridcraft_model::{Anchor, Chart, ChartKind, LegendPos, Series, Sheet, Workbook};
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::{read_xlsx, write_xlsx};
|
||||
|
||||
#[test]
|
||||
fn literal_category_with_separators_survives_save() {
|
||||
// (typed array constant, what reading the saved file gives back)
|
||||
let cats = [
|
||||
(r#"{"a,b"}"#, r#"{"a,b"}"#),
|
||||
(r#"{"a;b"}"#, r#"{"a;b"}"#),
|
||||
(r#"{"red","blue"}"#, r#"{"red","blue"}"#),
|
||||
(r#"{"say ""hi"", ok"}"#, r#"{"say ""hi"", ok"}"#),
|
||||
(r#"{"a,b","c;d","e"}"#, r#"{"a,b","c;d","e"}"#),
|
||||
(r#"{ "Jan" , "Feb, Mar" ; Apr }"#, r#"{"Jan","Feb, Mar","Apr"}"#),
|
||||
];
|
||||
let mut wb = Workbook::default();
|
||||
let mut s = Sheet::new("Data");
|
||||
let series: Vec<Series> = cats
|
||||
.iter()
|
||||
.map(|(c, _)| Series {
|
||||
name: Some("North, South".into()),
|
||||
categories: Some((*c).into()),
|
||||
values: "{1,2,3}".into(),
|
||||
bubble_sizes: None,
|
||||
color: None,
|
||||
secondary: false,
|
||||
kind: None,
|
||||
smooth: false,
|
||||
trendlines: Vec::new(),
|
||||
})
|
||||
.collect();
|
||||
s.charts.push(Chart {
|
||||
id: 1,
|
||||
kind: ChartKind::ColumnClustered,
|
||||
anchor: Anchor { cell: gridcraft_core::CellRef::new(0, 3), width: 480.0, height: 300.0, ..Anchor::default() },
|
||||
title: None,
|
||||
series,
|
||||
legend: LegendPos::Bottom,
|
||||
data_labels: false,
|
||||
gridlines: true,
|
||||
style: 2,
|
||||
x_title: None,
|
||||
y_title: None,
|
||||
source: None,
|
||||
by_rows: false,
|
||||
pivot: None,
|
||||
});
|
||||
wb.sheets = vec![Arc::new(s)];
|
||||
let bytes = write_xlsx(&wb).unwrap();
|
||||
let (back, _rep) = read_xlsx(&bytes).unwrap();
|
||||
let saved = &back.sheets[0].charts[0].series;
|
||||
for (i, (_, want)) in cats.iter().enumerate() {
|
||||
assert_eq!(saved[i].categories.as_deref(), Some(*want));
|
||||
assert_eq!(saved[i].name.as_deref(), Some("North, South"));
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ mod array_export;
|
|||
mod csv_tests;
|
||||
mod encrypted_tests;
|
||||
mod fixtures;
|
||||
mod literal_category_roundtrip;
|
||||
mod malformed;
|
||||
mod ods_tests;
|
||||
mod passthrough_tests;
|
||||
|
|
|
|||
Loading…
Reference in New Issue