mod fmt;
pub mod parser;
use crate::{library::AttributeType, ArcStr};
use core::{fmt::Write, num::ParseIntError, str::FromStr};
pub use fmt::{CodeFormatter, DefaultCodeFormatter, DefaultIndentation, Indentation};
use itertools::Itertools;
use nom::{error::Error, IResult};
use ordered_float::NotNan;
use std::collections::HashMap;
const DEFINED_COMMENT: &str = " /* user defined attribute */";
pub type SimpleWrapper = ArcStr;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum ComplexWrapper {
Single(Vec<ArcStr>),
Multi(Vec<Vec<ArcStr>>),
}
impl ComplexWrapper {
fn collect(mut vec: Vec<(Vec<&str>, usize)>) -> Self {
if vec.len() <= 1 {
vec.pop().map_or(Self::Single(Vec::new()), |first| {
Self::Single(first.0.into_iter().map_into::<ArcStr>().collect())
})
} else {
Self::Multi(
vec
.into_iter()
.map(|(v, _)| v.into_iter().map_into::<ArcStr>().collect())
.collect(),
)
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct GroupWrapper {
pub title: Vec<ArcStr>,
pub attri_map: Attributes,
}
impl Ord for GroupWrapper {
#[inline]
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.title.cmp(&other.title)
}
}
impl PartialOrd for GroupWrapper {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
pub type Attributes = HashMap<ArcStr, AttriValues>;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum AttriValues {
Simple(SimpleDefined),
Complex(Vec<ComplexWrapper>),
Group(Vec<GroupWrapper>),
}
pub(crate) enum UndefinedAttriValue {
Simple(SimpleWrapper),
Complex(ComplexWrapper),
Group(GroupWrapper),
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum SimpleDefined {
Boolean(Vec<Result<bool, ArcStr>>),
String(Vec<ArcStr>),
Integer(Vec<Result<isize, ArcStr>>),
Float(Vec<Result<NotNan<f64>, ArcStr>>),
}
#[inline]
pub(crate) fn attributs_fmt_liberty<T: Write, I: Indentation>(
attributes: &Attributes,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
#[expect(clippy::all)]
#[inline]
fn fmt1<T: Write, I: Indentation, U: Format>(
v: &Vec<U>,
key: &ArcStr,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
v.iter().try_for_each(|u| Format::liberty(u, key, f))
}
#[expect(clippy::all)]
#[inline]
fn fmt2<T: Write, I: Indentation, U: SimpleAttri>(
v: &Vec<Result<U, ArcStr>>,
key: &ArcStr,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
v.iter().try_for_each(|res_u| {
match res_u {
Ok(u) => SimpleAttri::fmt_liberty(u, key, f),
Err(u) => SimpleAttri::fmt_liberty(u, key, f),
}
.and(write!(f, "{DEFINED_COMMENT}"))
})
}
attributes.iter().sorted().try_for_each(|(key, attri)| match attri {
AttriValues::Simple(SimpleDefined::String(v)) => fmt1(v, key, f),
AttriValues::Simple(SimpleDefined::Boolean(v)) => fmt2(v, key, f),
AttriValues::Simple(SimpleDefined::Float(v)) => fmt2(v, key, f),
AttriValues::Simple(SimpleDefined::Integer(v)) => fmt2(v, key, f),
AttriValues::Complex(v) => fmt1(v, key, f),
AttriValues::Group(v) => fmt1(v, key, f),
})
}
#[inline]
pub(crate) fn attributs_set_undefined_simple(
attri_map: &mut Attributes,
key: &str,
undefined: ArcStr,
) {
if let Some(AttriValues::Simple(SimpleDefined::String(v))) = attri_map.get_mut(key) {
v.push(undefined);
} else {
_ = attri_map.insert(
ArcStr::from(key),
AttriValues::Simple(SimpleDefined::String(vec![undefined])),
);
}
}
#[inline]
pub(crate) fn attributs_set_undefined_complex(
attri_map: &mut Attributes,
key: &str,
undefined: ComplexWrapper,
) {
if let Some(AttriValues::Complex(v)) = attri_map.get_mut(key) {
v.push(undefined);
} else {
_ = attri_map.insert(ArcStr::from(key), AttriValues::Complex(vec![undefined]));
}
}
#[expect(clippy::too_many_lines)]
#[inline]
pub(crate) fn attributs_set_undefined_attri(
attri_map: &mut Attributes,
key: &str,
group_name: &str,
scope: &ParseScope,
undefined: UndefinedAttriValue,
) {
match scope.define_map.get(group_name).and_then(|m| m.get(key)) {
None => {
log::warn!("Line={}; undefined {}", scope.line_num, key);
if let Some(value) = attri_map.get_mut(key) {
match (value, undefined) {
(
AttriValues::Simple(SimpleDefined::String(v)),
UndefinedAttriValue::Simple(u),
) => {
v.push(u);
}
(AttriValues::Complex(v), UndefinedAttriValue::Complex(u)) => {
v.push(u);
}
(AttriValues::Group(v), UndefinedAttriValue::Group(u)) => {
v.push(u);
}
(_, _) => {
log::error!(
"Line={}; Key={key}, the old undefined attribute do NOT meet new one",
scope.line_num
);
}
}
} else {
_ = attri_map.insert(
ArcStr::from(key),
match undefined {
UndefinedAttriValue::Simple(u) => {
AttriValues::Simple(SimpleDefined::String(vec![u]))
}
UndefinedAttriValue::Complex(u) => AttriValues::Complex(vec![u]),
UndefinedAttriValue::Group(u) => AttriValues::Group(vec![u]),
},
);
}
}
Some(DefinedType::Simple(simple_type)) => {
if let UndefinedAttriValue::Simple(u) = undefined {
if let Some(value) = attri_map.get_mut(key) {
match simple_type {
AttributeType::Boolean => {
if let AttriValues::Simple(SimpleDefined::Boolean(v)) = value {
v.push(u.parse().map_or(Err(u), Ok));
} else {
log::error!(
"Line={}; Key={key}, the old attribute do NOT meet new one",
scope.line_num
);
}
}
AttributeType::String => {
if let AttriValues::Simple(SimpleDefined::String(v)) = value {
v.push(u);
} else {
log::error!(
"Line={}; Key={key}, the old attribute do NOT meet new one",
scope.line_num
);
}
}
AttributeType::Integer => {
if let AttriValues::Simple(SimpleDefined::Integer(v)) = value {
v.push(u.parse().map_or(Err(u), Ok));
} else {
log::error!(
"Line={}; Key={key}, the old attribute do NOT meet new one",
scope.line_num
);
}
}
AttributeType::Float => {
if let AttriValues::Simple(SimpleDefined::Float(v)) = value {
v.push(u.parse().map_or(Err(u), Ok));
} else {
log::error!(
"Line={}; Key={key}, the old attribute do NOT meet new one",
scope.line_num
);
}
}
}
} else {
_ = attri_map.insert(
ArcStr::from(key),
match simple_type {
AttributeType::Boolean => {
AttriValues::Simple(SimpleDefined::Boolean(vec![u
.parse()
.map_or(Err(u), Ok)]))
}
AttributeType::String => {
AttriValues::Simple(SimpleDefined::String(vec![u]))
}
AttributeType::Integer => {
AttriValues::Simple(SimpleDefined::Integer(vec![u
.parse()
.map_or(Err(u), Ok)]))
}
AttributeType::Float => AttriValues::Simple(SimpleDefined::Float(vec![u
.parse()
.map_or(Err(u), Ok)])),
},
);
}
} else {
log::error!("Line={}; Key={key}, `defined` got wrong type", scope.line_num);
}
}
Some(DefinedType::Group) => {
if let UndefinedAttriValue::Group(u) = undefined {
if let Some(value) = attri_map.get_mut(key) {
if let AttriValues::Group(v) = value {
v.push(u);
} else {
log::error!(
"Line={}; Key={key}, the old attribute do NOT meet new one",
scope.line_num
);
}
} else {
_ = attri_map.insert(ArcStr::from(key), AttriValues::Group(vec![u]));
}
} else {
log::error!("Line={}; Key={key}, `defined_group` got wrong type", scope.line_num);
}
}
}
}
#[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 {
#[expect(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,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DefinedType {
Simple(AttributeType),
Group,
}
#[derive(Debug, Default)]
pub(crate) struct ParseScope {
pub line_num: usize,
pub define_map: HashMap<ArcStr, HashMap<ArcStr, DefinedType>>,
}
#[derive(Debug, Clone)]
#[derive(Hash, PartialEq, Eq)]
#[derive(Ord, PartialOrd)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum DefinedAttribute {
Boolean(Vec<bool>),
String(Vec<ArcStr>),
Integer(Vec<isize>),
Float(Vec<NotNan<f64>>),
}
pub(crate) type SimpleParseRes<'a, T> =
IResult<&'a str, Result<T, ArcStr>, Error<&'a str>>;
pub(crate) type ComplexParseRes<'a, T> =
IResult<&'a str, Result<T, (ComplexParseError, ComplexWrapper)>, Error<&'a str>>;
#[inline]
pub(crate) fn nom_parse_from_str<'a, T: SimpleAttri + FromStr>(
i: &'a str,
scope: &mut ParseScope,
) -> SimpleParseRes<'a, T> {
let (input, s) = parser::simple(i, &mut scope.line_num)?;
s.parse()
.map_or(Ok((input, Err(ArcStr::from(s)))), |simple| Ok((input, Ok(simple))))
}
pub(crate) trait SimpleAttri: Sized + core::fmt::Display {
fn nom_parse<'a>(i: &'a str, scope: &mut ParseScope) -> SimpleParseRes<'a, Self>;
#[inline]
fn is_set(&self) -> bool {
true
}
#[inline]
fn fmt_self<T: Write, I: Indentation>(
&self,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
write!(f, "{self}")
}
#[inline]
fn fmt_liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
if self.is_set() {
write!(f, "\n{}{key} : ", f.indentation())?;
self.fmt_self(f)?;
write!(f, ";")
} else {
Ok(())
}
}
}
#[derive(thiserror::Error, Debug)]
pub enum ComplexParseError {
#[error("{0}")]
Float(#[from] fast_float2::Error),
#[error("{0}")]
Int(#[from] ParseIntError),
#[error("title length mismatch")]
LengthDismatch,
#[error("other")]
Other,
#[error("unsurpport word")]
UnsupportedWord,
}
#[inline]
pub fn join_fmt<
'a,
T: Sized + 'a,
I: Iterator<Item = T>,
W: Write,
F: FnMut(T, &mut W) -> core::fmt::Result,
>(
iter: I,
f: &mut W,
func: F,
sep: &str,
) -> core::fmt::Result {
write!(f, "\"")?;
join_fmt_no_quote(iter, f, func, sep)?;
write!(f, "\"")
}
#[inline]
pub fn join_fmt_no_quote<
'a,
T: Sized + 'a,
I: Iterator<Item = T>,
W: Write,
F: FnMut(T, &mut W) -> core::fmt::Result,
>(
mut iter: I,
f: &mut W,
mut func: F,
sep: &str,
) -> core::fmt::Result {
if let Some(first) = iter.next() {
func(first, f)?;
while let Some(t) = iter.next() {
write!(f, "{sep}")?;
func(t, f)?;
}
}
Ok(())
}
pub(crate) trait ComplexAttri: Sized {
fn parse<'a, I: Iterator<Item = &'a Vec<&'a str>>>(
iter: I,
scope: &mut ParseScope,
) -> Result<Self, ComplexParseError>;
#[expect(clippy::arithmetic_side_effects)]
#[inline]
fn nom_parse<'a>(i: &'a str, scope: &mut ParseScope) -> ComplexParseRes<'a, Self> {
let (input, vec) = parser::complex(i, &mut scope.line_num)?;
let mut line_num = 0;
let iter = vec.iter().map(|(v, n)| {
line_num += n;
v
});
let res = Self::parse(iter, scope);
scope.line_num += line_num;
match res {
Ok(s) => Ok((input, Ok(s))),
Err(e) => Ok((input, Err((e, ComplexWrapper::collect(vec))))),
}
}
#[inline]
fn is_set(&self) -> bool {
true
}
fn fmt_self<T: Write, I: Indentation>(
&self,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result;
#[inline]
fn fmt_liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
if self.is_set() {
let indent1 = f.indentation();
write!(f, "\n{indent1}{key} (")?;
f.indent(1);
self.fmt_self(f)?;
f.dedent(1);
write!(f, ");")
} else {
Ok(())
}
}
}
pub type GroupComments<T> = <T as Group>::Comments;
pub type AttriComment = Vec<ArcStr>;
pub(crate) trait GroupFn {
#[inline]
fn post_parse_process(&mut self, _scope: &mut ParseScope) {}
}
#[expect(private_bounds)]
pub trait Group: Sized + GroupAttri {
type Comments;
#[inline]
fn display(&self) -> GroupDisplay<'_, Self> {
GroupDisplay { inner: self }
}
}
pub(crate) trait GroupAttri: Sized {
fn nom_parse<'a>(
i: &'a str,
group_name: &str,
scope: &mut ParseScope,
) -> 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={want},got={got}), title={title:?}")]
LengthDismatch { want: usize, got: usize, title: Vec<ArcStr> },
#[error("replace same id")]
RepeatIdx,
#[error("replace same attribute")]
RepeatAttri,
#[error("{0}")]
Int(ParseIntError),
#[error("{0}")]
Other(String),
}
impl IdError {
#[inline]
pub(crate) fn length_dismatch(want: usize, got: usize, v: Vec<&str>) -> Self {
Self::LengthDismatch {
want,
got,
title: v.into_iter().map(ArcStr::from).collect(),
}
}
}
pub(crate) trait NamedGroup: GroupAttri {
fn parse_set_name(&mut self, v: Vec<&str>) -> Result<(), IdError>;
fn fmt_name<T: Write, I: Indentation>(
&self,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result;
}
pub trait NameAttri: Sized {
fn parse(v: Vec<&str>) -> Result<Self, IdError>;
fn fmt_self<T: Write, I: Indentation>(
&self,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result;
}
#[derive(Debug, thiserror::Error)]
pub enum ParserError {
#[error("Line#{0}, {1}")]
IdError(usize, IdError),
#[error("Line#{0}, {1}")]
NomError(usize, String),
#[error("Line#{0}, {1}")]
Other(usize, String),
}
impl ParserError {
#[inline]
pub(crate) fn nom(line: usize, e: nom::Err<Error<&str>>) -> Self {
Self::NomError(
line,
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("")
),
},
)
}
}
#[derive(Debug)]
pub struct GroupDisplay<'a, G> {
pub inner: &'a G,
}
impl<'a, G: GroupAttri> core::fmt::Display for GroupDisplay<'a, G> {
#[inline]
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let mut ff = DefaultCodeFormatter::new(f);
self.inner.fmt_liberty(core::any::type_name::<G>(), &mut ff)
}
}
#[cfg(test)]
#[inline]
pub(crate) fn test_parse<G: GroupAttri + Group>(input: &str) -> G {
let mut scope = ParseScope::default();
let g = match G::nom_parse(input, "", &mut scope) {
Ok((_, Ok(g))) => g,
Ok((_, Err(e))) => panic!("{e:#?}"),
Err(e) => panic!("{e:#?}"),
};
println!("{}", g.display());
g
}
#[cfg(test)]
#[inline]
pub(crate) fn test_parse_fmt<G: GroupAttri + Group>(input: &str, fmt_want: &str) -> G {
let mut scope = ParseScope::default();
let g = match G::nom_parse(input, "", &mut scope) {
Ok((_, Ok(g))) => g,
Ok((_, Err(e))) => panic!("{e:#?}"),
Err(e) => panic!("{e:#?}"),
};
let fmt_str = g.display().to_string();
println!("{fmt_str}");
dev::text_diff(fmt_want, fmt_str.as_str());
g
}
pub(crate) trait Format {
fn liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result;
#[expect(dead_code)]
#[inline]
fn db<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
_ = key;
_ = f;
todo!()
}
#[expect(dead_code)]
#[inline]
fn json<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
_ = key;
_ = f;
todo!()
}
}
#[inline]
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}/* ")?;
join_fmt_no_quote(
self.iter().flat_map(|lines| lines.split('\n')),
f,
|line, ff| write!(ff, "{line}"),
format!("\n{indent}** ").as_str(),
)?;
write!(f, " */")
}
}
}
#[inline]
pub(crate) fn fmt_library_beginning<T: Write, I: Indentation>(
comment: &AttriComment,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
if comment.is_empty() {
write!(
f,
"/* Generated by {} {} */",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_VERSION"),
)
} else {
write!(f, "/* ")?;
join_fmt_no_quote(
comment.iter().flat_map(|lines| lines.split('\n')),
f,
|line, ff| write!(ff, "{line}"),
"\n** ",
)?;
write!(f, " */")
}
}
impl Format for SimpleWrapper {
#[inline]
fn liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
SimpleAttri::fmt_liberty(self, key, f).and(write!(f, "{DEFINED_COMMENT}"))
}
}
impl Format for ComplexWrapper {
#[inline]
fn liberty<T: Write, I: Indentation>(
&self,
key: &str,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
match self {
Self::Single(signle) => {
ComplexAttri::fmt_liberty(signle, key, f).and(write!(f, "{DEFINED_COMMENT}"))
}
Self::Multi(multi) => {
let indent = f.indentation();
write!(f, "\n{indent}{key} (")?;
let mut iter = multi.iter();
if let Some(v) = iter.next() {
join_fmt(v.iter(), f, |s, ff| write!(ff, "{s}"), ", ")?;
}
while let Some(v) = iter.next() {
write!(f, ", \\\n{indent}")?;
join_fmt(v.iter(), f, |s, ff| write!(ff, "{s}"), ", ")?;
}
write!(f, "); {DEFINED_COMMENT}")
}
}
}
}
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} (")?;
join_fmt_no_quote(
self.title.iter(),
f,
|s, ff| if is_word(s) { write!(ff, "{s}") } else { write!(ff, "\"{s}\"") },
", ",
)?;
write!(f, ") {{ {DEFINED_COMMENT}")?;
f.indent(1);
attributs_fmt_liberty(&self.attri_map, f)?;
f.dedent(1);
write!(f, "\n{indent}}}")
}
}