neodes_codec 0.1.3

Library to read and write data from DSN files as described in the NeoDes norm.
Documentation
use crate::line::NeodesLine;
use crate::line::NeodesLineParseError;
use encoding_rs::mem::decode_latin1;
use std::fs::File;
use std::io::{BufRead, BufReader, Write};
use std::path::Path;
use std::str::FromStr;

/// Iterator structure that reads NeoDes lines.
pub struct NeodesLineIterator<B: BufRead> {
	reader: B,
	buffer: Vec<u8>,
}

/// Enum representing the errors that can happen while reading a buffer
#[derive(Debug)]
pub enum NeodesLineReadError {
	/// An error occurred while reading the buffer
	IOError(std::io::Error),

	/// The line was an invalid DSN line
	ParseError(NeodesLineParseError),
}

impl<B: BufRead> Iterator for NeodesLineIterator<B> {
	type Item = Result<NeodesLine, NeodesLineReadError>;

	#[inline]
	fn next(&mut self) -> Option<Self::Item> {
		self.buffer.clear();

		match self.reader.read_until(b'\n', &mut self.buffer) {
			Ok(0) => None, // EOF reached
			Ok(_n) => {
				let line = if self.buffer.ends_with(b"\n") {
					&self.buffer[..self.buffer.len() - 1]
				} else {
					&self.buffer[..]
				};

				let decoded = decode_latin1(line);
				Some(NeodesLine::from_str(&decoded).map_err(NeodesLineReadError::ParseError))
			}
			Err(e) => Some(Err(NeodesLineReadError::IOError(e))),
		}
	}
}

/// Read a NeoDes file and returns an iterator of NeoDes lines or parsing
/// errors.
///
/// The iterator continues after encountering a parsing error.
#[inline]
pub fn read_dsn_file<P: AsRef<Path>>(
	path: P,
) -> Result<impl Iterator<Item = Result<NeodesLine, NeodesLineReadError>>, std::io::Error> {
	let file = File::open(path)?;
	let reader = BufReader::new(file);
	Ok(read_dsn(reader))
}

/// Read a `BufRead` containing latin1 encoded lines
#[inline]
pub fn read_dsn<B: BufRead>(reader: B) -> NeodesLineIterator<B> {
	let buffer = Vec::with_capacity(256);
	NeodesLineIterator { reader, buffer }
}

/// Writes a NeoDes file from lines
#[inline]
pub fn write_dsn_file<'a, P: AsRef<Path>, I>(path: P, lines: I) -> Result<(), std::io::Error>
where
	I: IntoIterator<Item = &'a NeodesLine>,
{
	let mut file = File::create(path)?;
	for line in lines {
		file.write_all(&encoding_rs::mem::encode_latin1_lossy(&line.to_string()))?;
		file.write_all(b"\n")?;
	}

	Ok(())
}