mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-09 22:22:07 +00:00
Variables: Export Data Sets refuses more data sets or variables than Import Data Sets reads
dataset.export checked only the 1 MiB size, so a document with more than 1000 data sets or 256 variables exported a CSV that dataset.import then refused. Export now refuses such a document with a message naming the limit, and the messages have rows in every complete-message catalog.
This commit is contained in:
@@ -740,7 +740,7 @@ pub fn specs() -> Vec<CommandSpec> {
|
||||
"Export Data Sets…",
|
||||
["Window", "Variables"],
|
||||
None,
|
||||
"{path?} write every dataset as CSV (header: name, then variables); without path → {csv, datasets}",
|
||||
"{path?} write every dataset as CSV (header: name, then variables); without path → {csv, datasets}. Refused when the document has more than 1000 data sets or 256 variables, the most dataset.import reads",
|
||||
has_doc,
|
||||
dataset_export
|
||||
),
|
||||
|
||||
@@ -341,12 +341,20 @@ pub(super) fn dataset_import(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
|
||||
/// Export every dataset as CSV (`name` then variables). With `path`, write the file; else return `{csv}`.
|
||||
/// A document with more data sets or variables than [`dataset_import`] reads is refused.
|
||||
pub(super) fn dataset_export(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
const C: &str = "dataset.export";
|
||||
let st = s.doc()?;
|
||||
if st.doc.variables.variables.is_empty() {
|
||||
return Err(bad(C, "define a variable first"));
|
||||
}
|
||||
if st.doc.variables.datasets.len() > MAX_CSV_ROWS {
|
||||
return Err(bad(C, format!("more than {MAX_CSV_ROWS} data sets: Import Data Sets reads at most {MAX_CSV_ROWS}")));
|
||||
}
|
||||
// The name column comes first, so the import's column cap leaves room for this many variables.
|
||||
if st.doc.variables.variables.len() > MAX_CSV_COLS {
|
||||
return Err(bad(C, format!("more than {MAX_CSV_COLS} variables: Import Data Sets reads at most {MAX_CSV_COLS}")));
|
||||
}
|
||||
let csv = datasets_to_csv(&st.doc.variables);
|
||||
if csv.len() > MAX_CSV_BYTES {
|
||||
return Err(bad(C, format!("CSV would be larger than {MAX_CSV_BYTES} bytes")));
|
||||
|
||||
@@ -1325,6 +1325,33 @@ fn datasets_csv_keeps_spaces_in_quoted_cells() {
|
||||
assert_eq!(value("C"), text_value(" quoted "));
|
||||
}
|
||||
|
||||
/// Export refuses a document with more data sets or variables than `dataset.import` reads, so
|
||||
/// every file it writes imports back: 1,000 data sets and 256 variables export and import, and
|
||||
/// one more of either is refused.
|
||||
#[test]
|
||||
fn datasets_export_refuses_more_than_import_reads() {
|
||||
let mut s = session();
|
||||
let header: Vec<String> = (1..=256).map(|i| format!("V{i}")).collect();
|
||||
let rows: String = (1..=1000).map(|i| format!("Set {i},x\n")).collect();
|
||||
s.execute("dataset.import", &json!({"csv": format!("name,{}\n{rows}", header.join(","))})).unwrap();
|
||||
let csv = s.execute("dataset.export", &json!({})).unwrap()["csv"].as_str().unwrap().to_string();
|
||||
s.execute("dataset.import", &json!({"csv": csv})).unwrap();
|
||||
|
||||
s.execute("dataset.new", &json!({"name": "Set 1001"})).unwrap();
|
||||
let err = s.execute("dataset.export", &json!({})).unwrap_err().to_string();
|
||||
assert!(err.contains("more than 1000 data sets"), "{err}");
|
||||
s.execute("dataset.delete", &json!({"names": ["Set 1001"]})).unwrap();
|
||||
s.execute("dataset.export", &json!({})).unwrap();
|
||||
|
||||
s.execute("variable.define", &json!({"name": "V257", "kind": "text"})).unwrap();
|
||||
let err = s.execute("dataset.export", &json!({})).unwrap_err().to_string();
|
||||
assert!(err.contains("more than 256 variables"), "{err}");
|
||||
let dir = Folder::new("vars-csv-caps");
|
||||
let out = dir.file("sets.csv");
|
||||
assert!(s.execute("dataset.export", &json!({"path": out})).is_err());
|
||||
assert!(!std::path::Path::new(&out).exists(), "no file is written");
|
||||
}
|
||||
|
||||
/// A header row with a tab and no comma outside quotes makes tabs the separator; a quoted cell
|
||||
/// may then hold tabs and commas. A header row with a comma outside quotes keeps commas, even
|
||||
/// when a cell holds a tab. A tab-separated header with an unclosed quote fails with the reason.
|
||||
|
||||
Reference in New Issue
Block a user