mod fmt;
pub mod parser;
pub use fmt::CodeFormatter;
use itertools::Itertools;
use nom::{error::Error, IResult};
use std::{
cell::RefCell,
collections::HashMap,
fmt::{Debug, Display, Write},
hash::Hash,
ops::{Deref, DerefMut},
str::FromStr,
sync::Arc,
};
pub type SimpleWrapper = String;
pub type ComplexWrapper = Vec<Vec<String>>;
#[derive(Debug, Clone, Default)]
pub struct GroupWrapper {
pub title: Vec<String>,
pub attr_list: AttributeList,
}
pub type AttributeList = Vec<(String, AttriValue)>;
#[derive(Debug, Clone)]
pub enum AttriValue {
Simple(SimpleWrapper),
Complex(ComplexWrapper),
Group(GroupWrapper),
}
#[derive(Debug)]
#[derive(thiserror::Error)]
pub enum LinkError {
#[error("Can not find in hashset!")]
NotFind,
#[error("{0}")]
BorrowError(core::cell::BorrowError),
}
impl PartialEq for LinkError {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::BorrowError(_), Self::BorrowError(_)) => true,
(LinkError::NotFind, LinkError::NotFind) => true,
_ => false,
}
}
}
pub trait SimpleAttri: Sized + Display + FromStr {
#[inline]
fn parse(s: &str) -> Result<Self, <Self as FromStr>::Err> {
FromStr::from_str(s)
}
#[inline]
fn nom_parse<'a>(
i: &'a str,
line_num: &mut usize,
) -> IResult<&'a str, Result<Self, (<Self as FromStr>::Err, AttriValue)>, Error<&'a str>>
{
let (input, simple) = parser::simple(i, line_num)?;
match Self::parse(simple) {
Ok(s) => Ok((input, Ok(s))),
Err(e) => Ok((input, Err((e, AttriValue::Simple(simple.to_string()))))),
}
}
#[inline]
fn to_wrapper(&self) -> SimpleWrapper {
format!("{self}")
}
#[inline]
fn fmt_liberty<T: Write>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T>,
) -> std::fmt::Result {
<SimpleWrapper as Format>::liberty(&self.to_wrapper(), key, f)
}
}
#[derive(thiserror::Error, Debug)]
pub enum ComplexParseError {
#[error("{0}")]
Float(std::num::ParseFloatError),
#[error("{0}")]
Int(std::num::ParseIntError),
#[error("title length mismatch")]
LengthDismatch,
#[error("other")]
Other,
#[error("unsurpport word")]
UnsupportedWord,
}
pub trait NameAttri: Sized + Clone {
fn parse(v: Vec<String>) -> Result<Self, IdError>;
fn to_vec(self) -> Vec<String>;
}
pub trait ComplexAttri: Sized {
fn parse(v: Vec<&str>) -> Result<Self, ComplexParseError>;
fn to_wrapper(&self) -> ComplexWrapper;
#[inline]
fn nom_parse<'a>(
i: &'a str,
line_num: &mut usize,
) -> IResult<&'a str, Result<Self, ComplexParseError>, Error<&'a str>> {
let (input, complex) = parser::complex(i, line_num)?;
match Self::parse(complex) {
Ok(s) => Ok((input, Ok(s))),
Err(e) => Ok((input, Err(e))),
}
}
#[inline]
fn fmt_liberty<T: Write>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T>,
) -> std::fmt::Result {
<ComplexWrapper as Format>::liberty(&self.to_wrapper(), key, f)
}
}
pub type GroupComments<T> = <T as GroupAttri>::Comments;
pub type AttriComment = Vec<String>;
pub trait GroupAttri: Sized {
type Name;
type Comments;
fn name(&self) -> Self::Name;
fn set_name(&mut self, name: Self::Name);
fn comment(&self) -> &AttriComment;
fn comment_mut(&mut self) -> &mut AttriComment;
fn comments(&self) -> &Self::Comments;
fn comments_mut(&mut self) -> &mut Self::Comments;
fn undefined_list(&self) -> &AttributeList;
fn undefined_list_mut(&mut self) -> &mut AttributeList;
fn nom_parse<'a>(
i: &'a str,
line_num: &mut usize,
) -> IResult<&'a str, Result<Self, IdError>, Error<&'a str>>;
fn fmt_liberty<T: Write>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T>,
) -> std::fmt::Result;
}
#[derive(Debug)]
#[derive(thiserror::Error)]
pub enum IdError {
#[error("title length dismatch (want={0},got={1}), title={2:?}")]
LengthDismatch(usize, usize, Vec<String>),
#[error("replace same id")]
RepeatIdx,
#[error("{0}")]
Int(std::num::ParseIntError),
#[error("{0}")]
Other(String),
}
pub trait NamedGroup: GroupAttri {
fn parse(v: Vec<String>) -> Result<Self::Name, IdError>;
fn name2vec(name: Self::Name) -> Vec<String>;
#[inline]
fn fmt_liberty<T: Write>(&self, f: &mut CodeFormatter<'_, T>) -> std::fmt::Result {
write!(
f,
"{}",
Self::name2vec(self.name())
.into_iter()
.map(|s| if is_word(&s) { s } else { format!("\"{s}\"") })
.join(",")
)
}
}
fn display_nom_error(e: &nom::Err<Error<&str>>) -> String {
match e {
nom::Err::Incomplete(_) => e.to_string(),
nom::Err::Error(e) => format!(
"type[{}] at[{}]",
e.code.description(),
e.input.lines().next().unwrap_or("")
),
nom::Err::Failure(e) => format!(
"type[{}] at[{}]",
e.code.description(),
e.input.lines().next().unwrap_or("")
),
}
}
#[derive(Debug, thiserror::Error)]
pub enum ParserError<'a> {
#[error("Line#{0}, {1}")]
IdError(usize, IdError),
#[error("Line#{0}, {}", display_nom_error(.1))]
NomError(usize, nom::Err<Error<&'a str>>),
#[error("Line#{0}, {1}")]
Other(usize, String),
}
pub(crate) fn test_parse_group<G: GroupAttri + Debug>(s: &str) -> (G, usize) {
let mut n = 1;
match G::nom_parse(s, &mut n) {
Ok((_, Ok(group))) => {
println!("{:#?}", group);
println!("{n}");
let mut output = String::new();
let mut f = CodeFormatter::new(&mut output, "| ");
if let Err(e) = GroupAttri::fmt_liberty(&group, std::any::type_name::<G>(), &mut f)
{
panic!("{e}");
}
println!("{}", output);
return (group, n);
}
Ok((_, Err(e))) => panic!("{:#?}", e),
Err(e) => panic!("{:#?}", e),
}
}
pub trait Format {
fn liberty<T: Write>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T>,
) -> std::fmt::Result;
fn db<T: Write>(&self, key: &str, f: &mut CodeFormatter<'_, T>) -> std::fmt::Result {
todo!()
}
fn json<T: Write>(&self, key: &str, f: &mut CodeFormatter<'_, T>) -> std::fmt::Result {
todo!()
}
}
pub(crate) fn is_word(s: &String) -> bool {
!s.is_empty() && s.chars().all(parser::char_in_word)
}
impl Format for AttriComment {
#[inline]
fn liberty<T: Write>(&self, _: &str, f: &mut CodeFormatter<'_, T>) -> std::fmt::Result {
match self.len() {
0 => Ok(()),
_ => write!(f, "\n* {}", self.join("\n").replace("\n", "\n* ")),
}
}
}
impl Format for SimpleWrapper {
#[inline]
fn liberty<T: Write>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T>,
) -> std::fmt::Result {
if is_word(self) {
write!(f, "\n{key} : {};", self)
} else {
write!(f, "\n{key} : \"{}\";", self)
}
}
}
impl Format for ComplexWrapper {
fn liberty<T: Write>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T>,
) -> std::fmt::Result {
if self.is_empty() {
return write!(f, "\n{key} ();");
};
if self[0].iter().all(is_word) {
write!(f, "\n{key} ({}", self[0].join(","))?;
} else {
write!(f, "\n{key} (\"{}\"", self[0].join(","))?;
}
f.indent(1);
for v in self.iter().skip(1) {
if v.iter().all(is_word) {
write!(f, ", \\\n{}", v.join(","))?;
} else {
write!(f, ", \\\n\"{}\"", v.join(","))?;
}
}
f.dedent(1);
write!(f, ");")
}
}
pub(crate) fn liberty_attr_list<T: Write>(
attr_list: &AttributeList,
f: &mut CodeFormatter<'_, T>,
) -> std::fmt::Result {
for (key, attr) in attr_list.iter() {
match attr {
AttriValue::Simple(a) => Format::liberty(a, key, f)?,
AttriValue::Complex(a) => Format::liberty(a, key, f)?,
AttriValue::Group(a) => Format::liberty(a, key, f)?,
}
}
Ok(())
}
impl Format for GroupWrapper {
fn liberty<T: Write>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T>,
) -> std::fmt::Result {
write!(
f,
"\n{key} ({}) {{",
self
.title
.iter()
.map(|s| if is_word(s) { s.clone() } else { "\"".to_owned() + s + "\"" })
.join(",")
)?;
f.indent(1);
liberty_attr_list(&self.attr_list, f);
f.dedent(1);
write!(f, "\n}}")
}
}