use std::borrow::Cow;
use std::ops::Range;
use mdwright_document::Document;
use mdwright_document::LineIndex;
pub const DOCS_URL_DEFAULT: &str = "https://jcreinhold.github.io/mdwright";
#[must_use]
pub fn docs_url() -> Cow<'static, str> {
match std::env::var("MDWRIGHT_DOCS_URL") {
Ok(s) => Cow::Owned(s.trim_end_matches('/').to_owned()),
Err(_) => Cow::Borrowed(DOCS_URL_DEFAULT),
}
}
#[must_use]
pub fn rule_doc_url(rule_name: &str) -> String {
format!("{}/rules/{rule_name}.html", docs_url())
}
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Severity {
Error,
Warning,
Advisory,
}
impl Severity {
#[must_use]
pub fn as_str(self) -> &'static str {
match self {
Self::Error => "error",
Self::Warning => "warning",
Self::Advisory => "advisory",
}
}
}
#[derive(Clone, Debug)]
pub struct Snippet<'a> {
pub line_no: u32,
pub col_start: u32,
pub col_end: u32,
pub line_text: &'a str,
}
impl<'a> Snippet<'a> {
#[must_use]
pub fn from_span(line_index: &LineIndex, source: &'a str, span: &Range<usize>) -> Option<Self> {
let (line_no_usize, col_start_usize) = line_index.locate(source, span.start).ok()?;
let line_no = u32::try_from(line_no_usize).ok()?;
let col_start = u32::try_from(col_start_usize).ok()?;
let bounds = line_index.line_bounds(source, span.start)?;
let line_text = source.get(bounds.clone())?;
let end_on_line = span.end.min(bounds.end);
let after = source.get(bounds.start..end_on_line)?;
let after_cols = u32::try_from(after.chars().count().saturating_add(1)).ok()?;
let col_end = after_cols.max(col_start.saturating_add(1));
Some(Self {
line_no,
col_start,
col_end,
line_text,
})
}
}
#[derive(Clone, Debug)]
pub struct Diagnostic {
pub rule: Cow<'static, str>,
pub line: usize,
pub column: usize,
pub span: Range<usize>,
pub message: String,
pub fix: Option<Fix>,
pub advisory: bool,
}
#[derive(Clone, Debug)]
pub struct Fix {
pub replacement: String,
pub safe: bool,
}
impl Diagnostic {
#[must_use]
pub fn at(
doc: &Document,
byte_offset: usize,
local: Range<usize>,
message: String,
fix: Option<Fix>,
) -> Option<Self> {
let start = byte_offset.saturating_add(local.start);
let end = byte_offset.saturating_add(local.end);
let (line, column) = doc.line_index().locate(doc.source(), start).ok()?;
Some(Self {
rule: Cow::Borrowed(""),
line,
column,
span: start..end,
message,
fix,
advisory: false,
})
}
#[must_use]
pub fn suppress_via(&self) -> String {
format!("mdwright: allow {}", self.rule)
}
#[must_use]
pub fn severity(&self) -> Severity {
if self.advisory {
Severity::Advisory
} else {
Severity::Error
}
}
}