diff --git a/Cargo.lock b/Cargo.lock index 8dcd747..eb9cbdd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -28,6 +28,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "anyhow" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61604a8f862e1d5c3229fdd78f8b02c68dcf73a4c4b05fd636d12240aaa242c1" + [[package]] name = "arrayref" version = "0.3.6" @@ -711,6 +717,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" name = "opentally" version = "0.1.0" dependencies = [ + "anyhow", "assert_cmd", "clap", "console_error_panic_hook", diff --git a/Cargo.toml b/Cargo.toml index 27530aa..7fe9a5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,7 @@ edition = "2018" crate-type = ["lib", "cdylib"] [dependencies] +anyhow = "1.0.44" csv = "1.1.6" derive_builder = "0.10.2" derive_more = "0.99.14" diff --git a/src/cli/convert.rs b/src/cli/convert.rs index 17901f0..18330ed 100644 --- a/src/cli/convert.rs +++ b/src/cli/convert.rs @@ -113,7 +113,7 @@ pub fn main(mut cmd_opts: SubcmdOptions) -> Result<(), i32> { } "csp" => { let file = File::open(cmd_opts.infile).expect("IO Error"); - election = parser::csp::parse_reader(file, cmd_opts.require_1, cmd_opts.require_sequential, cmd_opts.require_strict_order); + election = parser::csp::parse_reader(file, cmd_opts.require_1, cmd_opts.require_sequential, cmd_opts.require_strict_order).expect("Syntax Error"); } _ => unreachable!() }; diff --git a/src/parser/csp.rs b/src/parser/csp.rs index 3ae303a..1d6e405 100644 --- a/src/parser/csp.rs +++ b/src/parser/csp.rs @@ -18,6 +18,7 @@ use crate::election::{Ballot, Candidate, Election}; use crate::numbers::Number; +use anyhow::{Context, Result}; use csv::ReaderBuilder; use itertools::Itertools; @@ -25,7 +26,7 @@ use std::collections::HashMap; use std::io::Read; /// Parse the given CSP file -pub fn parse_reader(reader: R, require_1: bool, require_sequential: bool, require_strict_order: bool) -> Election { +pub fn parse_reader(reader: R, require_1: bool, require_sequential: bool, require_strict_order: bool) -> Result> { // Read CSV file let mut reader = ReaderBuilder::new() .has_headers(true) @@ -33,14 +34,14 @@ pub fn parse_reader(reader: R, require_1: bool, require_sequ // Read candidates let mut candidates = Vec::new(); - let mut idx_map = HashMap::new(); // Map csp index -> candidates index + let mut col_map = HashMap::new(); // Map csp column -> candidates index - for (i, cand_name) in reader.headers().expect("Syntax Error").into_iter().enumerate() { + for (i, cand_name) in reader.headers()?.into_iter().enumerate() { if cand_name == "$mult" { continue; } - idx_map.insert(i, candidates.len()); + col_map.insert(i, candidates.len()); candidates.push(Candidate { name: cand_name.to_string(), }); @@ -49,22 +50,22 @@ pub fn parse_reader(reader: R, require_1: bool, require_sequ // Read ballots let mut ballots = Vec::new(); - for record in reader.into_records() { - let record = record.expect("Syntax Error"); + for (csv_row, record) in reader.into_records().enumerate() { + let record = record?; let mut value = N::one(); // Record preferences let mut preferences = Vec::new(); // Vec of (ranking, candidate index) - for (csv_index, preference) in record.into_iter().enumerate() { - match idx_map.get(&csv_index) { + for (csv_col, preference) in record.into_iter().enumerate() { + match col_map.get(&csv_col) { Some(cand_index) => { // Preference if preference.len() == 0 || preference == "-" { continue; } - let preference: usize = preference.parse().expect("Syntax Error"); + let preference: usize = preference.parse().context(format!("Invalid number \"{}\" at row {}, column {}", preference, csv_row + 2, csv_col + 1))?; if preference == 0 { continue; } @@ -73,7 +74,7 @@ pub fn parse_reader(reader: R, require_1: bool, require_sequ } None => { // $mult column - let mult: usize = preference.parse().expect("Syntax Error"); + let mult: usize = preference.parse().context(format!("Invalid number \"{}\" at row {}, column {}", preference, csv_row + 2, csv_col + 1))?; if mult == 1 { continue; } @@ -131,7 +132,7 @@ pub fn parse_reader(reader: R, require_1: bool, require_sequ }); } - return Election { + return Ok(Election { name: String::new(), seats: 0, candidates: candidates, @@ -139,5 +140,5 @@ pub fn parse_reader(reader: R, require_1: bool, require_sequ ballots: ballots, total_votes: None, constraints: None, - }; + }); }