Here's the result:
use std::env;
use std::error::Error;
use std::ffi::OsString;
use std::process;
use std::vec::Vec;
fn print_line(data: &Vec<Vec<String>>, name: &Option<&String>, col: usize) -> Result<(), Box<dyn Error>>{
if let Some(n) = name {
print!("{} = {{",n);
}else{
return Err(From::from("incorrect index"));
}
for record in data {
if let (Some(id), Some(val)) = (record.get(0), record.get(col)) {
print!("{}->{}, ",id, val);
}else{
return Err(From::from("incorrect index"));
}
}
println!("}}");
Ok(())
}
fn run() -> Result<(), Box<dyn Error>> {
let file_path = get_nth_arg(1)?;
let mut rdr = csv::ReaderBuilder::new()
.delimiter(b';')
.from_path(file_path)?;
let header: Vec<String> = rdr.headers()?.iter().map(|x| x.to_owned()).collect();
let data: Vec<Vec<String>> = rdr.records().into_iter().map(|res|
(if let Ok(rec) = res {rec.iter().map(|s| s.to_owned()).collect() } else { Vec::new() }))
.collect();
for i in 0..header.len(){
print_line(&data,&header.get(i), i)?;
}
Ok(())
}
fn get_nth_arg(i: usize) -> Result<OsString,Box<dyn Error>> {
match env::args_os().nth(i) {
None => Err(From::from(format!("expected {} arguments",i))),
Some(file_path) => Ok(file_path),
}
}
fn main() {
if let Err(err) = run() {
println!("{}", err);
process::exit(1);
}
}
Here's some sample content to make a csv that gets parsed by the above program:
P;M;O;W
p1;10;TRUE;5
p2;15;TRUE;6
p3;20;FALSE;7
p4;10;FALSE;8
p5;15;TRUE;9
p6;20;TRUE;5
p7;10;FALSE;6
p8;15;FALSE;7
p9;20;TRUE;8
p10;10;TRUE;9
The output should be:
P = {p1->p1, p2->p2, p3->p3, p4->p4, p5->p5, p6->p6, p7->p7, p8->p8, p9->p9, p10->p10, }
M = {p1->10, p2->15, p3->20, p4->10, p5->15, p6->20, p7->10, p8->15, p9->20, p10->10, }
O = {p1->TRUE, p2->TRUE, p3->FALSE, p4->FALSE, p5->TRUE, p6->TRUE, p7->FALSE, p8->FALSE, p9->TRUE, p10->TRUE, }
W = {p1->5, p2->6, p3->7, p4->8, p5->9, p6->5, p7->6, p8->7, p9->8, p10->9, }
So basically, read the csv, copy its contents to a Vec<Vec<String>>
, print data column by column with some extra formatting. Simple enough for a first project in a new language, I thought (and then I spent 2 days getting to this :D)
Questions:
- is the copy to to
Vec<Vec<String>>
really needed? I was hoping I could reuse the data in the csv reader, but that does not seem the case. - in the
print_line
function, I had some trouble handling theNone
case of theOption
s. Does this rust function look natural to you? - are references the right pointer type for
print_line
parameters? - (edit) is it possible to succinctly throw an error instead of creating an empty vector when copying a record from the csv reader fails?
- any other tips that will avoid headaches in the future?
FWIW, C++ is my main programming language.