Skip to content

Commit

Permalink
Print more helpful error message on ParsePicaError (#643)
Browse files Browse the repository at this point in the history
  • Loading branch information
nwagner84 committed Jul 7, 2023
1 parent c78fc2a commit 1fbb0ae
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 17 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* #641 Stabilize `sample` command
* #642 Add `--squash` and `--merge` option

### Changed

* #643 Print more helpful error message on `ParsePicaError`

### Removed

* #639 Remove `xml` command
Expand Down
21 changes: 18 additions & 3 deletions pica-record/src/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ pub use writer::{
/// [BufReader](std::io::BufReader).
#[derive(Error, Debug)]
pub enum ReadPicaError {
#[error("parse error")]
Parse(#[from] ParsePicaError),
#[error("parse error: {msg:?}")]
Parse { msg: String, err: ParsePicaError },

#[error("io error")]
Io(#[from] io::Error),
Expand All @@ -34,7 +34,22 @@ impl ReadPicaError {
/// Returns true, if the underlying error was caused by parsing an
/// invalid record.
pub fn is_invalid_record(&self) -> bool {
matches!(self, Self::Parse(ParsePicaError::InvalidRecord(_)))
matches!(
self,
Self::Parse {
msg: _,
err: ParsePicaError::InvalidRecord(_)
}
)
}
}

impl From<ParsePicaError> for ReadPicaError {
fn from(err: ParsePicaError) -> Self {
Self::Parse {
msg: "invalid record".into(),
err,
}
}
}

Expand Down
41 changes: 34 additions & 7 deletions pica-record/src/io/reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ impl ReaderBuilder {
/// let data =
/// Cursor::new(b"003@ \x1f0abc\x1e\n003@ \x1f0def\x1e\n");
/// let mut reader =
/// ReaderBuilder::new().limit(1).from_reader(data);
/// ReaderBuilder::new().limit(1).from_reader(data, None);
///
/// let mut count = 0;
/// while let Some(result) = reader.next() {
Expand All @@ -60,7 +60,7 @@ impl ReaderBuilder {
/// fn example() -> anyhow::Result<()> {
/// let data =
/// Cursor::new(b"003@ \x1f0abc\x1e\n003@ \x1f0def\x1e\n");
/// let mut reader = ReaderBuilder::new().from_reader(data);
/// let mut reader = ReaderBuilder::new().from_reader(data, None);
///
/// let mut count = 0;
/// while let Some(result) = reader.next() {
Expand All @@ -72,15 +72,20 @@ impl ReaderBuilder {
/// Ok(())
/// }
/// ```
pub fn from_reader<R: Read>(&self, reader: R) -> Reader<R> {
Reader::new(self, reader)
pub fn from_reader<R: Read>(
&self,
reader: R,
source: Option<String>,
) -> Reader<R> {
Reader::new(self, reader, source)
}

pub fn from_path<P: AsRef<Path>>(
&self,
path: P,
) -> io::Result<Reader<Box<dyn Read>>> {
let path = path.as_ref();
let source = path.to_string_lossy().to_string();

let reader: Box<dyn Read> = match path
.extension()
Expand All @@ -96,22 +101,28 @@ impl ReaderBuilder {
}
};

Ok(self.from_reader(reader))
Ok(self.from_reader(reader, Some(source)))
}
}

pub struct Reader<R: Read> {
inner: BufReader<R>,
source: Option<String>,
limit: usize,
count: usize,
buf: Vec<u8>,
}

impl<R: Read> Reader<R> {
pub fn new(builder: &ReaderBuilder, reader: R) -> Self {
pub fn new(
builder: &ReaderBuilder,
reader: R,
source: Option<String>,
) -> Self {
Self {
inner: BufReader::new(reader),
limit: builder.limit,
source,
buf: vec![],
count: 0,
}
Expand Down Expand Up @@ -145,7 +156,23 @@ impl<R: Read> RecordsIterator for Reader<R> {
Ok(_) => {
let result = ByteRecord::from_bytes(&self.buf);
match result {
Err(e) => Some(Err(ReadPicaError::from(e))),
Err(err) => {
let msg = match &self.source {
Some(source) => {
if source == "-" {
format!("invalid record in line {} (stdin)", self.count)
} else {
format!("invalid record in line {} ({})", self.count, source)
}
}
None => format!(
"invalid record on line {}",
self.count
),
};

Some(Err(ReadPicaError::Parse { msg, err }))
}
Ok(record) => {
self.count += 1;
Some(Ok(record))
Expand Down
7 changes: 4 additions & 3 deletions src/bin/pica/commands/invalid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,10 @@ impl Invalid {

while let Some(result) = reader.next() {
match result {
Err(ReadPicaError::Parse(
ParsePicaError::InvalidRecord(data),
)) => {
Err(ReadPicaError::Parse {
msg: _,
err: ParsePicaError::InvalidRecord(data),
}) => {
writer.write_all(&data)?;
}
Err(e) => return Err(e.into()),
Expand Down
9 changes: 5 additions & 4 deletions src/bin/pica/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub(crate) enum CliError {
Io(io::Error),
Csv(csv::Error),
Pica(pica::Error),
ParsePica(pica_record::ParsePicaError),
ParsePica(String),
ParsePath(pica_path::ParsePathError),
ParseMatcher(pica_matcher::ParseMatcherError),
ParseQuery(pica_select::ParseQueryError),
Expand Down Expand Up @@ -52,9 +52,10 @@ impl From<pica_record::io::ReadPicaError> for CliError {
fn from(err: pica_record::io::ReadPicaError) -> Self {
match err {
pica_record::io::ReadPicaError::Io(e) => CliError::Io(e),
pica_record::io::ReadPicaError::Parse(e) => {
CliError::ParsePica(e)
}
pica_record::io::ReadPicaError::Parse {
msg: m,
err: _,
} => CliError::ParsePica(m),
}
}
}
Expand Down

0 comments on commit 1fbb0ae

Please sign in to comment.