wowlab_tidy/languages/rust/rules/docs/
support.rs1use 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}