use crate::{
ast::{
join_fmt, CodeFormatter, GroupComments, GroupFn, Indentation, ParseScope, SimpleAttri,
},
Ctx,
};
use core::{
cmp::Ordering,
fmt::{self, Write},
hash,
str::FromStr,
};
use itertools::Itertools as _;
use std::collections::HashSet;
use strum_macros::{Display, EnumString};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Default, EnumString, Display)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum SdfEdgeType {
#[default]
#[strum(serialize = "noedge")]
Noedge,
#[strum(serialize = "start_edge")]
StartEdge,
#[strum(serialize = "end_edge")]
EndEdge,
#[strum(serialize = "both_edges")]
BothEdges,
}
crate::ast::impl_self_builder!(SdfEdgeType);
impl SimpleAttri for SdfEdgeType {
#[inline]
fn nom_parse<'a>(
i: &'a str,
scope: &mut ParseScope,
) -> crate::ast::SimpleParseRes<'a, Self> {
crate::ast::nom_parse_from_str(i, scope)
}
}
#[mut_set::derive::item(sort)]
#[derive(Debug, Clone, Default)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct IdVector {
#[id]
pub id: usize,
pub vec: Vec<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[derive(Display, EnumString)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum VariableType {
#[strum(serialize = "input_net_transition")]
InputNetTransition,
#[strum(serialize = "normalized_voltage")]
NormalizedVoltage,
#[strum(serialize = "total_output_net_capacitance")]
TotalOutputNetCapacitance,
#[strum(serialize = "related_out_total_output_net_capacitance")]
RelatedOutTotalOutputNetCapacitance,
#[strum(serialize = "constrained_pin_transition")]
ConstrainedPinTransition,
#[strum(serialize = "fanout_number")]
FanoutNumber,
#[strum(serialize = "fanout_pin_capacitance")]
FanoutPinCapacitance,
#[strum(serialize = "driver_slew")]
DriverSlew,
#[strum(serialize = "input_transition_time")]
InputTransitionTime,
}
crate::ast::impl_self_builder!(VariableType);
impl SimpleAttri for VariableType {
#[inline]
fn nom_parse<'a>(
i: &'a str,
scope: &mut ParseScope,
) -> crate::ast::SimpleParseRes<'a, Self> {
crate::ast::nom_parse_from_str(i, scope)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, PartialOrd, Ord, Hash)]
#[derive(Display, EnumString)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum SigmaType {
#[strum(serialize = "early")]
Early,
#[strum(serialize = "late")]
Late,
#[default]
#[strum(serialize = "early_and_late")]
EarlyAndLate,
}
crate::ast::impl_self_builder!(SigmaType);
impl SimpleAttri for SigmaType {
#[inline]
fn nom_parse<'a>(
i: &'a str,
scope: &mut ParseScope,
) -> crate::ast::SimpleParseRes<'a, Self> {
crate::ast::nom_parse_from_str(i, scope)
}
}
#[derive(Debug, Default, Clone, Eq, PartialEq)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct WordSet {
pub inner: HashSet<String, crate::ast::RandomState>,
}
impl fmt::Display for WordSet {
#[expect(clippy::unwrap_in_result)]
#[expect(clippy::unwrap_used)]
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner.len() {
0 => Ok(()),
1 => write!(f, "{}", self.inner.iter().next().unwrap()),
_ => join_fmt(self.inner.iter().sorted(), f, |s, ff| ff.write_str(s.as_str()), " "),
}
}
}
impl Ord for WordSet {
#[inline]
fn cmp(&self, other: &Self) -> Ordering {
match self.inner.len().cmp(&other.inner.len()) {
Ordering::Less => Ordering::Less,
Ordering::Greater => Ordering::Greater,
Ordering::Equal => self.inner.iter().sorted().cmp(other.inner.iter().sorted()),
}
}
}
#[expect(clippy::non_canonical_partial_ord_impl)]
impl PartialOrd for WordSet {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
match self.inner.len().cmp(&other.inner.len()) {
Ordering::Less => self.inner.is_subset(&other.inner).then_some(Ordering::Less),
Ordering::Greater => {
self.inner.is_superset(&other.inner).then_some(Ordering::Greater)
}
Ordering::Equal => {
self.inner.iter().sorted().partial_cmp(other.inner.iter().sorted())
}
}
}
}
crate::ast::impl_self_builder!(WordSet);
impl SimpleAttri for WordSet {
#[inline]
fn nom_parse<'a>(
i: &'a str,
scope: &mut ParseScope,
) -> crate::ast::SimpleParseRes<'a, Self> {
crate::ast::nom_parse_from_str(i, scope)
}
#[inline]
fn is_set(&self) -> bool {
!self.inner.is_empty()
}
#[inline]
fn fmt_self<T: Write, I: Indentation>(
&self,
f: &mut CodeFormatter<'_, T, I>,
) -> fmt::Result {
write!(f, "{self}")
}
}
impl hash::Hash for WordSet {
#[inline]
fn hash<H: hash::Hasher>(&self, state: &mut H) {
let mut sum = 0_u64;
for item in &self.inner {
let mut hasher = std::hash::DefaultHasher::new();
item.hash(&mut hasher);
sum = sum.wrapping_add(hash::Hasher::finish(&hasher));
}
state.write_u64(sum);
}
}
impl FromStr for WordSet {
type Err = fmt::Error;
#[inline]
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self {
inner: s
.split(' ')
.filter_map(|_s| if _s.is_empty() { None } else { Some(String::from(_s)) })
.collect(),
})
}
}
#[mut_set::derive::item(sort)]
#[derive(Debug, Clone)]
#[derive(liberty_macros::Group)]
#[derive(serde::Serialize, serde::Deserialize)]
#[serde(bound = "C::Other: serde::Serialize + serde::de::DeserializeOwned")]
pub struct DummyGroup<C: Ctx> {
#[size = 8]
#[liberty(name)]
#[id(borrow = "Option<&str>", check_fn = "mut_set::borrow_option!", with_ref = false)]
name: Option<String>,
#[size = 32]
#[liberty(comments)]
comments: GroupComments,
#[size = 0]
#[liberty(extra_ctx)]
pub extra_ctx: C::Other,
#[size = 40]
#[liberty(attributes)]
pub attributes: crate::ast::Attributes,
}
impl<C: Ctx> GroupFn for DummyGroup<C> {}
#[derive(Debug, Clone, Default)]
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Formula(pub String);
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(serde::Serialize, serde::Deserialize)]
pub enum NameList {
Name(String),
List(WordSet),
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum RefNameList<'a> {
Name(&'a str),
List(&'a WordSet),
}
impl<'a> From<&'a str> for RefNameList<'a> {
#[inline]
fn from(value: &'a str) -> Self {
Self::Name(value)
}
}
impl NameList {
#[inline]
#[must_use]
pub fn as_ref(&self) -> RefNameList<'_> {
match self {
Self::Name(s) => RefNameList::Name(s.as_str()),
Self::List(word_set) => RefNameList::List(word_set),
}
}
#[inline]
#[must_use]
pub fn contains(&self, name: &str) -> bool {
match self {
Self::Name(s) => s.as_str() == name,
Self::List(word_set) => word_set.inner.contains(name),
}
}
}
impl Default for NameList {
#[inline]
fn default() -> Self {
Self::Name(String::new())
}
}