Skip to content

Commit 8f64f08

Browse files
committed
Restructure QplibParseError
1 parent 10eb8a9 commit 8f64f08

File tree

2 files changed

+59
-48
lines changed

2 files changed

+59
-48
lines changed

rust/ommx/src/qplib.rs

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,43 @@ mod parser;
77
pub use parser::QplibFile;
88

99
/// Reads and parses the file at the given path as a gzipped MPS file.
10-
pub fn load_file(path: impl AsRef<Path>) -> Result<crate::v1::Instance, QplibParseError> {
10+
pub fn load_file(path: impl AsRef<Path>) -> anyhow::Result<crate::v1::Instance> {
1111
let data = QplibFile::from_file(path)?;
12-
convert::convert(data)
12+
let converted = convert::convert(data)?;
13+
Ok(converted)
1314
}
1415

15-
pub fn load_file_bytes(path: impl AsRef<Path>) -> Result<Vec<u8>, QplibParseError> {
16+
pub fn load_file_bytes(path: impl AsRef<Path>) -> anyhow::Result<Vec<u8>> {
1617
let instance = load_file(path)?;
1718
Ok(instance.encode_to_vec())
1819
}
1920

2021
#[derive(Debug, thiserror::Error)]
21-
pub enum QplibParseError {
22+
#[error("{reason} (at line {line_num})")]
23+
pub struct QplibParseError {
24+
line_num: usize,
25+
reason: ParseErrorReason,
26+
}
27+
28+
impl QplibParseError {
29+
// generic "invalid line" error
30+
fn invalid_line(line_num: usize) -> Self {
31+
Self {
32+
line_num,
33+
reason: ParseErrorReason::InvalidLine(line_num),
34+
}
35+
}
36+
37+
fn unexpected_eof(line_num: usize) -> Self {
38+
Self {
39+
line_num,
40+
reason: ParseErrorReason::UnexpectedEndOfFile(line_num),
41+
}
42+
}
43+
}
44+
45+
#[derive(Debug, thiserror::Error)]
46+
pub enum ParseErrorReason {
2247
#[error("Invalid problem type: {0}")]
2348
InvalidProblemType(String),
2449
#[error("Invalid OBJSENSE: {0}")]
@@ -33,39 +58,23 @@ pub enum QplibParseError {
3358
ParseInt(#[from] std::num::ParseIntError),
3459
#[error(transparent)]
3560
ParseFloat(#[from] std::num::ParseFloatError),
36-
#[error(transparent)]
37-
Io(#[from] std::io::Error),
38-
39-
// a little hack to allow us to add the line numbers
40-
// as context to errors generated in `FromStr` impls
41-
#[error("{inner} (at line {line_num})")]
42-
WithLine {
43-
line_num: usize,
44-
inner: Box<QplibParseError>,
45-
},
4661
}
4762

48-
impl QplibParseError {
63+
impl ParseErrorReason {
4964
// This is a method to make it easier to add the line number at which an
5065
// error occurred in the qplib parser.
5166
pub(crate) fn with_line(self, line_num: usize) -> QplibParseError {
52-
use QplibParseError::*;
53-
match self {
54-
e @ UnexpectedEndOfFile(_) => e,
55-
e @ InvalidLine(_) => e,
56-
WithLine { inner, .. } => WithLine { line_num, inner },
57-
e => WithLine {
58-
line_num,
59-
inner: Box::new(e),
60-
},
67+
QplibParseError {
68+
line_num,
69+
reason: self,
6170
}
6271
}
6372
}
6473

6574
// Workaround to the fact that `String`'s `FromStr` impl has error
6675
// type `Infallible`. As the conversion can't fail, by definition,
6776
// this will never be called and no panic will ever happen
68-
impl From<std::convert::Infallible> for QplibParseError {
77+
impl From<std::convert::Infallible> for ParseErrorReason {
6978
fn from(_: std::convert::Infallible) -> Self {
7079
unreachable!()
7180
}

rust/ommx/src/qplib/parser.rs

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use super::QplibParseError;
1+
use super::{ParseErrorReason, QplibParseError};
22
use std::collections::HashMap;
33
use std::{
44
fmt::Display,
@@ -8,7 +8,7 @@ use std::{
88
str::FromStr,
99
};
1010

11-
pub type Result<T> = std::result::Result<T, QplibParseError>;
11+
use anyhow::{Context, Result};
1212

1313
#[derive(Default, Debug)]
1414
pub struct QplibFile {
@@ -49,7 +49,9 @@ pub struct QplibFile {
4949

5050
impl QplibFile {
5151
pub fn from_file(path: impl AsRef<Path>) -> Result<Self> {
52-
let f = fs::File::open(path)?;
52+
let path = path.as_ref();
53+
let f = fs::File::open(path)
54+
.with_context(|| format!("Failed to read file {}", path.display()))?;
5355
Self::from_reader(f)
5456
}
5557

@@ -70,7 +72,7 @@ impl QplibFile {
7072
.split_whitespace()
7173
.next()
7274
.map(|s| s.to_string())
73-
.ok_or(QplibParseError::InvalidLine(cursor.line_num))?;
75+
.ok_or(QplibParseError::invalid_line(cursor.line_num))?;
7476
let ProblemType(okind, vkind, ckind) = cursor.next_parse()?;
7577
let sense = cursor.next_parse()?;
7678
let num_vars = cursor.next_parse()?;
@@ -276,10 +278,10 @@ impl Display for ProbConstrKind {
276278
}
277279

278280
impl FromStr for ProblemType {
279-
type Err = QplibParseError;
281+
type Err = ParseErrorReason;
280282

281-
fn from_str(s: &str) -> Result<Self> {
282-
let err_out = || QplibParseError::InvalidProblemType(s.to_owned());
283+
fn from_str(s: &str) -> Result<Self, Self::Err> {
284+
let err_out = || ParseErrorReason::InvalidProblemType(s.to_owned());
283285
let mut chars = s.chars();
284286
let ((o, v), c) = chars
285287
.next()
@@ -322,13 +324,13 @@ pub enum ObjSense {
322324
}
323325

324326
impl FromStr for ObjSense {
325-
type Err = QplibParseError;
327+
type Err = ParseErrorReason;
326328

327-
fn from_str(s: &str) -> Result<Self> {
329+
fn from_str(s: &str) -> Result<Self, Self::Err> {
328330
match s.to_lowercase().as_str() {
329331
"minimize" => Ok(Self::Minimize),
330332
"maximize" => Ok(Self::Maximize),
331-
_ => Err(QplibParseError::InvalidObjSense(s.to_owned())),
333+
_ => Err(ParseErrorReason::InvalidObjSense(s.to_owned())),
332334
}
333335
}
334336
}
@@ -342,14 +344,14 @@ pub enum VarType {
342344
}
343345

344346
impl FromStr for VarType {
345-
type Err = QplibParseError;
347+
type Err = ParseErrorReason;
346348

347-
fn from_str(s: &str) -> Result<Self> {
349+
fn from_str(s: &str) -> Result<Self, Self::Err> {
348350
match s {
349351
"0" => Ok(VarType::Continuous),
350352
"1" => Ok(VarType::Integer),
351353
"2" => Ok(VarType::Binary),
352-
_ => Err(QplibParseError::InvalidVarType(s.to_owned())),
354+
_ => Err(ParseErrorReason::InvalidVarType(s.to_owned())),
353355
}
354356
}
355357
}
@@ -403,30 +405,30 @@ where
403405
return Ok(s);
404406
}
405407
}
406-
Err(QplibParseError::UnexpectedEndOfFile(self.line_num))
408+
Err(QplibParseError::unexpected_eof(self.line_num).into())
407409
}
408410

409411
fn parse_or_err_with_line<T, E>(&self, raw: &str) -> Result<T>
410412
where
411413
T: FromStr<Err = E>,
412-
E: Into<QplibParseError>,
414+
E: Into<ParseErrorReason>,
413415
{
414416
raw.parse::<T>()
415-
.map_err(|e| e.into().with_line(self.line_num))
417+
.map_err(|e| e.into().with_line(self.line_num).into())
416418
}
417419

418420
/// Consumes the next line and tries to parse the first value
419421
/// in it (determined by whitespace).
420422
fn next_parse<T, E>(&mut self) -> Result<T>
421423
where
422424
T: FromStr<Err = E>,
423-
E: Into<QplibParseError>,
425+
E: Into<ParseErrorReason>,
424426
{
425427
let line = self.expect_next()?;
426428
let val = line
427429
.split_whitespace()
428430
.next()
429-
.ok_or(QplibParseError::InvalidLine(self.line_num))?;
431+
.ok_or(QplibParseError::invalid_line(self.line_num))?;
430432
self.parse_or_err_with_line(val)
431433
}
432434

@@ -450,12 +452,12 @@ where
450452
&mut self,
451453
// number of "segments" to split line into.
452454
segments: usize,
453-
f: impl Fn(Vec<String>) -> Result<(K, V)>,
455+
f: impl Fn(Vec<String>) -> Result<(K, V), ParseErrorReason>,
454456
) -> Result<HashMap<K, V>>
455457
where
456458
K: Eq + std::hash::Hash,
457459
V: FromStr<Err = E>,
458-
QplibParseError: From<E>,
460+
ParseErrorReason: From<E>,
459461
{
460462
let num = self.next_parse()?;
461463
let mut out = HashMap::with_capacity(num);
@@ -475,7 +477,7 @@ where
475477
fn collect_i_val<V, E>(&mut self) -> Result<HashMap<usize, V>>
476478
where
477479
V: FromStr<Err = E>,
478-
QplibParseError: From<E>,
480+
ParseErrorReason: From<E>,
479481
{
480482
self.consume_map(2, |parts| {
481483
let key = parts[0].parse::<usize>()? - 1;
@@ -511,7 +513,7 @@ where
511513
size: usize,
512514
// number of "segments" to split line into.
513515
segments: usize,
514-
f: impl Fn(Vec<String>) -> Result<(usize, K, f64)>,
516+
f: impl Fn(Vec<String>) -> Result<(usize, K, f64), ParseErrorReason>,
515517
) -> Result<Vec<HashMap<K, f64>>>
516518
where
517519
K: Eq + std::hash::Hash + Clone,
@@ -566,7 +568,7 @@ where
566568
fn collect_list<V, E>(&mut self, size: usize) -> Result<Vec<V>>
567569
where
568570
V: FromStr<Err = E> + Clone,
569-
E: Into<QplibParseError>,
571+
E: Into<ParseErrorReason>,
570572
{
571573
let default: V = self.next_parse()?;
572574
let mut out = vec![default; size];

0 commit comments

Comments
 (0)