sphinx_rustdocgen/directives/
variable_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:variable`` directive
18
19use syn::{
20    Fields,
21    ForeignItemStatic,
22    ImplItemConst,
23    ItemConst,
24    ItemStatic,
25    TraitItemConst,
26    Visibility,
27};
28
29use crate::directives::directive_options::{DirectiveOption, DirectiveVisibility, IndexEntryType};
30use crate::directives::{extract_doc_from_attrs, Directive};
31use crate::formats::{MdContent, MdDirective, RstContent, RstDirective};
32use crate::nodes::{nodes_for_type, Node};
33
34/// Struct to hold the data for documenting variables and struct fields.
35#[derive(Clone, Debug)]
36pub struct VariableDirective {
37    /// The Rust path of the variable or field.
38    pub(crate) name: String,
39    /// The identifier of the variable or field.
40    pub(crate) ident: String,
41    /// The options for the directive.
42    pub(crate) options: Vec<DirectiveOption>,
43    /// The content of the directive.
44    pub(crate) content: Vec<String>,
45}
46
47/// DRY macro to create :rust:struct:`VariableDirective` from various ``syn``
48/// structs.
49macro_rules! var_from_item {
50    ($item:expr, $parent_path:expr, $vis:expr, $inherited:expr, $prefix:expr, $index:expr) => {{
51        let name = format!("{}::{}", $parent_path, &$item.ident);
52        let mut nodes = vec![
53            Node::Keyword($prefix),
54            Node::Space,
55            Node::Name($item.ident.to_string()),
56            Node::Punctuation(": "),
57        ];
58        nodes.extend(nodes_for_type(&$item.ty));
59
60        let options = vec![
61            DirectiveOption::Index($index),
62            DirectiveOption::Vis(DirectiveVisibility::effective_visibility(&$vis, $inherited)),
63            DirectiveOption::Toc(format!("{} {}", $prefix, &$item.ident)),
64            DirectiveOption::Layout(nodes),
65        ];
66
67        VariableDirective {
68            name,
69            ident: $item.ident.to_string(),
70            options,
71            content: extract_doc_from_attrs(&$item.attrs),
72        }
73    }};
74}
75
76impl VariableDirective {
77    const DIRECTIVE_NAME: &'static str = "variable";
78
79    /// Create variable directives from the fields of a struct or enum variant.
80    pub(crate) fn from_fields(
81        parent_path: &str,
82        fields: &Fields,
83        inherited_visibility: &Option<&Visibility>,
84        index_entry_type: IndexEntryType,
85    ) -> Vec<Self> {
86        if let Fields::Named(named_fields) = fields {
87            named_fields
88                .named
89                .iter()
90                .map(|f| {
91                    let mut nodes = vec![
92                        Node::Name(f.ident.as_ref().unwrap().to_string()),
93                        Node::Punctuation(": "),
94                    ];
95                    nodes.extend(nodes_for_type(&f.ty));
96                    let options = vec![
97                        DirectiveOption::Index(index_entry_type),
98                        DirectiveOption::Vis(DirectiveVisibility::effective_visibility(
99                            &f.vis,
100                            inherited_visibility,
101                        )),
102                        DirectiveOption::Toc(format!("{}", f.ident.as_ref().unwrap())),
103                        DirectiveOption::Layout(nodes),
104                    ];
105
106                    VariableDirective {
107                        name: format!("{}::{}", parent_path, f.ident.as_ref().unwrap()),
108                        ident: f.ident.as_ref().unwrap().to_string(),
109                        options,
110                        content: extract_doc_from_attrs(&f.attrs),
111                    }
112                })
113                .collect()
114        }
115        else {
116            Vec::new()
117        }
118    }
119
120    /// Create a variable directive for a static variable.
121    pub(crate) fn from_static(parent_path: &str, item: &ItemStatic) -> Directive {
122        Directive::Variable(var_from_item!(
123            item,
124            parent_path,
125            item.vis,
126            &None,
127            "static",
128            IndexEntryType::Normal
129        ))
130    }
131
132    /// Create a variable directive for a const variable.
133    pub(crate) fn from_const(parent_path: &str, item: &ItemConst) -> Directive {
134        Directive::Variable(var_from_item!(
135            item,
136            parent_path,
137            item.vis,
138            &None,
139            "const",
140            IndexEntryType::Normal
141        ))
142    }
143
144    /// Create a variable directive for a const variable within an impl block.
145    pub(crate) fn from_impl_const(
146        parent_path: &str,
147        item: &ImplItemConst,
148        inherited_visibility: &Option<&Visibility>,
149    ) -> Directive {
150        Directive::Variable(var_from_item!(
151            item,
152            parent_path,
153            item.vis,
154            inherited_visibility,
155            "const",
156            IndexEntryType::None
157        ))
158    }
159
160    /// Create a variable directive for a const variable within a trait
161    /// definition.
162    pub(crate) fn from_trait_const(
163        parent_path: &str,
164        item: &TraitItemConst,
165        inherited_visibility: &Option<&Visibility>,
166    ) -> Directive {
167        Directive::Variable(var_from_item!(
168            item,
169            parent_path,
170            Visibility::Inherited,
171            inherited_visibility,
172            "const",
173            IndexEntryType::None
174        ))
175    }
176
177    /// Create a variable directive for a foreign static variable.
178    pub(crate) fn from_extern_static(parent_path: &str, item: &ForeignItemStatic) -> Directive {
179        Directive::Variable(var_from_item!(
180            item,
181            parent_path,
182            item.vis,
183            &None,
184            "extern static",
185            IndexEntryType::Normal
186        ))
187    }
188
189    /// Return the visibility of this directive.
190    pub(crate) fn directive_visibility(&self) -> &DirectiveVisibility {
191        if let DirectiveOption::Vis(v) = &self.options[1] {
192            return v;
193        }
194        unreachable!("Variable: order of options changed")
195    }
196
197    /// Change the parent directive of the variable.
198    pub(crate) fn change_parent(&mut self, new_parent: &str) {
199        self.name = format!("{new_parent}::{}", self.ident);
200    }
201}
202
203impl RstDirective for VariableDirective {
204    // noinspection DuplicatedCode
205    fn get_rst_text(self, level: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
206        if self.directive_visibility() > max_visibility {
207            return vec![];
208        }
209        let content_indent = Self::make_indent(level + 1);
210
211        let mut text =
212            Self::make_rst_header(Self::DIRECTIVE_NAME, &self.name, &self.options, level);
213        text.extend(self.content.get_rst_text(&content_indent));
214
215        text
216    }
217}
218
219impl MdDirective for VariableDirective {
220    // noinspection DuplicatedCode
221    fn get_md_text(self, fence_size: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
222        if self.directive_visibility() > max_visibility {
223            return vec![];
224        }
225        let fence = Self::make_fence(fence_size);
226
227        let mut text =
228            Self::make_md_header(Self::DIRECTIVE_NAME, &self.name, &self.options, &fence);
229        text.extend(self.content.get_md_text());
230
231        text.push(fence);
232        text
233    }
234}