use std::fmt;
use std::str::FromStr;
#[non_exhaustive]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub enum Metric {
Cognitive,
Cyclomatic,
Halstead,
Loc,
Nom,
Tokens,
NArgs,
Exit,
Abc,
Npm,
Npa,
Mi,
Wmc,
}
impl Metric {
#[inline]
const fn bit(self) -> u32 {
1 << (self as u32)
}
#[must_use]
pub const fn dependencies(self) -> &'static [Metric] {
match self {
Self::Mi => &[Self::Loc, Self::Cyclomatic, Self::Halstead],
Self::Wmc => &[Self::Cyclomatic, Self::Nom],
_ => &[],
}
}
pub const NAMES: &'static [&'static str] = &[
"abc",
"cognitive",
"cyclomatic",
"halstead",
"loc",
"mi",
"nargs",
"nexits",
"nom",
"npa",
"npm",
"tokens",
"wmc",
];
}
impl fmt::Display for Metric {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = match self {
Self::Cognitive => "cognitive",
Self::Cyclomatic => "cyclomatic",
Self::Halstead => "halstead",
Self::Loc => "loc",
Self::Nom => "nom",
Self::Tokens => "tokens",
Self::NArgs => "nargs",
Self::Exit => "exit",
Self::Abc => "abc",
Self::Npm => "npm",
Self::Npa => "npa",
Self::Mi => "mi",
Self::Wmc => "wmc",
};
f.write_str(s)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ParseMetricError(String);
impl fmt::Display for ParseMetricError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "unknown metric: {}", self.0)
}
}
impl std::error::Error for ParseMetricError {}
impl FromStr for Metric {
type Err = ParseMetricError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"cognitive" => Ok(Self::Cognitive),
"cyclomatic" => Ok(Self::Cyclomatic),
"halstead" => Ok(Self::Halstead),
"loc" => Ok(Self::Loc),
"nom" => Ok(Self::Nom),
"tokens" => Ok(Self::Tokens),
"nargs" => Ok(Self::NArgs),
"exit" | "nexits" => Ok(Self::Exit),
"abc" => Ok(Self::Abc),
"npm" => Ok(Self::Npm),
"npa" => Ok(Self::Npa),
"mi" => Ok(Self::Mi),
"wmc" => Ok(Self::Wmc),
_ => Err(ParseMetricError(s.to_owned())),
}
}
}
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
pub struct MetricSet(u32);
impl MetricSet {
const ALL_BITS: u32 = Metric::Cognitive.bit()
| Metric::Cyclomatic.bit()
| Metric::Halstead.bit()
| Metric::Loc.bit()
| Metric::Nom.bit()
| Metric::Tokens.bit()
| Metric::NArgs.bit()
| Metric::Exit.bit()
| Metric::Abc.bit()
| Metric::Npm.bit()
| Metric::Npa.bit()
| Metric::Mi.bit()
| Metric::Wmc.bit();
#[inline]
#[must_use]
pub const fn empty() -> Self {
Self(0)
}
#[inline]
#[must_use]
pub const fn all() -> Self {
Self(Self::ALL_BITS)
}
#[inline]
#[must_use]
pub const fn contains(self, metric: Metric) -> bool {
(self.0 & metric.bit()) != 0
}
#[inline]
#[must_use]
pub const fn with(self, metric: Metric) -> Self {
Self(self.0 | metric.bit())
}
#[inline]
#[must_use]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
#[inline]
pub fn insert(&mut self, metric: Metric) {
self.0 |= metric.bit();
}
#[must_use]
pub fn from_slice_with_deps(metrics: &[Metric]) -> Self {
let mut set = Self::empty();
let mut worklist: Vec<Metric> = metrics.to_vec();
while let Some(m) = worklist.pop() {
if set.contains(m) {
continue;
}
set.insert(m);
for &dep in m.dependencies() {
if !set.contains(dep) {
worklist.push(dep);
}
}
}
set
}
}
impl Default for MetricSet {
#[inline]
fn default() -> Self {
Self::all()
}
}
#[cfg(test)]
#[path = "metric_set_tests.rs"]
mod tests;