From ec799fb107569e5b4128e6e9e6b2f998798a61e1 Mon Sep 17 00:00:00 2001 From: Mikolaj Wielgus Date: Sun, 3 Mar 2024 10:53:42 +0000 Subject: [PATCH] dsn: display line, column when printing error --- src/dsn/de.rs | 93 ++++++++++++++++++++++++++++------------------- src/dsn/design.rs | 23 ++++++++++-- 2 files changed, 74 insertions(+), 42 deletions(-) diff --git a/src/dsn/de.rs b/src/dsn/de.rs index 6194b24..f058a1f 100644 --- a/src/dsn/de.rs +++ b/src/dsn/de.rs @@ -1,11 +1,13 @@ +use std::fmt; + use serde::de::{self, DeserializeSeed, SeqAccess, Visitor}; use serde::Deserialize; use thiserror::Error; -type Result = std::result::Result; +type Result = std::result::Result; #[derive(Error, Debug)] -pub enum Error { +pub enum DsnDeError { #[error("{0}")] Message(String), #[error("unexpected EOF")] @@ -22,20 +24,31 @@ pub enum Error { ExpectedOpeningParen, #[error("expected closing parenthesis")] ExpectedClosingParen, - #[error("wrong struct, expected {0}")] - ExpectedStruct(&'static str), + #[error("wrong keyword: expected {0}, got {1}")] + WrongKeyword(&'static str, String), } -impl de::Error for Error { +impl de::Error for DsnDeError { fn custom(msg: T) -> Self { - Error::Message(msg.to_string()) + DsnDeError::Message(msg.to_string()) } } -struct Deserializer<'de> { - input: &'de str, +#[derive(Debug)] +pub struct DsnContext { line: usize, column: usize, +} + +impl fmt::Display for DsnContext { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "line {0}, column {1}", self.line, self.column) + } +} + +pub struct Deserializer<'de> { + input: &'de str, + pub context: DsnContext, string_quote: Option, space_in_quoted_tokens: bool, @@ -52,11 +65,10 @@ enum ReconfigIncoming { } impl<'de> Deserializer<'de> { - fn from_str(input: &'de str) -> Self { + pub fn from_str(input: &'de str) -> Self { Self { input, - line: 0, - column: 0, + context: DsnContext { line: 0, column: 0 }, string_quote: None, space_in_quoted_tokens: false, @@ -80,17 +92,17 @@ impl<'de> Deserializer<'de> { } fn peek(&mut self) -> Result { - self.input.chars().next().ok_or(Error::Eof) + self.input.chars().next().ok_or(DsnDeError::Eof) } fn next(&mut self) -> Result { let chr = self.peek()?; self.input = &self.input[1..]; if chr == '\n' { - self.line += 1; - self.column = 0; + self.context.line += 1; + self.context.column = 0; } else { - self.column += 1; + self.context.column += 1; } Ok(chr) } @@ -111,9 +123,9 @@ impl<'de> Deserializer<'de> { Ok(string) => match string.as_str() { "on" => Ok(true), "off" => Ok(false), - _ => Err(Error::ExpectedBool), + _ => Err(DsnDeError::ExpectedBool), }, - Err(_) => Err(Error::ExpectedBool), + Err(_) => Err(DsnDeError::ExpectedBool), } } @@ -140,8 +152,7 @@ impl<'de> Deserializer<'de> { if string.len() > 0 { return Ok(string); } else { - //dbg!(self.line, self.column); - return Err(Error::ExpectedUnquoted); + return Err(DsnDeError::ExpectedUnquoted); } } } @@ -160,7 +171,7 @@ impl<'de> Deserializer<'de> { // 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? if self.space_in_quoted_tokens != true && chr == ' ' { - return Err(Error::SpaceInQuoted); + return Err(DsnDeError::SpaceInQuoted); } if Some(chr) == self.string_quote { @@ -173,7 +184,7 @@ impl<'de> Deserializer<'de> { } } -pub fn from_str<'a, T>(input: &'a str) -> Result +/*pub fn from_str<'a, T>(input: &'a str) -> Result where T: Deserialize<'a>, { @@ -184,10 +195,10 @@ where dbg!(deserializer.input); }*/ Ok(t) -} +}*/ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { - type Error = Error; + type Error = DsnDeError; fn deserialize_any(self, _visitor: V) -> Result where @@ -359,17 +370,19 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { if self.next()? != '(' { - return Err(Error::ExpectedOpeningParen); + return Err(DsnDeError::ExpectedOpeningParen); } self.skip_ws(); - if &self.parse_identifier()? != name { - return Err(Error::ExpectedStruct(name)); + let parsed_keyword = self.parse_identifier()?; + + if parsed_keyword != name { + return Err(DsnDeError::WrongKeyword(name, parsed_keyword)); } self.skip_ws(); if self.next()? != ')' { - return Err(Error::ExpectedClosingParen); + return Err(DsnDeError::ExpectedClosingParen); } self.skip_ws(); @@ -383,12 +396,14 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { if self.next()? != '(' { - return Err(Error::ExpectedOpeningParen); + return Err(DsnDeError::ExpectedOpeningParen); } self.skip_ws(); - if &self.parse_identifier()? != name { - return Err(Error::ExpectedStruct(name)); + let parsed_keyword = self.parse_identifier()?; + + if parsed_keyword != name { + return Err(DsnDeError::WrongKeyword(name, parsed_keyword)); } self.skip_ws(); @@ -404,7 +419,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { let value = visitor.visit_seq(NewtypeStructFields::new(self))?; if self.next()? != ')' { - return Err(Error::ExpectedClosingParen); + return Err(DsnDeError::ExpectedClosingParen); } self.skip_ws(); @@ -457,19 +472,21 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { if self.next()? != '(' { - return Err(Error::ExpectedOpeningParen); + return Err(DsnDeError::ExpectedOpeningParen); } self.skip_ws(); - if &self.parse_identifier()? != name { - return Err(Error::ExpectedStruct(name)); + let parsed_keyword = self.parse_identifier()?; + + if parsed_keyword != name { + return Err(DsnDeError::WrongKeyword(name, parsed_keyword)); } self.skip_ws(); let value = visitor.visit_seq(StructFields::new(self, fields))?; if self.next()? != ')' { - return Err(Error::ExpectedClosingParen); + return Err(DsnDeError::ExpectedClosingParen); } self.skip_ws(); @@ -517,7 +534,7 @@ impl<'a, 'de> NewtypeStructFields<'a, 'de> { } impl<'de, 'a> SeqAccess<'de> for NewtypeStructFields<'a, 'de> { - type Error = Error; + type Error = DsnDeError; fn next_element_seed(&mut self, seed: S) -> Result> where @@ -542,7 +559,7 @@ impl<'a, 'de> ArrayIndices<'a, 'de> { } impl<'de, 'a> SeqAccess<'de> for ArrayIndices<'a, 'de> { - type Error = Error; + type Error = DsnDeError; fn next_element_seed(&mut self, seed: S) -> Result> where @@ -584,7 +601,7 @@ impl<'a, 'de> StructFields<'a, 'de> { } impl<'de, 'a> SeqAccess<'de> for StructFields<'a, 'de> { - type Error = Error; + type Error = DsnDeError; fn next_element_seed(&mut self, seed: S) -> Result> where diff --git a/src/dsn/design.rs b/src/dsn/design.rs index 0038b4e..d94a95b 100644 --- a/src/dsn/design.rs +++ b/src/dsn/design.rs @@ -1,4 +1,7 @@ -use std::collections::HashMap; +use std::{collections::HashMap, fmt}; + +use serde::Deserialize; +use thiserror::Error; use crate::{ layout::{dot::FixedDotWeight, seg::FixedSegWeight, Layout}, @@ -6,11 +9,20 @@ use crate::{ }; use super::{ - de::{from_str, Error}, + de::{Deserializer, DsnContext, DsnDeError}, rules::Rules, structure::Pcb, }; +#[derive(Error, Debug)] +pub struct DsnError(DsnContext, DsnDeError); + +impl fmt::Display for DsnError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{0}: {1}", self.0, self.1) + } +} + #[derive(Debug)] pub struct DsnDesign { pcb: Pcb, @@ -18,9 +30,12 @@ pub struct DsnDesign { } impl DsnDesign { - pub fn load_from_file(filename: &str) -> Result { + pub fn load_from_file(filename: &str) -> Result { let contents = std::fs::read_to_string(filename).unwrap(); // TODO: remove unwrap. - let pcb = from_str::(&contents)?; + //let pcb = from_str::(&contents).map_err(|err| DsnError())?; + let mut deserializer = Deserializer::from_str(&contents); + let pcb = Pcb::deserialize(&mut deserializer) + .map_err(|err| DsnError(deserializer.context, err))?; let rules = Rules::from_pcb(&pcb);