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