Return Result from CSP parse_reader, better error messages

This commit is contained in:
RunasSudo 2021-10-17 17:00:24 +11:00
parent 71dc671c34
commit e78d06289a
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
4 changed files with 22 additions and 13 deletions

7
Cargo.lock generated
View File

@ -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",

View File

@ -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"

View File

@ -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!()
};

View File

@ -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<R: Read, N: Number>(reader: R, require_1: bool, require_sequential: bool, require_strict_order: bool) -> Election<N> {
pub fn parse_reader<R: Read, N: Number>(reader: R, require_1: bool, require_sequential: bool, require_strict_order: bool) -> Result<Election<N>> {
// Read CSV file
let mut reader = ReaderBuilder::new()
.has_headers(true)
@ -33,14 +34,14 @@ pub fn parse_reader<R: Read, N: Number>(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<R: Read, N: Number>(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<R: Read, N: Number>(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<R: Read, N: Number>(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<R: Read, N: Number>(reader: R, require_1: bool, require_sequ
ballots: ballots,
total_votes: None,
constraints: None,
};
});
}