steeldb/projectors/
csv_proj.rs1use crate::projector::{slug, CorpusKind, Projector, Situation};
5use std::path::PathBuf;
6
7pub struct CsvProjector {
8 path: PathBuf,
9 columns: Vec<String>,
10}
11
12impl CsvProjector {
13 pub fn open(path: impl Into<PathBuf>) -> std::io::Result<CsvProjector> {
15 let path = path.into();
16 let mut rdr = csv::ReaderBuilder::new().flexible(true).from_path(&path)?;
17 let columns = rdr.headers()?.iter().map(slug).collect();
18 Ok(CsvProjector { path, columns })
19 }
20}
21
22impl Projector for CsvProjector {
23 fn columns(&self) -> Vec<String> {
24 self.columns.clone()
25 }
26 fn kind(&self) -> CorpusKind {
27 CorpusKind::Csv
28 }
29 fn source(&self) -> String {
30 self.path.display().to_string()
31 }
32 fn project(self: Box<Self>, sink: &mut dyn FnMut(Situation)) -> std::io::Result<()> {
33 let mut rdr = csv::ReaderBuilder::new().flexible(true).from_path(&self.path)?;
34 for rec in rdr.records() {
35 let rec = match rec {
36 Ok(r) => r,
37 Err(_) => continue,
38 };
39 let cells: Vec<String> = rec.iter().map(|c| c.to_string()).collect();
40 let mut tokens = Vec::with_capacity(cells.len());
41 let mut numbers = Vec::new();
42 for (i, cell) in cells.iter().enumerate() {
43 let v = cell.trim();
44 if v.is_empty() {
45 continue;
46 }
47 let col = self.columns.get(i).map(|s| s.as_str()).unwrap_or("col");
48 tokens.push(format!("{col}/{}", slug(v)));
49 if let Some(n) = crate::units::parse_number(v) {
51 numbers.push((col.to_string(), n));
52 }
53 }
54 sink(Situation { tokens, display: cells, numbers, beliefs: Vec::new() });
55 }
56 Ok(())
57 }
58}