Skip to main content

wowlab_tidy/languages/rust/rules/docs/
support.rs

1use ra_ap_syntax::{
2    AstNode,
3    ast::{self, HasAttrs, HasDocComments, LiteralKind},
4};
5
6pub(super) fn is_item_or_impl_fn(function: &ast::Fn) -> bool {
7    !function.syntax().ancestors().skip(1).any(|ancestor| {
8        ast::Trait::can_cast(ancestor.kind()) || ast::ExternBlock::can_cast(ancestor.kind())
9    })
10}
11
12pub(super) fn doc_lines(function: &ast::Fn) -> Vec<String> {
13    let mut lines: Vec<String> = function
14        .doc_comments()
15        .filter_map(|comment| comment.doc_comment().map(|(text, _)| text.to_owned()))
16        .collect();
17
18    lines.extend(function.attrs().filter_map(|attr| doc_attr_text(&attr)));
19
20    lines
21}
22
23fn doc_attr_text(attr: &ast::Attr) -> Option<String> {
24    let ast::Meta::KeyValueMeta(meta) = attr.meta()? else {
25        return None;
26    };
27
28    let name = meta
29        .path()
30        .and_then(|path| path.segment())
31        .and_then(|segment| segment.name_ref());
32
33    if name.is_none_or(|name| name.text() != "doc") {
34        return None;
35    }
36
37    let ast::Expr::Literal(literal) = meta.expr()? else {
38        return None;
39    };
40    let LiteralKind::String(text) = literal.kind() else {
41        return None;
42    };
43
44    text.value().ok().map(std::borrow::Cow::into_owned)
45}
46
47pub(super) fn has_heading(doc_lines: &[String], name: &str) -> bool {
48    doc_lines
49        .iter()
50        .flat_map(|chunk| chunk.lines())
51        .any(|line| {
52            let trimmed = line.trim();
53            let stripped = trimmed.trim_start_matches('#');
54
55            stripped.len() < trimmed.len() && stripped.trim() == name
56        })
57}