sphinx_rustdocgen/directives/
trait_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:trait`` directive
18
19use syn::{ItemTrait, ItemTraitAlias};
20
21use crate::directives::directive_options::{DirectiveOption, DirectiveVisibility, IndexEntryType};
22use crate::directives::{extract_doc_from_attrs, order_items, Directive, ImplDirective};
23use crate::formats::{MdContent, MdDirective, RstContent, RstDirective};
24use crate::nodes::{nodes_for_generics, nodes_for_where_clause, Node};
25
26/// Struct to hold details for documenting a trait.
27#[derive(Clone, Debug)]
28pub struct TraitDirective {
29    /// The full Rust path of the trait, used as the name of the directive.
30    pub(crate) name: String,
31    /// The identifier for the trait.
32    pub(crate) ident: String,
33    /// The directive options to use.
34    pub(crate) options: Vec<DirectiveOption>,
35    /// The docstring for the trait.
36    pub(crate) content: Vec<String>,
37    /// Items within the trait.
38    pub(crate) items: Vec<Directive>,
39    /// Generic impls of the trait.
40    pub(crate) impls: Vec<ImplDirective>,
41}
42
43impl TraitDirective {
44    const DIRECTIVE_NAME: &'static str = "trait";
45
46    /// Create a trait directive from the trait definition.
47    // noinspection DuplicatedCode
48    pub(crate) fn from_item(parent_path: &str, item: &ItemTrait) -> Directive {
49        let name = format!("{}::{}", parent_path, item.ident);
50
51        let mut nodes = vec![];
52        if item.unsafety.is_some() {
53            nodes.push(Node::Keyword("unsafe"));
54            nodes.push(Node::Space);
55        }
56        nodes.push(Node::Keyword(Self::DIRECTIVE_NAME));
57        nodes.push(Node::Space);
58        nodes.push(Node::Name(item.ident.to_string()));
59        nodes.extend(nodes_for_generics(&item.generics));
60        if let Some(wc) = &item.generics.where_clause {
61            nodes.extend(nodes_for_where_clause(wc));
62        }
63
64        let options = vec![
65            DirectiveOption::Index(IndexEntryType::WithSubEntries),
66            DirectiveOption::Vis(DirectiveVisibility::from(&item.vis)),
67            DirectiveOption::Layout(nodes),
68        ];
69
70        let items = Directive::from_trait_items(&name, item.items.iter(), &Some(&item.vis));
71
72        Directive::Trait(TraitDirective {
73            name,
74            ident: item.ident.to_string(),
75            options,
76            content: extract_doc_from_attrs(&item.attrs),
77            items,
78            impls: vec![],
79        })
80    }
81
82    // noinspection DuplicatedCode
83    /// Create a trait directive from a trait alias.
84    pub(crate) fn from_alias(parent_path: &str, alias: &ItemTraitAlias) -> Directive {
85        let name = format!("{}::{}", parent_path, alias.ident);
86
87        let mut nodes = vec![
88            Node::Keyword(Self::DIRECTIVE_NAME),
89            Node::Space,
90            Node::Name(alias.ident.to_string()),
91        ];
92        nodes.extend(nodes_for_generics(&alias.generics));
93        if let Some(wc) = &alias.generics.where_clause {
94            nodes.extend(nodes_for_where_clause(wc));
95        }
96
97        let options = vec![
98            DirectiveOption::Index(IndexEntryType::Normal),
99            DirectiveOption::Vis(DirectiveVisibility::from(&alias.vis)),
100            DirectiveOption::Layout(nodes),
101        ];
102
103        Directive::Trait(TraitDirective {
104            name,
105            ident: alias.ident.to_string(),
106            options,
107            content: extract_doc_from_attrs(&alias.attrs),
108            items: vec![],
109            impls: vec![],
110        })
111    }
112
113    /// Return the visibility of this directive.
114    pub(crate) fn directive_visibility(&self) -> &DirectiveVisibility {
115        if let DirectiveOption::Vis(v) = &self.options[1] {
116            return v;
117        }
118        unreachable!("Trait: order of options changed")
119    }
120
121    /// Change the parent module of the trait and its items.
122    pub(crate) fn change_parent(&mut self, new_parent: &str) {
123        self.name = format!("{new_parent}::{}", self.ident);
124        for item in &mut self.items {
125            item.change_parent(&self.name);
126        }
127        for impl_ in &mut self.impls {
128            impl_.change_parent(new_parent);
129        }
130    }
131
132    /// Add the impl directive to the trait.
133    ///
134    /// The parent and visibility of the impl directive are updated along with
135    /// the ownership.
136    ///
137    /// Args:
138    ///     :impl\_: The :rust:struct:`ImplDirective` for the trait.
139    pub(crate) fn add_impl(&mut self, mut impl_: ImplDirective) {
140        impl_.change_parent(&self.name[0..self.name.rfind("::").unwrap()]);
141        impl_.set_directive_visibility(self.directive_visibility());
142        self.impls.push(impl_);
143    }
144}
145
146impl RstDirective for TraitDirective {
147    fn get_rst_text(self, level: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
148        if self.directive_visibility() > max_visibility {
149            return vec![];
150        }
151        let content_indent = Self::make_content_indent(level);
152
153        let mut text =
154            Self::make_rst_header(Self::DIRECTIVE_NAME, &self.name, &self.options, level);
155        text.extend(self.content.get_rst_text(&content_indent));
156
157        for (section, items) in order_items(self.items) {
158            text.extend(Self::make_rst_section(
159                section,
160                level,
161                items,
162                max_visibility,
163            ));
164        }
165
166        text.extend(Self::make_rst_section(
167            "Implemented for",
168            level,
169            self.impls.into_iter().map(Directive::Impl).collect(),
170            max_visibility,
171        ));
172
173        text
174    }
175}
176
177impl MdDirective for TraitDirective {
178    fn get_md_text(self, fence_size: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
179        if self.directive_visibility() > max_visibility {
180            return vec![];
181        }
182        let fence = Self::make_fence(fence_size);
183
184        let mut text =
185            Self::make_md_header(Self::DIRECTIVE_NAME, &self.name, &self.options, &fence);
186        text.extend(self.content.get_md_text());
187
188        for (section, items) in order_items(self.items) {
189            text.extend(Self::make_md_section(
190                section,
191                fence_size,
192                items,
193                max_visibility,
194            ));
195        }
196
197        text.extend(Self::make_md_section(
198            "Implemented for",
199            fence_size,
200            self.impls.into_iter().map(Directive::Impl).collect(),
201            max_visibility,
202        ));
203
204        text.push(fence);
205        text
206    }
207
208    fn fence_size(&self) -> usize {
209        [
210            Self::calc_fence_size(&self.items),
211            match self.impls.iter().map(ImplDirective::fence_size).max() {
212                Some(s) => s + 1,
213                None => 3,
214            },
215        ]
216        .into_iter()
217        .max()
218        .unwrap()
219    }
220}