use indexmap::IndexMap;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum Severity {
Info,
Warning,
Error,
}
impl Severity {
#[must_use]
pub const fn label(self) -> &'static str {
match self {
Self::Info => "info",
Self::Warning => "warning",
Self::Error => "error",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Confidence {
High,
Medium,
Low,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum FixKind {
CssPropertyReplace {
property: String,
from: String,
to: String,
},
CssPropertyRemove {
property: String,
},
WrapElement {
tag: String,
},
AddAttribute {
name: String,
value: String,
},
Description {
text: String,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct Fix {
pub kind: FixKind,
pub description: String,
pub confidence: Confidence,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct Rect {
pub x: i32,
pub y: i32,
pub width: u32,
pub height: u32,
}
#[derive(
Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize, JsonSchema,
)]
pub struct ViewportKey(pub String);
impl ViewportKey {
#[must_use]
pub fn new(key: impl Into<String>) -> Self {
Self(key.into())
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
pub struct RunId(pub String);
impl RunId {
#[must_use]
pub fn new(hash: impl Into<String>) -> Self {
Self(hash.into())
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct Violation {
pub rule_id: String,
pub severity: Severity,
pub message: String,
pub selector: String,
pub viewport: ViewportKey,
pub rect: Option<Rect>,
pub dom_order: u64,
pub fix: Option<Fix>,
pub doc_url: String,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub metadata: IndexMap<String, serde_json::Value>,
}
impl Violation {
#[must_use]
pub fn sort_key(&self) -> (&str, &str, &str, u64) {
(
self.rule_id.as_str(),
self.viewport.as_str(),
self.selector.as_str(),
self.dom_order,
)
}
}
#[derive(Debug)]
pub struct ViolationSink<'a> {
buffer: &'a mut Vec<Violation>,
}
impl<'a> ViolationSink<'a> {
#[must_use]
pub fn new(buffer: &'a mut Vec<Violation>) -> Self {
Self { buffer }
}
pub fn push(&mut self, violation: Violation) {
self.buffer.push(violation);
}
#[must_use]
pub fn len(&self) -> usize {
self.buffer.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.buffer.is_empty()
}
}