dsn: encapsulate de::Deserializer, reorganize error types

This commit is contained in:
Tomasz Cichoń 2024-03-08 12:37:53 +01:00
parent 12574712a4
commit 164a2230ee
2 changed files with 47 additions and 49 deletions

View File

@ -4,12 +4,12 @@ use serde::de::{self, DeserializeSeed, SeqAccess, EnumAccess, VariantAccess, Vis
use serde::Deserialize; use serde::Deserialize;
use thiserror::Error; use thiserror::Error;
type Result<T> = std::result::Result<T, DsnDeError>; type Result<T> = std::result::Result<T, DeError>;
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub enum DsnDeError { pub enum DeError {
#[error("{0}")] #[error("{0}")]
Message(String), Custom(String),
#[error("unexpected EOF")] #[error("unexpected EOF")]
Eof, Eof,
#[error("expected boolean value")] #[error("expected boolean value")]
@ -30,19 +30,23 @@ pub enum DsnDeError {
WrongKeyword(&'static str, String), WrongKeyword(&'static str, String),
} }
impl de::Error for DsnDeError { impl de::Error for DeError {
fn custom<T: std::fmt::Display>(msg: T) -> Self { fn custom<T: std::fmt::Display>(msg: T) -> Self {
DsnDeError::Message(msg.to_string()) DeError::Custom(msg.to_string())
} }
} }
#[derive(Error, Debug)]
#[error("syntax error at {0}: {1}")]
pub struct SyntaxError(pub Context, pub DeError);
#[derive(Debug)] #[derive(Debug)]
pub struct DsnContext { pub struct Context {
line: usize, line: usize,
column: usize, column: usize,
} }
impl fmt::Display for DsnContext { impl fmt::Display for Context {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "line {0}, column {1}", self.line, self.column) write!(f, "line {0}, column {1}", self.line, self.column)
} }
@ -50,7 +54,7 @@ impl fmt::Display for DsnContext {
pub struct Deserializer<'de> { pub struct Deserializer<'de> {
input: &'de str, input: &'de str,
pub context: DsnContext, context: Context,
string_quote: Option<char>, string_quote: Option<char>,
space_in_quoted_tokens: bool, space_in_quoted_tokens: bool,
@ -67,10 +71,10 @@ enum ReconfigIncoming {
} }
impl<'de> Deserializer<'de> { impl<'de> Deserializer<'de> {
pub fn from_str(input: &'de str) -> Self { fn from_str(input: &'de str) -> Self {
Self { Self {
input, input,
context: DsnContext { line: 1, column: 0 }, context: Context { line: 1, column: 0 },
string_quote: None, string_quote: None,
space_in_quoted_tokens: false, space_in_quoted_tokens: false,
@ -94,7 +98,7 @@ impl<'de> Deserializer<'de> {
} }
fn peek(&mut self) -> Result<char> { fn peek(&mut self) -> Result<char> {
self.input.chars().next().ok_or(DsnDeError::Eof) self.input.chars().next().ok_or(DeError::Eof)
} }
fn next(&mut self) -> Result<char> { fn next(&mut self) -> Result<char> {
@ -125,9 +129,9 @@ impl<'de> Deserializer<'de> {
Ok(string) => match string.as_str() { Ok(string) => match string.as_str() {
"on" => Ok(true), "on" => Ok(true),
"off" => Ok(false), "off" => Ok(false),
_ => Err(DsnDeError::ExpectedBool), _ => Err(DeError::ExpectedBool),
}, },
Err(_) => Err(DsnDeError::ExpectedBool), Err(_) => Err(DeError::ExpectedBool),
} }
} }
@ -156,7 +160,7 @@ impl<'de> Deserializer<'de> {
if string.len() > 0 { if string.len() > 0 {
return Ok(string); return Ok(string);
} else { } else {
return Err(DsnDeError::ExpectedUnquoted); return Err(DeError::ExpectedUnquoted);
} }
} }
} }
@ -175,7 +179,7 @@ impl<'de> Deserializer<'de> {
// but there's no reason we shouldn't try to parse the file anyway, no ambiguity arises // but there's no reason we shouldn't try to parse the file anyway, no ambiguity arises
// maybe this should log a warning and proceed? // maybe this should log a warning and proceed?
if self.space_in_quoted_tokens != true && chr == ' ' { if self.space_in_quoted_tokens != true && chr == ' ' {
return Err(DsnDeError::SpaceInQuoted); return Err(DeError::SpaceInQuoted);
} }
if Some(chr) == self.string_quote { if Some(chr) == self.string_quote {
@ -188,21 +192,18 @@ impl<'de> Deserializer<'de> {
} }
} }
pub fn from_str<'a, T>(input: &'a str) -> Result<T> pub fn from_str<'a, T>(input: &'a str) -> std::result::Result<T, SyntaxError>
where where
T: Deserialize<'a>, T: Deserialize<'a>,
{ {
let mut deserializer = Deserializer::from_str(input); let mut deserializer = Deserializer::from_str(input);
let t = T::deserialize(&mut deserializer)?; let value = T::deserialize(&mut deserializer);
/*if !deserializer.input.is_empty() { deserializer.skip_ws();
println!("remaining input"); value.map_err(|err| SyntaxError(deserializer.context, err))
dbg!(deserializer.input);
}*/
Ok(t)
} }
impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
type Error = DsnDeError; type Error = DeError;
fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value> fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
where where
@ -442,14 +443,14 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
{ {
self.skip_ws(); self.skip_ws();
if self.next()? != '(' { if self.next()? != '(' {
return Err(DsnDeError::ExpectedOpeningParen("an enum variant")); return Err(DeError::ExpectedOpeningParen("an enum variant"));
} }
let value = visitor.visit_enum(Enum::new(self))?; let value = visitor.visit_enum(Enum::new(self))?;
self.skip_ws(); self.skip_ws();
if self.next()? != ')' { if self.next()? != ')' {
return Err(DsnDeError::ExpectedClosingParen(name)); return Err(DeError::ExpectedClosingParen(name));
} }
Ok(value) Ok(value)
@ -460,7 +461,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> {
V: Visitor<'de>, V: Visitor<'de>,
{ {
self.skip_ws(); self.skip_ws();
visitor.visit_string(self.parse_string().map_err(|err| DsnDeError::ExpectedKeyword)?) visitor.visit_string(self.parse_string().map_err(|err| DeError::ExpectedKeyword)?)
} }
fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value> fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value>
@ -482,7 +483,7 @@ impl<'a, 'de> Enum<'a, 'de> {
} }
impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> { impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> {
type Error = DsnDeError; type Error = DeError;
type Variant = Self; type Variant = Self;
fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)> fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>
@ -494,7 +495,7 @@ impl<'de, 'a> EnumAccess<'de> for Enum<'a, 'de> {
} }
impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> { impl<'de, 'a> VariantAccess<'de> for Enum<'a, 'de> {
type Error = DsnDeError; type Error = DeError;
fn unit_variant(self) -> Result<()> { fn unit_variant(self) -> Result<()> {
todo!(); todo!();
@ -538,7 +539,7 @@ impl<'a, 'de> ArrayIndices<'a, 'de> {
} }
impl<'de, 'a> SeqAccess<'de> for ArrayIndices<'a, 'de> { impl<'de, 'a> SeqAccess<'de> for ArrayIndices<'a, 'de> {
type Error = DsnDeError; type Error = DeError;
fn next_element_seed<S>(&mut self, seed: S) -> Result<Option<S::Value>> fn next_element_seed<S>(&mut self, seed: S) -> Result<Option<S::Value>>
where where
@ -554,7 +555,7 @@ impl<'de, 'a> SeqAccess<'de> for ArrayIndices<'a, 'de> {
seed.deserialize(&mut *self.de).map(Some) seed.deserialize(&mut *self.de).map(Some)
} else { } else {
let lookahead = self.de.keyword_lookahead().ok_or( let lookahead = self.de.keyword_lookahead().ok_or(
DsnDeError::ExpectedOpeningParen(self.elem_type) DeError::ExpectedOpeningParen(self.elem_type)
)?; )?;
if lookahead == self.elem_type { if lookahead == self.elem_type {
// cannot fail, consuming the lookahead // cannot fail, consuming the lookahead
@ -565,7 +566,7 @@ impl<'de, 'a> SeqAccess<'de> for ArrayIndices<'a, 'de> {
self.de.skip_ws(); self.de.skip_ws();
if self.de.next()? != ')' { if self.de.next()? != ')' {
Err(DsnDeError::ExpectedClosingParen(self.elem_type)) Err(DeError::ExpectedClosingParen(self.elem_type))
} else { } else {
Ok(Some(value)) Ok(Some(value))
} }
@ -593,7 +594,7 @@ impl<'a, 'de> StructFields<'a, 'de> {
} }
impl<'de, 'a> SeqAccess<'de> for StructFields<'a, 'de> { impl<'de, 'a> SeqAccess<'de> for StructFields<'a, 'de> {
type Error = DsnDeError; type Error = DeError;
fn next_element_seed<S>(&mut self, seed: S) -> Result<Option<S::Value>> fn next_element_seed<S>(&mut self, seed: S) -> Result<Option<S::Value>>
where where
@ -631,12 +632,12 @@ impl<'de, 'a> SeqAccess<'de> for StructFields<'a, 'de> {
self.de.skip_ws(); self.de.skip_ws();
if self.de.next()? != ')' { if self.de.next()? != ')' {
Err(DsnDeError::ExpectedClosingParen(field_name)) Err(DeError::ExpectedClosingParen(field_name))
} else { } else {
Ok(Some(value)) Ok(Some(value))
} }
} else { } else {
Err(DsnDeError::WrongKeyword(field_name, parsed_keyword)) Err(DeError::WrongKeyword(field_name, parsed_keyword))
} }
} }
}; };

