use serde::Serialize;
use crate::model::SourceLocation;
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum CommentKind {
Line,
Block,
}
#[derive(Debug, Clone, Serialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum CommentPosition {
Leading,
Trailing,
Inner,
}
#[derive(Debug, Clone, Serialize)]
pub struct Comment {
pub text: String,
pub location: SourceLocation,
pub kind: CommentKind,
pub position: CommentPosition,
}
impl Comment {
pub fn new(
text: String,
location: SourceLocation,
kind: CommentKind,
position: CommentPosition,
) -> Self {
Self {
text,
location,
kind,
position,
}
}
pub fn contains(&self, pattern: &str) -> bool {
self.body().to_lowercase().contains(&pattern.to_lowercase())
}
pub fn body(&self) -> &str {
let t = self.text.trim();
match self.kind {
CommentKind::Line => t.strip_prefix("//").unwrap_or(t).trim_start(),
CommentKind::Block => t
.strip_prefix("/*")
.and_then(|s| s.strip_suffix("*/"))
.unwrap_or(t)
.trim(),
}
}
pub fn extract_ignore_rules(&self) -> Option<Vec<String>> {
let text = self.body();
if let Some(after_prefix) = text
.strip_prefix("gobject-linter-ignore:")
.or_else(|| text.strip_prefix("gobject-linter-ignore-next-line:"))
.or_else(|| text.strip_prefix("goblint-ignore:"))
.or_else(|| text.strip_prefix("goblint-ignore-next-line:"))
{
let rules: Vec<String> = after_prefix
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
return Some(rules);
}
None
}
pub fn is_gtk_doc(&self) -> bool {
matches!(self.kind, CommentKind::Block)
&& matches!(self.position, CommentPosition::Leading)
&& self.text.starts_with("/**")
}
pub fn is_marker(&self) -> bool {
let upper = self.body().to_uppercase();
upper.contains("TODO")
|| upper.contains("FIXME")
|| upper.contains("HACK")
|| upper.contains("XXX")
}
}