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