synthez_core/parse/
attr.rs

1//! Batteries for parsing a single attribute.
2
3use crate::Spanning;
4
5/// Lookups for the standard Rust `#[doc]` attributes in the given
6/// [`syn::Attribute`]s and parses their text as [`String`] with a little
7/// normalization.
8///
9/// # Errors
10///
11/// - If parsing text from `#[doc]` attribute fails.
12/// - If `#[doc]` doesn't contain text.
13pub fn doc_string(
14    attrs: &[syn::Attribute],
15) -> syn::Result<Option<Spanning<String>>> {
16    let mut span = None;
17    let mut out = String::new();
18
19    for a in super::attrs::filter_by_name("doc", attrs) {
20        if let syn::Meta::NameValue(item) = &a.meta {
21            if let syn::Expr::Lit(expr) = &item.value {
22                if let syn::Lit::Str(lit) = &expr.lit {
23                    if span.is_none() {
24                        span = Some(lit.span());
25                    }
26
27                    let s = lit.value();
28                    let s = s.strip_prefix(' ').unwrap_or(&s).trim_end();
29                    if s.ends_with('\\') {
30                        out.push_str(s.trim_end_matches('\\'));
31                        out.push(' ');
32                    } else {
33                        out.push_str(s);
34                        out.push('\n');
35                    }
36                } else {
37                    return Err(syn::Error::new_spanned(
38                        &expr.lit,
39                        "`#[doc]` attribute can contain string literals only",
40                    ));
41                }
42            } else {
43                return Err(syn::Error::new_spanned(
44                    &item.value,
45                    "`#[doc]` attribute should be in `#[doc = value] format`",
46                ));
47            }
48        }
49    }
50    if !out.is_empty() {
51        out.truncate(out.trim_end().len());
52    }
53
54    Ok(span.map(|s| Spanning::new(out, s)))
55}
56
57/// Lookups for the standard Rust `#[doc]` attributes in the given
58/// [`syn::Attribute`]s and parses their text as [`syn::LitStr`] with a little
59/// normalization.
60///
61/// # Errors
62///
63/// - If parsing text from `#[doc]` attribute fails.
64/// - If `#[doc]` doesn't contain text.
65///
66/// [`syn::LitStr`]: struct@syn::LitStr
67pub fn doc(attrs: &[syn::Attribute]) -> syn::Result<Option<syn::LitStr>> {
68    doc_string(attrs).map(|opt| opt.map(Into::into))
69}