8bd4b1a77b
- 8jk6.4: the CSV parser yields source text (CsvRecords), and only the role reads it as a number. A forced category keeps00565796and220944e2verbatim; measures and analysis read the text through csv_parser::number_in. CsvRecords reaches a pipeline only through ImportPipeline::from_csv, so handing CSV text to the JSON constructor no longer compiles. - 8jk6.5: an import formula that does not parse is refused by name with its parse error: the build refuses (headless exits non-zero and writes nothing), and the wizard keeps it out of the list with the text still in the buffer. - 8jk6.6: an import that kept no record refuses and says why, from the build and from the wizard's review step. A partial skip stays a warning with a count and exit 0. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VdfEGXA7aJ6ftMsCc9PJze
66 lines
2.3 KiB
Rust
66 lines
2.3 KiB
Rust
//! A folder import's per-file views, saved and reloaded, as both front ends
|
|
//! project them (improvise-lpzu). Evidence: `context/test-evidence/cli.md`.
|
|
|
|
use improvise::import::csv_parser::merge_csvs_from;
|
|
use improvise::import::wizard::ImportPipeline;
|
|
use improvise::persistence::{format_md, parse_md};
|
|
use improvise::ui::app::App;
|
|
use improvise_command::ui::project::{Viewport, grid_in};
|
|
use improvise_io::storage::{MemoryFs, Storage};
|
|
|
|
fn reloaded_folder_import() -> App {
|
|
let fs = MemoryFs::new();
|
|
let files = [
|
|
(
|
|
"maat/age.csv",
|
|
"entity,age-months\nsrc/a.rs,2\nsrc/c.rs,7\n",
|
|
),
|
|
(
|
|
"maat/coupling.csv",
|
|
"entity,coupled,degree,average-revs\nsrc/a.rs,src/b.rs,80,5\nsrc/b.rs,src/c.rs,40,3\n",
|
|
),
|
|
("maat/soc.csv", "entity,soc\n"),
|
|
];
|
|
for (path, body) in files {
|
|
fs.write(std::path::Path::new(path), body.as_bytes())
|
|
.unwrap();
|
|
}
|
|
let merged = merge_csvs_from(&fs, &files.map(|(p, _)| p)).unwrap();
|
|
let (wb, _) = ImportPipeline::from_merge(merged)
|
|
.build_model_with_warnings()
|
|
.unwrap();
|
|
App::new(parse_md(&format_md(&wb)).unwrap(), None)
|
|
}
|
|
|
|
/// Evidence: `context/test-evidence/cli.md`.
|
|
#[test]
|
|
fn a_saved_per_file_view_projects_only_its_file() {
|
|
let app = reloaded_folder_import();
|
|
let whole = Viewport {
|
|
row_offset: 0,
|
|
col_offset: 0,
|
|
rows: 100,
|
|
cols: 100,
|
|
};
|
|
for (view, texts) in [("age", ["2.00", "7.00"]), ("coupling", ["5.00", "3.00"])] {
|
|
let grid = grid_in(&app, view, whole);
|
|
let file = grid.page_coords.iter().find(|p| p.cat == "File").unwrap();
|
|
assert_eq!((file.selected.as_str(), &file.items), (view, &None));
|
|
let drawn: Vec<&str> = grid
|
|
.cells
|
|
.iter()
|
|
.flatten()
|
|
.map(|c| c.text.as_str())
|
|
.filter(|t| !t.is_empty())
|
|
.collect();
|
|
assert_eq!(drawn, texts, "{view}");
|
|
}
|
|
|
|
// A header-only file: its item exists, and its view has nothing editable.
|
|
let soc = grid_in(&app, "soc", whole);
|
|
let file = soc.page_coords.iter().find(|p| p.cat == "File").unwrap();
|
|
assert_eq!(file.selected, "soc");
|
|
assert!(soc.records, "a file with no measure opens in records mode");
|
|
assert_eq!(soc.extent.rows, 0, "{soc:?}");
|
|
}
|