sphinx_rustdocgen/directives/
struct_directive.rs1use syn::{Fields, Generics, ItemStruct, ItemUnion, Variant, Visibility};
20
21use crate::directives::directive_options::{DirectiveOption, DirectiveVisibility, IndexEntryType};
22use crate::directives::variable_directive::VariableDirective;
23use crate::directives::{extract_doc_from_attrs, Directive, ImplDirective};
24use crate::formats::{MdContent, MdDirective, RstContent, RstDirective};
25use crate::nodes::{nodes_for_generics, nodes_for_type, nodes_for_where_clause, Node};
26
27#[derive(Clone, Debug)]
29pub struct StructDirective {
30 pub(crate) name: String,
32 pub(crate) ident: String,
34 pub(crate) options: Vec<DirectiveOption>,
36 pub(crate) content: Vec<String>,
38 pub(crate) fields: Vec<VariableDirective>,
40 pub(crate) self_impls: Vec<ImplDirective>,
42 pub(crate) trait_impls: Vec<ImplDirective>,
44}
45
46macro_rules! make_nodes {
48 ($ident:expr, $fields:expr, $generics:expr, $item_keyword:expr) => {{
49 let mut nodes = if let Some(keyword) = $item_keyword {
50 vec![
51 Node::Keyword(keyword),
52 Node::Space,
53 Node::Name($ident.to_string()),
54 ]
55 }
56 else {
57 vec![Node::Name($ident.to_string())]
58 };
59
60 if let Some(generics) = $generics {
61 nodes.extend(nodes_for_generics(generics));
62 }
63
64 if let Fields::Unnamed(fields) = &$fields {
65 nodes.push(Node::Punctuation("("));
66 for field in &fields.unnamed {
67 nodes.extend(nodes_for_type(&field.ty));
68 nodes.push(Node::Punctuation(", "));
69 }
70 nodes.pop();
71 nodes.push(Node::Punctuation(")"));
72 }
73
74 if let Some(generics) = $generics {
75 if let Some(wc) = &generics.where_clause {
76 nodes.extend(nodes_for_where_clause(wc));
77 }
78 }
79
80 nodes
81 }};
82}
83
84impl StructDirective {
85 const DIRECTIVE_NAME: &'static str = "struct";
86
87 pub(crate) fn from_variant(
89 parent_path: &str,
90 variant: &Variant,
91 inherited_visibility: &Option<&Visibility>,
92 ) -> StructDirective {
93 let name = format!("{}::{}", parent_path, variant.ident);
94
95 let options = vec![
96 DirectiveOption::Index(IndexEntryType::SubEntry),
97 DirectiveOption::Vis(DirectiveVisibility::from(inherited_visibility.unwrap())),
98 DirectiveOption::Toc(variant.ident.to_string()),
99 DirectiveOption::Layout(make_nodes!(
100 variant.ident,
101 variant.fields,
102 None::<&Generics>,
103 None
104 )),
105 ];
106
107 let fields = VariableDirective::from_fields(
108 &name,
109 &variant.fields,
110 inherited_visibility,
111 IndexEntryType::None,
112 );
113
114 StructDirective {
115 name,
116 ident: variant.ident.to_string(),
117 options,
118 content: extract_doc_from_attrs(&variant.attrs),
119 fields,
120 self_impls: vec![],
121 trait_impls: vec![],
122 }
123 }
124
125 pub(crate) fn from_item(parent_path: &str, item: &ItemStruct) -> Directive {
127 let name = format!("{}::{}", parent_path, item.ident);
128
129 let options = vec![
130 DirectiveOption::Index(IndexEntryType::WithSubEntries),
131 DirectiveOption::Vis(DirectiveVisibility::from(&item.vis)),
132 DirectiveOption::Toc(format!("struct {}", &item.ident)),
133 DirectiveOption::Layout(make_nodes!(
134 item.ident,
135 item.fields,
136 Some(&item.generics),
137 Some(Self::DIRECTIVE_NAME)
138 )),
139 ];
140
141 let fields =
142 VariableDirective::from_fields(&name, &item.fields, &None, IndexEntryType::SubEntry);
143
144 Directive::Struct(StructDirective {
145 name,
146 ident: item.ident.to_string(),
147 options,
148 content: extract_doc_from_attrs(&item.attrs),
149 fields,
150 self_impls: vec![],
151 trait_impls: vec![],
152 })
153 }
154
155 pub(crate) fn from_union(parent_path: &str, item: &ItemUnion) -> Directive {
157 let name = format!("{parent_path}::{}", item.ident);
158 let fields = Fields::Named(item.fields.clone());
159
160 let options = vec![
161 DirectiveOption::Index(IndexEntryType::WithSubEntries),
162 DirectiveOption::Vis(DirectiveVisibility::from(&item.vis)),
163 DirectiveOption::Toc(format!("union {}", item.ident)),
164 DirectiveOption::Layout(make_nodes!(
165 item.ident,
166 fields,
167 Some(&item.generics),
168 Some("union")
169 )),
170 ];
171
172 let fields =
173 VariableDirective::from_fields(&name, &fields, &None, IndexEntryType::SubEntry);
174
175 Directive::Struct(StructDirective {
176 name,
177 ident: item.ident.to_string(),
178 options,
179 content: extract_doc_from_attrs(&item.attrs),
180 fields,
181 self_impls: vec![],
182 trait_impls: vec![],
183 })
184 }
185
186 pub(crate) fn directive_visibility(&self) -> &DirectiveVisibility {
188 if let DirectiveOption::Vis(v) = &self.options[1] {
189 return v;
190 }
191 unreachable!("Struct: order of options changed")
192 }
193
194 pub(crate) fn change_parent(&mut self, new_parent: &str) {
196 self.name = format!("{new_parent}::{}", self.ident);
197 for field in &mut self.fields {
198 field.change_parent(&self.name);
199 }
200 for impl_ in &mut self.self_impls {
201 impl_.change_parent(new_parent);
202 }
203 for impl_ in &mut self.trait_impls {
204 impl_.change_parent(new_parent);
205 }
206 }
207
208 pub(crate) fn add_impl(&mut self, mut impl_: ImplDirective) {
217 impl_.change_parent(&self.name[0..self.name.rfind("::").unwrap()]);
219 impl_.set_directive_visibility(self.directive_visibility());
220 if impl_.trait_.is_some() {
221 self.trait_impls.push(impl_);
222 }
223 else {
224 self.self_impls.push(impl_);
225 }
226 }
227}
228
229impl RstDirective for StructDirective {
230 fn get_rst_text(self, level: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
231 if self.directive_visibility() > max_visibility {
232 return vec![];
233 }
234 let content_indent = Self::make_content_indent(level);
235
236 let mut text =
237 Self::make_rst_header(Self::DIRECTIVE_NAME, &self.name, &self.options, level);
238 text.extend(self.content.get_rst_text(&content_indent));
239
240 for field in self.fields {
241 text.extend(field.get_rst_text(level + 1, max_visibility));
242 }
243
244 text.extend(Self::make_rst_section(
245 "Implementations",
246 level,
247 self.self_impls.into_iter().map(Directive::Impl).collect(),
248 max_visibility,
249 ));
250
251 text.extend(Self::make_rst_section(
252 "Traits implemented",
253 level,
254 self.trait_impls.into_iter().map(Directive::Impl).collect(),
255 max_visibility,
256 ));
257
258 text
259 }
260}
261
262impl MdDirective for StructDirective {
263 fn get_md_text(self, fence_size: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
264 if self.directive_visibility() > max_visibility {
265 return vec![];
266 }
267 let fence = Self::make_fence(fence_size);
268
269 let mut text =
270 Self::make_md_header(Self::DIRECTIVE_NAME, &self.name, &self.options, &fence);
271 text.extend(self.content.get_md_text());
272
273 for field in self.fields {
274 text.extend(field.get_md_text(fence_size - 1, max_visibility));
275 }
276
277 text.extend(Self::make_md_section(
278 "Implementations",
279 fence_size,
280 self.self_impls.into_iter().map(Directive::Impl).collect(),
281 max_visibility,
282 ));
283
284 text.extend(Self::make_md_section(
285 "Traits implemented",
286 fence_size,
287 self.trait_impls.into_iter().map(Directive::Impl).collect(),
288 max_visibility,
289 ));
290
291 text.push(fence);
292 text
293 }
294
295 fn fence_size(&self) -> usize {
297 [
298 match self.fields.iter().map(VariableDirective::fence_size).max() {
299 Some(s) => s + 1,
300 None => 3,
301 },
302 match self.trait_impls.iter().map(ImplDirective::fence_size).max() {
303 Some(s) => s + 1,
304 None => 3,
305 },
306 match self.self_impls.iter().map(ImplDirective::fence_size).max() {
307 Some(s) => s + 1,
308 None => 3,
309 },
310 ]
311 .into_iter()
312 .max()
313 .unwrap()
314 }
315}