mod fmt;
pub mod parser;
use crate::{library::AttributeType, ArcStr, NotNan};
use core::hash::{BuildHasher, Hash, Hasher};
use core::{fmt::Write, num::ParseIntError, str::FromStr};
pub use fmt::{CodeFormatter, DefaultCodeFormatter, DefaultIndentation, Indentation};
use itertools::Itertools;
use nom::{error::Error, IResult};
use std::collections::HashMap;
const DEFINED_COMMENT: &str = " /* user defined attribute */";
#[cfg(not(feature = "fast_hash"))]
pub(crate) type RandomState = std::hash::RandomState;
#[cfg(feature = "fast_hash")]
pub(crate) type RandomState = ahash::RandomState;
#[cfg(feature = "hash_match")]
const HASHER: foldhash::fast::FixedState = foldhash::fast::FixedState::with_seed(41);
#[inline]
#[cfg(feature = "hash_match")]
#[expect(clippy::manual_hash_one)]
pub(crate) fn hash_one<T: Hash + ?Sized>(t: &T) -> u64 {
let mut hasher = HASHER.build_hasher();
t.hash(&mut hasher);
hasher.finish()
}
pub(crate) type GroupSet<T> = <T as mut_set::Item>::MutSet<RandomState>;
pub type SimpleWrapper = ArcStr;
#[expect(clippy::field_scoped_visibility_modifiers)]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct ComplexWrapper(pub(crate) Vec<ArcStr>);
impl ComplexWrapper {
#[expect(clippy::arithmetic_side_effects)]
fn collect(vec: Vec<(usize, &str)>, scope: &mut ParseScope) -> Self {
Self(
vec
.into_iter()
.map(|(n, s)| {
scope.line_num += n;
ArcStr::from(s)
})
.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, foldhash::fast::FixedState>;
#[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(&define_id(&scope.hasher, group_name, 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,
}
#[expect(clippy::field_scoped_visibility_modifiers)]
#[derive(Debug, Default)]
pub(crate) struct ParseScope {
pub(crate) line_num: usize,
pub(crate) define_map: HashMap<u64, DefinedType, mut_set::NoHashBuildHasher>,
pub(crate) hasher: RandomState,
}
#[inline]
#[must_use]
pub(crate) fn define_id(hash_builder: &RandomState, group_name: &str, key: &str) -> u64 {
let mut hasher = hash_builder.build_hasher();
group_name.hash(&mut hasher);
key.hash(&mut hasher);
hasher.finish()
}
#[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(crate) 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(crate) 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(crate) 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 &'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 res = Self::parse(
vec.iter().map(|(n, s)| {
line_num += n;
s
}),
scope,
);
scope.line_num += line_num;
match res {
Ok(s) => Ok((input, Ok(s))),
Err(e) => Ok((input, Err((e, ComplexWrapper::collect(vec, scope))))),
}
}
#[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(crate) trait GroupFn {
#[inline]
fn post_parse_process(&mut self, _scope: &mut ParseScope) {}
}
#[expect(private_bounds)]
pub trait Group: Sized + GroupAttri {
#[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_utils::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)
}
#[doc(hidden)]
#[expect(clippy::field_scoped_visibility_modifiers)]
#[derive(Default, Debug, Clone)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct GroupComments(pub(crate) HashMap<u64, String, mut_set::NoHashBuildHasher>);
#[inline]
pub(crate) fn fmt_comment_liberty<T: Write, I: Indentation>(
comments: Option<&String>,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
if let Some(s) = comments {
if s.is_empty() {
Ok(())
} else {
let indent = f.indentation();
write!(f, "\n{indent}/* ")?;
join_fmt_no_quote(
s.split('\n'),
f,
|line, ff| write!(ff, "{line}"),
format!("\n{indent}** ").as_str(),
)?;
write!(f, " */")
}
} else {
Ok(())
}
}
#[inline]
pub(crate) fn fmt_library_beginning<T: Write, I: Indentation>(
comments: Option<&String>,
f: &mut CodeFormatter<'_, T, I>,
) -> core::fmt::Result {
if let Some(s) = comments {
if !s.is_empty() {
write!(f, "/* ")?;
join_fmt_no_quote(s.split('\n'), f, |line, ff| write!(ff, "{line}"), "\n** ")?;
return write!(f, " */");
}
}
write!(f, "/* Generated by {} {} */", env!("CARGO_PKG_NAME"), env!("CARGO_PKG_VERSION"),)
}
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 {
ComplexAttri::fmt_liberty(&self.0, key, f).and(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}}}")
}
}