122 lines
3.5 KiB
Rust
122 lines
3.5 KiB
Rust
use std::fmt::{self, Write};
|
|
|
|
#[derive(Debug, Clone, Default)]
|
|
pub struct ClippyConfiguration {
|
|
pub name: String,
|
|
pub default: String,
|
|
pub lints: Vec<String>,
|
|
pub doc: String,
|
|
pub deprecation_reason: Option<&'static str>,
|
|
}
|
|
|
|
impl fmt::Display for ClippyConfiguration {
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
write!(f, "- `{}`: {}", self.name, self.doc)?;
|
|
if !self.default.is_empty() {
|
|
write!(f, " (default: `{}`)", self.default)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
impl ClippyConfiguration {
|
|
pub fn new(
|
|
name: &'static str,
|
|
default: String,
|
|
doc_comment: &'static str,
|
|
deprecation_reason: Option<&'static str>,
|
|
) -> Self {
|
|
let (mut lints, doc) = parse_config_field_doc(doc_comment)
|
|
.unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
|
|
|
|
lints.sort();
|
|
|
|
Self {
|
|
name: to_kebab(name),
|
|
lints,
|
|
doc,
|
|
default,
|
|
deprecation_reason,
|
|
}
|
|
}
|
|
|
|
pub fn to_markdown_paragraph(&self) -> String {
|
|
let mut out = format!(
|
|
"## `{}`\n{}\n\n",
|
|
self.name,
|
|
self.doc
|
|
.lines()
|
|
.map(|line| line.strip_prefix(" ").unwrap_or(line))
|
|
.collect::<Vec<_>>()
|
|
.join("\n"),
|
|
);
|
|
|
|
if !self.default.is_empty() {
|
|
write!(out, "**Default Value:** `{}`\n\n", self.default).unwrap();
|
|
}
|
|
|
|
write!(
|
|
out,
|
|
"---\n**Affected lints:**\n{}\n\n",
|
|
self.lints
|
|
.iter()
|
|
.map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
|
|
.map(|name| format!("* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
|
|
.collect::<Vec<_>>()
|
|
.join("\n"),
|
|
)
|
|
.unwrap();
|
|
|
|
out
|
|
}
|
|
|
|
pub fn to_markdown_link(&self) -> String {
|
|
const BOOK_CONFIGS_PATH: &str = "https://doc.rust-lang.org/clippy/lint_configuration.html";
|
|
format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name)
|
|
}
|
|
}
|
|
|
|
/// This parses the field documentation of the config struct.
|
|
///
|
|
/// ```rust, ignore
|
|
/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
|
|
/// ```
|
|
///
|
|
/// Would yield:
|
|
/// ```rust, ignore
|
|
/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
|
|
/// ```
|
|
fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
|
|
const DOC_START: &str = " Lint: ";
|
|
if doc_comment.starts_with(DOC_START)
|
|
&& let Some(split_pos) = doc_comment.find('.')
|
|
{
|
|
let mut doc_comment = doc_comment.to_string();
|
|
let mut documentation = doc_comment.split_off(split_pos);
|
|
|
|
// Extract lints
|
|
doc_comment.make_ascii_lowercase();
|
|
let lints: Vec<String> = doc_comment
|
|
.split_off(DOC_START.len())
|
|
.lines()
|
|
.next()
|
|
.unwrap()
|
|
.split(", ")
|
|
.map(str::to_string)
|
|
.collect();
|
|
|
|
// Format documentation correctly
|
|
// split off leading `.` from lint name list and indent for correct formatting
|
|
documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n ");
|
|
|
|
Some((lints, documentation))
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
|
|
fn to_kebab(config_name: &str) -> String {
|
|
config_name.replace('_', "-")
|
|
}
|