zzp 0.1.0

bookkeeping library for freelancers
Documentation
use std::path::Path;

mod hours;
mod entry;

pub use hours::*;
pub use entry::*;

pub fn parse_file(path: impl AsRef<Path>) -> Result<Vec<Entry>, FileParseError> {
	let data = std::fs::read(path)?;
	parse_bytes(&data).map_err(|e| e.into())
}

pub fn parse_bytes(data: &[u8]) -> Result<Vec<Entry>, FileEntryParseError> {
	let mut result = Vec::new();

	for (i, line) in data.split(|c| *c == b'\n').enumerate() {
		let line = std::str::from_utf8(line).map_err(|_| FileEntryParseError::new(i, EntryParseError::InvalidUtf8))?;
		let line = line.trim();
		if line.is_empty() || line.starts_with('#') {
			continue;
		}

		let entry = Entry::from_str(line).map_err(|e| FileEntryParseError::new(i + 1, e))?;
		result.push(entry);
	}

	Ok(result)
}

#[derive(Debug)]
pub enum FileParseError {
	Io(std::io::Error),
	Entry(FileEntryParseError)
}

#[derive(Debug)]
pub struct FileEntryParseError {
	pub line: usize,
	pub error: EntryParseError,
}

impl FileEntryParseError {
	fn new(line: usize, error: EntryParseError) -> Self {
		Self { line, error }
	}
}

impl std::error::Error for FileParseError {}
impl std::error::Error for FileEntryParseError {}

impl From<std::io::Error> for FileParseError {
	fn from(other: std::io::Error) -> Self {
		Self::Io(other)
	}
}

impl From<FileEntryParseError> for FileParseError {
	fn from(other: FileEntryParseError) -> Self {
		Self::Entry(other)
	}
}

impl std::fmt::Display for FileParseError {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		match self {
			Self::Io(e) => write!(f, "{}", e),
			Self::Entry(e) => write!(f, "{}", e),
		}
	}
}

impl std::fmt::Display for FileEntryParseError {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "on line {}: {}", self.line, self.error)
	}
}