sphinx_rustdocgen/directives/
type_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:type`` directive
18
19use syn::{ForeignItemType, ImplItemType, ItemType, TraitItemType, 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::{nodes_for_generics, nodes_for_where_clause, Node};
25
26/// Struct to hold the data for documenting type definitions.
27#[derive(Clone, Debug)]
28pub struct TypeDirective {
29    /// The Rust path of the type.
30    pub(crate) name: String,
31    /// The identifier of the type.
32    pub(crate) ident: String,
33    /// The options of the directive.
34    pub(crate) options: Vec<DirectiveOption>,
35    /// The content of the directive.
36    pub(crate) content: Vec<String>,
37}
38
39/// DRY macro to create :rust:struct:`TypeDirective` from various syn structs.
40macro_rules! type_from_item {
41    ($parent_path:expr, $item:expr, $vis:expr, $inherited:expr, $index:expr) => {{
42        let name = format!("{}::{}", $parent_path, &$item.ident);
43
44        let mut nodes = vec![
45            Node::Keyword(TypeDirective::DIRECTIVE_NAME),
46            Node::Space,
47            Node::Name($item.ident.to_string()),
48        ];
49        nodes.extend(nodes_for_generics(&$item.generics));
50        if let Some(wc) = &$item.generics.where_clause {
51            nodes.extend(nodes_for_where_clause(wc));
52        }
53
54        let options = vec![
55            DirectiveOption::Index($index),
56            DirectiveOption::Vis(DirectiveVisibility::effective_visibility(&$vis, $inherited)),
57            DirectiveOption::Layout(nodes),
58        ];
59
60        TypeDirective {
61            name,
62            ident: $item.ident.to_string(),
63            options,
64            content: extract_doc_from_attrs(&$item.attrs),
65        }
66    }};
67}
68
69impl TypeDirective {
70    const DIRECTIVE_NAME: &'static str = "type";
71
72    /// Create a type directive from a type definition.
73    pub(crate) fn from_item(parent_path: &str, item: &ItemType) -> Directive {
74        Directive::Type(type_from_item!(
75            parent_path,
76            item,
77            item.vis,
78            &None,
79            IndexEntryType::Normal
80        ))
81    }
82
83    /// Create a type directive from a type definition within an impl block.
84    pub(crate) fn from_impl_item(
85        parent_path: &str,
86        item: &ImplItemType,
87        inherited_visibility: &Option<&Visibility>,
88    ) -> Directive {
89        Directive::Type(type_from_item!(
90            parent_path,
91            item,
92            item.vis,
93            inherited_visibility,
94            IndexEntryType::None
95        ))
96    }
97
98    /// Create a type directive from a type definition within a trait
99    /// definition.
100    pub(crate) fn from_trait_item(
101        parent_path: &str,
102        item: &TraitItemType,
103        inherited_visibility: &Option<&Visibility>,
104    ) -> Directive {
105        Directive::Type(type_from_item!(
106            parent_path,
107            item,
108            Visibility::Inherited,
109            inherited_visibility,
110            IndexEntryType::SubEntry
111        ))
112    }
113
114    /// Create a type directive from a foreign type definition.
115    pub(crate) fn from_extern(parent_path: &str, item: &ForeignItemType) -> Directive {
116        Directive::Type(type_from_item!(
117            parent_path,
118            item,
119            item.vis,
120            &None,
121            IndexEntryType::Normal
122        ))
123    }
124
125    /// Return the visibility of this directive.
126    pub(crate) fn directive_visibility(&self) -> &DirectiveVisibility {
127        if let DirectiveOption::Vis(v) = &self.options[1] {
128            return v;
129        }
130        unreachable!("Type: order of options changed")
131    }
132
133    /// Change the parent module of the type.
134    pub(crate) fn change_parent(&mut self, new_parent: &str) {
135        self.name = format!("{new_parent}::{}", self.ident);
136    }
137}
138
139impl RstDirective for TypeDirective {
140    // noinspection DuplicatedCode
141    fn get_rst_text(self, level: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
142        if self.directive_visibility() > max_visibility {
143            return vec![];
144        }
145        let content_indent = Self::make_content_indent(level);
146
147        let mut text =
148            Self::make_rst_header(Self::DIRECTIVE_NAME, &self.name, &self.options, level);
149        text.extend(self.content.get_rst_text(&content_indent));
150
151        text
152    }
153}
154
155impl MdDirective for TypeDirective {
156    // noinspection DuplicatedCode
157    fn get_md_text(self, fence_size: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
158        if self.directive_visibility() > max_visibility {
159            return vec![];
160        }
161        let fence = Self::make_fence(fence_size);
162
163        let mut text =
164            Self::make_md_header(Self::DIRECTIVE_NAME, &self.name, &self.options, &fence);
165        text.extend(self.content.get_md_text());
166
167        text.push(fence);
168        text
169    }
170}