use core::prelude::v1;
use std::fmt::Display;
use std::num::ParseIntError;
use crate::affix::FlagType;
use crate::dict::FlagValue;
use crate::helpers::convertu32;
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub enum Error {
Parse(ParseError),
Build(BuildError),
Regex(regex::Error),
Io(IoError),
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub struct ParseError {
err: Box<ParseErrorKind>,
span: Option<Span>,
ctx: String,
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Span {
start: LineCol,
end: LineCol,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct LineCol {
line: u32,
col: u32,
}
#[non_exhaustive]
#[derive(Clone, Debug, PartialEq)]
pub enum BuildError {
BuilderCfgSpecTwice,
BuilderCfgUnspecified,
UnknownFlag(String),
FlagType { value: String, expected: FlagType },
DuplicateFlag {
flag: String,
t1: FlagValue,
t2: Option<FlagValue>,
},
NonmatchingFlag { stem: String, flag: String },
}
#[derive(Clone, Debug, PartialEq)]
pub struct IoError {
fname: String,
err: std::io::ErrorKind,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ParseErrorKind {
Boolean,
Char(usize, usize),
Int(ParseIntError),
TableCount {
expected: u32,
actual: u32,
},
AffixHeader,
AffixBody,
AffixFlagMismatch(String),
AffixCrossProduct,
NonWhitespace(char),
ContainsWhitespace,
MorphInfoDelim(String),
MorphInvalidTag(String),
ConversionSplit(usize),
Encoding,
FlagType,
FlagParse(FlagType),
InvalidFlag,
CompoundSyllableCount(usize),
CompoundSyllableParse(ParseIntError),
Personal,
CompoundPattern,
Phonetic(usize),
PartOfSpeech(String),
DictEntry,
Regex(regex::Error),
}
#[derive(Clone, Debug, PartialEq)]
pub struct WordNotFoundError;
impl Span {
pub(crate) fn new(line: u32, col: u32) -> Self {
let lc = LineCol { line, col };
Self {
start: lc,
end: LineCol {
line: line + 1,
col,
},
}
}
}
impl ParseError {
#[inline]
pub fn err(&self) -> &ParseErrorKind {
&self.err
}
#[inline]
pub fn span(&self) -> Option<&Span> {
self.span.as_ref()
}
#[inline]
pub(crate) fn new<T>(err: ParseErrorKind, ctx: &str, line: T, col: T) -> Self
where
T: TryInto<u32> + Display + Copy,
{
Self {
err: Box::new(err),
span: Some(Span::new(convertu32(line), convertu32(col))),
ctx: ctx.to_owned(),
}
}
#[inline]
pub(crate) fn new_nospan(err: ParseErrorKind, ctx: &str) -> Self {
Self {
err: Box::new(err),
span: None,
ctx: ctx.to_owned(),
}
}
#[inline]
pub(crate) fn new_nocol<T>(err: ParseErrorKind, ctx: &str, line: T) -> Self
where
T: TryInto<u32> + Display + Copy,
{
Self::new(err, ctx, convertu32(line), 0)
}
pub(crate) fn add_offset_ret<T>(mut self, line: T, col: T) -> Self
where
T: TryInto<u32> + Display + Copy,
{
let l_inc = convertu32(line);
let c_inc = convertu32(col);
if let Some(span) = self.span.as_mut() {
span.start.line += l_inc;
span.end.line += l_inc;
span.start.col += c_inc;
span.end.col += c_inc;
} else {
self.span = Some(Span::new(l_inc, c_inc));
}
self
}
}
impl ParseErrorKind {
fn help_msg(&self) -> Option<&'static str> {
match self {
ParseErrorKind::Boolean => {
Some("boolean types cannot have anything else on their line")
}
ParseErrorKind::Int(e) => todo!(),
ParseErrorKind::TableCount { expected, actual } => todo!(),
_ => None,
}
}
}
impl IoError {
pub(crate) fn new(fname: &str, err: std::io::ErrorKind) -> Self {
Self {
fname: fname.to_owned(),
err,
}
}
}
impl std::error::Error for Error {}
impl std::error::Error for ParseError {}
impl std::error::Error for ParseErrorKind {}
impl std::error::Error for BuildError {}
impl Display for Error {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Error::Parse(e) => write!(f, "parse error: {e}"),
Error::Build(e) => write!(f, "build error: {e}"),
Error::Regex(e) => write!(f, "regex error: {e}"),
Error::Io(e) => write!(f, "io error: {e}"),
}
}
}
impl Display for ParseError {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match &self.span {
Some(span) => write!(
f,
"parse error at line {}: {}. Context: '{}'",
span.start.line, self.err, self.ctx
)?,
None => write!(f, "parse error: {}", self.err)?,
};
Ok(())
}
}
impl Display for ParseErrorKind {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
ParseErrorKind::Boolean => write!(f, "expected a boolean flag with no content"),
ParseErrorKind::Char(a, b) => write!(f, "expected {a} flags but got {b}"),
ParseErrorKind::Int(e) => write!(f, "failed to parse integer: {e}"),
ParseErrorKind::TableCount {
expected,
actual: received,
} => write!(f, "expected {expected} values in table but got {received}"),
ParseErrorKind::AffixHeader => write!(f, "could not parse affix header"),
ParseErrorKind::AffixBody => write!(f, "could not parse affix body"),
ParseErrorKind::AffixFlagMismatch(flag) => write!(
f,
"invalid affix body: flag does not match expected '{flag}'"
),
ParseErrorKind::AffixCrossProduct => {
write!(f, "value is not a valid cross product indicator")
}
ParseErrorKind::NonWhitespace(c) => write!(
f,
"unexpected non-comment characters before line termination starting at '{c}'"
),
ParseErrorKind::MorphInfoDelim(s) => {
write!(f, "missing ':' delimiter in morph info at '{s}'")
}
ParseErrorKind::MorphInvalidTag(s) => {
write!(f, "tag '{s}' does not match any morphographic types")
}
ParseErrorKind::ContainsWhitespace => write!(f, "not allowed to contain whitespace"),
ParseErrorKind::Encoding => write!(f, "unrecognized encoding"),
ParseErrorKind::FlagType => write!(f, "unrecognized flag"),
ParseErrorKind::CompoundPattern => write!(f, "invalid compound pattern"),
ParseErrorKind::Phonetic(n) => write!(f, "expected 2 items but got {n}"),
ParseErrorKind::DictEntry => write!(f, "invalid dictionary entry"),
ParseErrorKind::PartOfSpeech(s) => {
write!(f, "value '{s}' is not a known part of speech")
}
ParseErrorKind::ConversionSplit(n) => {
write!(f, "expected a conversion with 2 items but got {n}")
}
ParseErrorKind::CompoundSyllableCount(n) => write!(f, "expected 2 items but got {n}"),
ParseErrorKind::CompoundSyllableParse(e) => write!(f, "unable to parse integer: {e}"),
ParseErrorKind::Regex(e) => e.fmt(f),
ParseErrorKind::AffixHeader => todo!(),
ParseErrorKind::Personal => write!(f, "error parsing entry in personal dictionary"),
ParseErrorKind::InvalidFlag => {
write!(f, "expected a single alphanumeric flag (4 bytes maximum)")
}
ParseErrorKind::FlagParse(v) => write!(f, "error parsing flag of type '{v}'"),
}
}
}
impl Display for BuildError {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
BuildError::BuilderCfgSpecTwice => {
write!(f, "configuration specified twice in builder")
}
BuildError::BuilderCfgUnspecified => {
write!(f, "configuration unspecified twice in builder")
}
BuildError::UnknownFlag(v) => write!(
f,
"got flag `{v}` that wasn't present in affix configuration"
),
BuildError::FlagType { value, expected } => {
write!(f, "value '{value}' is not valid for flag type {expected}")
}
BuildError::DuplicateFlag {
flag,
t1,
t2: Some(v),
} => write!(
f,
"flag '{flag}' used for two or more flags: '{t1}' and '{v}'"
),
BuildError::DuplicateFlag { flag, t1, t2: None } => write!(
f,
"flag '{flag}' used for two or more flags: '{t1}' and affix rule"
),
BuildError::NonmatchingFlag { stem, flag } => write!(
f,
"stem '{stem}' is marked with flag '{flag}' but it does not match any patterns"
),
}
}
}
impl Display for IoError {
#[inline]
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "in file '{}' {}", self.fname, self.err)
}
}
impl From<ParseError> for Error {
#[inline]
fn from(value: ParseError) -> Self {
Self::Parse(value)
}
}
impl From<BuildError> for Error {
#[inline]
fn from(value: BuildError) -> Self {
Self::Build(value)
}
}
impl From<regex::Error> for Error {
#[inline]
fn from(value: regex::Error) -> Self {
Self::Regex(value)
}
}
impl From<regex::Error> for ParseErrorKind {
#[inline]
fn from(value: regex::Error) -> Self {
Self::Regex(value)
}
}
impl From<ParseIntError> for ParseErrorKind {
#[inline]
fn from(value: ParseIntError) -> Self {
Self::Int(value)
}
}
impl From<IoError> for Error {
#[inline]
fn from(value: IoError) -> Self {
Self::Io(value)
}
}