sphinx_rustdocgen/directives/
function_directive.rs

1// sphinxcontrib_rust - Sphinx extension for the Rust programming language
2// Copyright (C) 2024  Munir Contractor
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17//! Implementation of the ``rust:function`` directive
18
19use syn::{FnArg, ForeignItemFn, ImplItemFn, ItemFn, Signature, TraitItemFn, Visibility};
20
21use crate::directives::directive_options::{DirectiveOption, DirectiveVisibility, IndexEntryType};
22use crate::directives::{extract_doc_from_attrs, Directive};
23use crate::formats::{MdContent, MdDirective, RstContent, RstDirective};
24use crate::nodes::{
25    nodes_for_abi_opt,
26    nodes_for_generics,
27    nodes_for_pat_type,
28    nodes_for_return_type,
29    nodes_for_type,
30    nodes_for_where_clause,
31    Node,
32};
33
34/// Struct to hold data for documenting a function.
35#[derive(Clone, Debug)]
36pub struct FunctionDirective {
37    /// The full Rust path of the function.
38    pub(crate) name: String,
39    /// The identifier of the function.
40    pub(crate) ident: String,
41    /// The directive options to use.
42    pub(crate) options: Vec<DirectiveOption>,
43    /// The docstring for the function.
44    pub(crate) content: Vec<String>,
45}
46
47/// Generate docutils node layout for the function from its signature
48fn nodes_for_fn_signature(signature: &Signature) -> Vec<Node> {
49    let mut nodes = Vec::new();
50    if signature.constness.is_some() {
51        nodes.push(Node::Keyword("const"));
52        nodes.push(Node::Space);
53    }
54    if signature.asyncness.is_some() {
55        nodes.push(Node::Keyword("async"));
56        nodes.push(Node::Space);
57    }
58    if signature.unsafety.is_some() {
59        nodes.push(Node::Keyword("unsafe"));
60        nodes.push(Node::Space);
61    }
62    nodes.extend(nodes_for_abi_opt(&signature.abi));
63    nodes.extend_from_slice(&[
64        Node::Keyword("fn"),
65        Node::Space,
66        Node::Name(signature.ident.to_string()),
67    ]);
68
69    // Nodes for generics
70    if !signature.generics.params.is_empty() {
71        nodes.extend(nodes_for_generics(&signature.generics));
72    }
73
74    // Nodes for arguments
75    nodes.push(Node::Punctuation("("));
76    for arg in &signature.inputs {
77        match arg {
78            FnArg::Receiver(r) => {
79                if let Some((_, lt_opt)) = &r.reference {
80                    nodes.push(Node::Punctuation("&"));
81                    if let Some(lt) = lt_opt {
82                        nodes.push(Node::Lifetime(lt.to_string()));
83                        nodes.push(Node::Space);
84                    }
85                }
86                if r.mutability.is_some() {
87                    nodes.push(Node::Keyword("mut"));
88                    nodes.push(Node::Space);
89                }
90                nodes.push(Node::Keyword("self"));
91                if r.colon_token.is_some() {
92                    nodes.push(Node::Punctuation(":"));
93                    nodes.extend(nodes_for_type(&r.ty));
94                }
95            }
96            FnArg::Typed(t) => nodes.extend(nodes_for_pat_type(t)),
97        }
98        nodes.push(Node::Punctuation(", "));
99    }
100
101    // If variadic, add the "..." otherwise remove the last ", ".
102    if signature.variadic.is_some() {
103        nodes.push(Node::Punctuation("..."));
104    }
105    else if !signature.inputs.is_empty() {
106        nodes.pop();
107    }
108    // Closing parenthesis
109    nodes.push(Node::Punctuation(")"));
110
111    // Return type
112    nodes.extend(nodes_for_return_type(&signature.output));
113
114    // Nodes for where clause
115    if let Some(wc) = &signature.generics.where_clause {
116        nodes.extend(nodes_for_where_clause(wc))
117    }
118
119    nodes
120}
121
122/// DRY macro to parse the different item types.
123macro_rules! func_from_item {
124    ($parent_path:expr, $item:expr, $vis:expr, $inherited:expr, $index:expr) => {{
125        let sig = &$item.sig;
126        let options = vec![
127            DirectiveOption::Index($index),
128            DirectiveOption::Vis(DirectiveVisibility::effective_visibility(&$vis, $inherited)),
129            DirectiveOption::Layout(nodes_for_fn_signature(sig)),
130        ];
131
132        FunctionDirective {
133            name: format!("{}::{}", $parent_path, $item.sig.ident),
134            ident: $item.sig.ident.to_string(),
135            options,
136            content: extract_doc_from_attrs(&$item.attrs),
137        }
138    }};
139}
140
141impl FunctionDirective {
142    const DIRECTIVE_NAME: &'static str = "function";
143
144    /// Create a new ``Directive::Function`` from a ``syn::ItemFn``.
145    ///
146    /// Args:
147    ///     :parent_path: The full path of the module the function is in.
148    ///     :item: The ``syn::ItemFn`` reference to parse.
149    ///     :inherited_visibility: The visibility of the parent module.
150    ///
151    /// Returns:
152    ///     A new ``Directive::Function`` value, which contains the parsed
153    ///     ``FunctionDirective`` in it.
154    pub(crate) fn from_item(parent_path: &str, item: &ItemFn) -> Directive {
155        Directive::Function(func_from_item!(
156            parent_path,
157            item,
158            item.vis,
159            &None,
160            IndexEntryType::Normal
161        ))
162    }
163
164    /// Create a new ``Directive::Function`` from a ``syn::ImplItemFn``.
165    ///
166    /// Args:
167    ///     :parent_path: The full path of the impl block the function is in.
168    ///     :item: The ``syn::ImplItemFn`` reference to parse.
169    ///     :inherited_visibility: The visibility of the impl block.
170    ///
171    /// Returns:
172    ///     A new ``Directive::Function`` value, which contains the parsed
173    ///     ``FunctionDirective`` in it.
174    pub(crate) fn from_impl_item(
175        parent_path: &str,
176        item: &ImplItemFn,
177        inherited_visibility: &Option<&Visibility>,
178    ) -> Directive {
179        Directive::Function(func_from_item!(
180            parent_path,
181            item,
182            item.vis,
183            inherited_visibility,
184            IndexEntryType::None
185        ))
186    }
187
188    /// Create a new ``Directive::Function`` from a ``syn::TraitItemFn``.
189    ///
190    /// Args:
191    ///     :parent_path: The full path of the trait the function is in.
192    ///     :item: The ``syn::TraitItemFn`` reference to parse.
193    ///     :inherited_visibility: The visibility of the trait.
194    ///
195    /// Returns:
196    ///     A new ``Directive::Function`` value, which contains the parsed
197    ///     ``FunctionDirective`` in it.
198    pub(crate) fn from_trait_item(
199        parent_path: &str,
200        item: &TraitItemFn,
201        inherited_visibility: &Option<&Visibility>,
202    ) -> Directive {
203        Directive::Function(func_from_item!(
204            parent_path,
205            item,
206            &Visibility::Inherited,
207            inherited_visibility,
208            IndexEntryType::SubEntry
209        ))
210    }
211
212    /// Create a new ``Directive::Function`` from a ``syn::ForeignItemFn``.
213    ///
214    /// Args:
215    ///     :parent_path: The full path of the trait the function is in.
216    ///     :item: The ``syn::ForeignItemFn`` reference to parse.
217    ///     :inherited_visibility: The visibility of the parent module.
218    ///
219    /// Returns:
220    ///     A new ``Directive::Function`` value, which contains the parsed
221    ///     ``FunctionDirective`` in it.
222    pub(crate) fn from_extern(parent_path: &str, item: &ForeignItemFn) -> Directive {
223        Directive::Function(func_from_item!(
224            parent_path,
225            item,
226            item.vis,
227            &None,
228            IndexEntryType::Normal
229        ))
230    }
231
232    /// Return the visibility of this directive.
233    pub(crate) fn directive_visibility(&self) -> &DirectiveVisibility {
234        if let DirectiveOption::Vis(v) = &self.options[1] {
235            return v;
236        }
237        unreachable!("Function: order of options changed")
238    }
239
240    /// Change the parent directive of the function.
241    pub(crate) fn change_parent(&mut self, new_parent: &str) {
242        self.name = format!("{new_parent}::{}", self.ident)
243    }
244}
245
246impl RstDirective for FunctionDirective {
247    // noinspection DuplicatedCode
248    fn get_rst_text(self, level: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
249        if self.directive_visibility() > max_visibility {
250            return vec![];
251        }
252        let content_indent = Self::make_content_indent(level);
253
254        let mut text =
255            Self::make_rst_header(Self::DIRECTIVE_NAME, &self.name, &self.options, level);
256        text.extend(self.content.get_rst_text(&content_indent));
257
258        text
259    }
260}
261
262impl MdDirective for FunctionDirective {
263    // noinspection DuplicatedCode
264    fn get_md_text(self, fence_size: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
265        if self.directive_visibility() > max_visibility {
266            return vec![];
267        }
268        let fence = Self::make_fence(fence_size);
269
270        let mut text =
271            Self::make_md_header(Self::DIRECTIVE_NAME, &self.name, &self.options, &fence);
272        text.extend(self.content.get_md_text());
273        text.push(fence);
274
275        text
276    }
277}