use core::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum ParseErrorKind {
Empty,
UnexpectedChar(char),
UnexpectedEnd,
UnknownElement(String),
BadBracketAtom(&'static str),
UnbalancedParen,
EmptyBranch,
UnclosedRingBond(u32),
RingBondToSelf(u32),
DuplicateRingBond(u32),
ConflictingRingBondOrder(u32),
DanglingBond,
NumberOverflow,
StereoPermOutOfRange {
geometry: &'static str,
got: u32,
max: u32,
},
}
impl fmt::Display for ParseErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => write!(f, "空的 SMILES"),
Self::UnexpectedChar(c) => write!(f, "意外的字符 {c:?}"),
Self::UnexpectedEnd => write!(f, "输入意外结束"),
Self::UnknownElement(s) => write!(f, "无法识别的元素符号 {s:?}"),
Self::BadBracketAtom(why) => write!(f, "方括号原子语法错误:{why}"),
Self::UnbalancedParen => write!(f, "括号不匹配"),
Self::EmptyBranch => write!(f, "空的分支"),
Self::UnclosedRingBond(n) => write!(f, "环闭合标号 {n} 未配对"),
Self::RingBondToSelf(n) => write!(f, "环闭合标号 {n} 的两端是同一个原子"),
Self::DuplicateRingBond(n) => write!(f, "环闭合标号 {n} 在同一对原子间重复"),
Self::ConflictingRingBondOrder(n) => {
write!(f, "环闭合标号 {n} 的两端指定了冲突的键级")
}
Self::DanglingBond => write!(f, "键符号后面缺少原子"),
Self::NumberOverflow => write!(f, "数值超出范围"),
Self::StereoPermOutOfRange { geometry, got, max } => write!(
f,
"立体标记 @{geometry}{got} 的序号超出范围,@{geometry} 最大为 {max}"
),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseError {
pub kind: ParseErrorKind,
pub pos: usize,
pub input: String,
}
impl ParseError {
pub(crate) fn new(kind: ParseErrorKind, pos: usize, input: &[u8]) -> Self {
Self {
kind,
pos,
input: String::from_utf8_lossy(input).into_owned(),
}
}
#[must_use]
pub fn render(&self) -> String {
let cols = self.input[..self.pos.min(self.input.len())].chars().count();
format!("{}\n{}^ {}", self.input, " ".repeat(cols), self.kind)
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "第 {} 字符处:{}", self.pos, self.kind)
}
}
impl std::error::Error for ParseError {}
pub type Result<T> = core::result::Result<T, ParseError>;