View File

@ -1,6 +1,5 @@
use std::{collections::HashMap, fmt}; use std::collections::HashMap;
use serde::Deserialize;
use thiserror::Error; use thiserror::Error;
use crate::{ use crate::{
@ -9,18 +8,17 @@ use crate::{
}; };
use super::{ use super::{
de::{Deserializer, DsnContext, DsnDeError}, de,
rules::Rules, rules::Rules,
structure::{DsnFile, Pcb, Shape}, structure::{DsnFile, Pcb, Shape},
}; };
#[derive(Error, Debug)] #[derive(Error, Debug)]
pub struct DsnError(DsnContext, DsnDeError); pub enum LoadingError {
#[error(transparent)]
impl fmt::Display for DsnError { Io(#[from] std::io::Error),
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { #[error(transparent)]
write!(f, "{0}: {1}", self.0, self.1) Syntax(#[from] de::SyntaxError),
}
} }
#[derive(Debug)] #[derive(Debug)]
@ -30,12 +28,11 @@ pub struct DsnDesign {
} }
impl DsnDesign { impl DsnDesign {
pub fn load_from_file(filename: &str) -> Result<Self, DsnError> { pub fn load_from_file(filename: &str) -> Result<Self, LoadingError> {
let contents = std::fs::read_to_string(filename).unwrap(); // TODO: remove unwrap. let contents = std::fs::read_to_string(filename)?;
//let pcb = from_str::<Pcb>(&contents).map_err(|err| DsnError())?; let pcb = de::from_str::<DsnFile>(&contents)
let mut deserializer = Deserializer::from_str(&contents); .map_err(|err| LoadingError::Syntax(err))?
let pcb = DsnFile::deserialize(&mut deserializer) .pcb;
.map_err(|err| DsnError(deserializer.context, err))?.pcb;
let rules = Rules::from_pcb(&pcb); let rules = Rules::from_pcb(&pcb);