#![allow(clippy::doc_markdown)]
use std::path::Path;
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::output::numfmt::MessageMetric;
pub const TOOL_ID: &str = "big-code-analysis";
pub(crate) fn warn_non_utf8_path<'a>(format: &str, path: &'a Path) -> Option<&'a str> {
if let Some(s) = path.to_str() {
Some(s)
} else {
eprintln!(
"Warning: skipping non-UTF-8 path in {format} output: {}",
path.display()
);
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum Severity {
#[default]
Warning,
Error,
}
impl Severity {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::Warning => "warning",
Self::Error => "error",
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct OffenderRecord {
pub path: PathBuf,
pub function: Option<String>,
pub start_line: u32,
pub end_line: u32,
pub start_col: Option<u32>,
pub metric: String,
pub value: f64,
pub limit: f64,
pub severity: Severity,
}
impl OffenderRecord {
#[must_use]
pub fn default_message(&self) -> String {
format!(
"{} {} exceeds limit {}",
self.metric,
MessageMetric(self.value),
MessageMetric(self.limit),
)
}
}
#[cfg(test)]
#[allow(
clippy::float_cmp,
clippy::cast_precision_loss,
clippy::cast_possible_truncation,
clippy::cast_sign_loss,
clippy::similar_names,
clippy::doc_markdown,
clippy::needless_raw_string_hashes,
clippy::too_many_lines
)]
mod tests {
use super::*;
#[test]
fn severity_default_is_warning() {
assert_eq!(Severity::default(), Severity::Warning);
}
#[test]
fn severity_as_str_lowercase() {
assert_eq!(Severity::Warning.as_str(), "warning");
assert_eq!(Severity::Error.as_str(), "error");
}
#[test]
fn default_message_renders_integral_value() {
let r = OffenderRecord {
path: PathBuf::from("a.rs"),
function: Some("f".into()),
start_line: 1,
end_line: 2,
start_col: None,
metric: "cyclomatic".into(),
value: 17.0,
limit: 15.0,
severity: Severity::Warning,
};
assert_eq!(r.default_message(), "cyclomatic 17 exceeds limit 15");
}
#[test]
fn default_message_renders_fractional_value() {
let r = OffenderRecord {
path: PathBuf::from("a.rs"),
function: None,
start_line: 1,
end_line: 1,
start_col: None,
metric: "halstead.volume".into(),
value: 12.5,
limit: 10.0,
severity: Severity::Error,
};
assert_eq!(r.default_message(), "halstead.volume 12.5 exceeds limit 10");
}
#[test]
fn default_message_renders_non_finite_values() {
let mut r = OffenderRecord {
path: PathBuf::from("a.rs"),
function: None,
start_line: 1,
end_line: 1,
start_col: None,
metric: "halstead.volume".into(),
value: f64::NAN,
limit: 10.0,
severity: Severity::Warning,
};
assert_eq!(r.default_message(), "halstead.volume NaN exceeds limit 10");
r.value = f64::INFINITY;
assert_eq!(r.default_message(), "halstead.volume inf exceeds limit 10");
r.value = f64::NEG_INFINITY;
assert_eq!(r.default_message(), "halstead.volume -inf exceeds limit 10");
}
}