sphinx_rustdocgen/directives/
function_directive.rs1use 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#[derive(Clone, Debug)]
36pub struct FunctionDirective {
37 pub(crate) name: String,
39 pub(crate) ident: String,
41 pub(crate) options: Vec<DirectiveOption>,
43 pub(crate) content: Vec<String>,
45}
46
47fn 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 if !signature.generics.params.is_empty() {
71 nodes.extend(nodes_for_generics(&signature.generics));
72 }
73
74 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 signature.variadic.is_some() {
103 nodes.push(Node::Punctuation("..."));
104 }
105 else if !signature.inputs.is_empty() {
106 nodes.pop();
107 }
108 nodes.push(Node::Punctuation(")"));
110
111 nodes.extend(nodes_for_return_type(&signature.output));
113
114 if let Some(wc) = &signature.generics.where_clause {
116 nodes.extend(nodes_for_where_clause(wc))
117 }
118
119 nodes
120}
121
122macro_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 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 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 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 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 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 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 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 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}