mod fmt;
pub mod parser;
use crate::ArcStr;
use core::{
fmt::{Debug, Display, Write},
num::{ParseFloatError, ParseIntError},
str::FromStr,
};
pub use fmt::{
CodeFormatter, DefaultCodeFormatter, DefaultIndentation, Indentation,
TestCodeFormatter, TestIndentation,
};
use itertools::Itertools;
use nom::{error::Error, IResult};
use ordered_float::ParseNotNanError;
pub type SimpleWrapper = ArcStr;
pub type ComplexWrapper = Vec<Vec<ArcStr>>;
#[derive(Debug, Clone, Default)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct GroupWrapper {
pub title: Vec<ArcStr>,
pub attr_list: AttributeList,
}
pub type AttributeList = Vec<(ArcStr, AttriValue)>;
#[derive(Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
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 {
#[allow(clippy::match_like_matches_macro)]
#[inline]
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::NotFind, Self::NotFind) | (Self::BorrowError(_), Self::BorrowError(_)) => {
true
}
_ => false,
}
}
}
type SimpleParseErr<'a, T> =
IResult<&'a str, Result<T, (<T as FromStr>::Err, AttriValue)>, Error<&'a str>>;
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) -> SimpleParseErr<'a, Self> {
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(ArcStr::from(simple)))))),
}
}
#[inline]
fn to_wrapper(&self) -> SimpleWrapper {
self.to_string().into()
}
#[inline]
fn fmt_liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
<SimpleWrapper as Format>::liberty(&self.to_wrapper(), key, f)
}
}
#[derive(thiserror::Error, Debug)]
pub enum ComplexParseError {
#[error("{0}")]
Float(ParseNotNanError<ParseFloatError>),
#[error("{0}")]
Int(ParseIntError),
#[error("title length mismatch")]
LengthDismatch,
#[error("other")]
Other,
#[error("unsurpport word")]
UnsupportedWord,
}
pub trait NameAttri: Sized + Clone {
fn parse(v: Vec<ArcStr>) -> Result<Self, IdError>;
fn to_vec(self) -> Vec<ArcStr>;
}
pub trait ComplexAttri: Sized {
fn parse(v: &[&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, AttriValue)>, 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,
AttriValue::Complex(vec![complex.into_iter().map(ArcStr::from).collect()]),
)),
)),
}
}
#[inline]
fn fmt_liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
<ComplexWrapper as Format>::liberty(&self.to_wrapper(), key, f)
}
}
pub type GroupComments<T> = <T as GroupAttri>::Comments;
pub type AttriComment = Vec<ArcStr>;
pub trait GroupFn {
#[inline]
fn post_process(&mut self) {}
}
pub trait GroupAttri: Sized {
type Name;
type Comments;
fn name(&self) -> Self::Name;
fn set_name(&mut self, name: Self::Name);
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, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result;
}
#[derive(Debug)]
#[derive(thiserror::Error)]
pub enum IdError {
#[error("title length dismatch (want={0},got={1}), title={2:?}")]
LengthDismatch(usize, usize, Vec<ArcStr>),
#[error("replace same id")]
RepeatIdx,
#[error("replace same attribute")]
RepeatAttri,
#[error("{0}")]
Int(ParseIntError),
#[error("{0}")]
Other(String),
}
pub trait NamedGroup: GroupAttri {
fn parse(v: Vec<ArcStr>) -> Result<Self::Name, IdError>;
fn name2vec(name: Self::Name) -> Vec<ArcStr>;
#[inline]
fn fmt_liberty<T: Write, I: Indentation>(
&self,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
write!(
f,
"{}",
Self::name2vec(self.name())
.into_iter()
.map(|s| if is_word(&s) { s } else { format!("\"{s}\"").into() })
.join(", ")
)
}
}
fn display_nom_error(e: &nom::Err<Error<&str>>) -> ArcStr {
match e {
nom::Err::Incomplete(_) => e.to_string(),
nom::Err::Failure(_e) | nom::Err::Error(_e) => format!(
"type[{}] at[{}]",
_e.code.description(),
_e.input.lines().next().unwrap_or("")
),
}
.into()
}
#[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),
}
#[allow(unused)]
pub(crate) fn test_parse_group<G: GroupAttri + Debug>(s: &str) -> (G, String, 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 = TestCodeFormatter::new(&mut output);
if let Err(e) = GroupAttri::fmt_liberty(&group, core::any::type_name::<G>(), &mut f)
{
panic!("{e}");
}
println!("{output}");
(group, output, n)
}
Ok((_, Err(e))) => panic!("{e:#?}"),
Err(e) => panic!("{e:#?}"),
}
}
pub trait Format {
fn liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result;
#[inline]
fn db<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
_ = key;
_ = f;
todo!()
}
#[inline]
fn json<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
_ = key;
_ = f;
todo!()
}
}
pub(crate) fn is_word(s: &ArcStr) -> bool {
!s.is_empty() && s.chars().all(parser::char_in_word)
}
impl Format for AttriComment {
#[inline]
fn liberty<T: Write, I: Indentation>(
&self,
_: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
if self.is_empty() {
Ok(())
} else {
let indent = f.indentation();
write!(
f,
"\n{indent}/* {} */",
self.join("\n").replace('\n', format!("\n{indent}* ").as_str())
)
}
}
}
impl Format for SimpleWrapper {
#[inline]
fn liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
if self.is_empty() {
Ok(())
} else if is_word(self) {
write!(f, "\n{}{key} : {self};", f.indentation())
} else {
write!(f, "\n{}{key} : \"{self}\";", f.indentation())
}
}
}
impl Format for ComplexWrapper {
#[allow(clippy::indexing_slicing)]
#[inline]
fn liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
if self.is_empty() || (self.len() == 1 && self[0].is_empty()) {
return Ok(());
};
let indent1 = f.indentation();
if self[0].iter().all(is_word) {
write!(f, "\n{indent1}{key} ({}", self[0].join(", "))?;
} else {
write!(f, "\n{indent1}{key} (\"{}\"", self[0].join(", "))?;
}
f.indent(1);
let indent2 = f.indentation();
for v in self.iter().skip(1) {
if v.iter().all(is_word) {
write!(f, ", \\\n{indent2}{}", v.join(", "))?;
} else {
write!(f, ", \\\n{indent2}\"{}\"", v.join(", "))?;
}
}
f.dedent(1);
write!(f, ");")
}
}
#[inline]
pub(crate) fn liberty_attr_list<T: Write, I: Indentation>(
attr_list: &AttributeList,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
for (key, attr) in attr_list {
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 {
#[inline]
fn liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
let indent = f.indentation();
write!(
f,
"\n{indent}{key} ({}) {{",
self
.title
.iter()
.map(|s| if is_word(s) { s.clone() } else { format!("\"{s}\"").into() })
.join(",")
)?;
f.indent(1);
liberty_attr_list(&self.attr_list, f)?;
f.dedent(1);
write!(f, "\n{indent}}}")
}
}