sphinx_rustdocgen/directives/
use_directive.rs1use std::collections::BTreeMap;
18
19use ::syn;
20use syn::{ItemUse, UseTree, Visibility};
21
22use crate::directives::directive_options::DirectiveVisibility;
23use crate::directives::{
24 extract_doc_from_attrs,
25 Directive,
26 MdDirective,
27 ModuleDirective,
28 RstDirective,
29};
30use crate::formats::{MdOption, RstOption};
31
32#[derive(Clone, Debug, Default)]
33pub(crate) struct UsePathBuilder {
34 pub(crate) path: Vec<String>,
35 pub(crate) used_name: String,
36}
37
38enum UseDirectiveOption {
40 UsedName(String),
41 Reexport(String),
42}
43
44impl RstOption for UseDirectiveOption {
45 fn get_rst_text(&self, indent: &str) -> Option<String> {
46 match self {
47 UseDirectiveOption::UsedName(u) => Some(format!("{indent}:used_name: {u}")),
48 UseDirectiveOption::Reexport(r) => Some(format!("{indent}:reexport: {r}")),
49 }
50 }
51}
52
53impl MdOption for UseDirectiveOption {
54 fn get_md_text(&self) -> Option<String> {
55 match self {
56 UseDirectiveOption::UsedName(u) => Some(format!(":used_name: {u}")),
57 UseDirectiveOption::Reexport(r) => Some(format!(":reexport: {r}")),
58 }
59 }
60}
61
62#[derive(Clone, Debug)]
63pub struct UseDirective {
64 pub(crate) paths: BTreeMap<String, String>,
65 pub(crate) reexport: Option<String>,
66 pub(crate) content: Vec<String>,
67 directive_visibility: DirectiveVisibility,
68}
69
70impl UseDirective {
71 const DIRECTIVE_NAME: &'static str = "use";
72
73 pub(crate) fn for_path(path: &str, target: &str) -> Self {
75 UseDirective {
76 paths: BTreeMap::from([(target.to_string(), path.to_string())]),
77 reexport: None,
78 content: vec![],
79 directive_visibility: DirectiveVisibility::Pvt,
80 }
81 }
82
83 pub(crate) fn for_use_paths(paths: BTreeMap<String, String>) -> Self {
85 UseDirective {
86 paths,
87 reexport: None,
88 content: vec![],
89 directive_visibility: DirectiveVisibility::Pvt,
90 }
91 }
92
93 pub(crate) fn from_item_as_directive(parent_path: &str, item: &ItemUse) -> Directive {
96 Directive::Use(Self::from_item(parent_path, item))
97 }
98
99 pub(crate) fn from_item(parent_path: &str, item: &ItemUse) -> Self {
101 let crate_name = &parent_path[0..parent_path.find("::").unwrap_or(parent_path.len())];
106
107 let mut complete_paths = vec![];
109
110 let mut incomplete_paths = vec![UsePathBuilder::default()];
113
114 let mut item_stack = vec![&item.tree];
116
117 while let Some(t) = item_stack.pop() {
118 match t {
119 UseTree::Path(p) => {
120 incomplete_paths
124 .last_mut()
125 .unwrap()
126 .path
127 .push(p.ident.to_string());
128 item_stack.push(&p.tree);
129 }
130 UseTree::Name(n) => {
131 let mut use_path = incomplete_paths.pop().unwrap();
134
135 let name = n.ident.to_string();
137 if name == "self" {
138 use_path.used_name.clone_from(use_path.path.last().unwrap());
139 }
140 else {
141 use_path.path.push(name.clone());
142 use_path.used_name = name;
143 }
144
145 if use_path.path[0] == "crate" {
147 use_path.path[0] = crate_name.to_string();
148 }
149 else if use_path.path[0] == "self" {
150 use_path.path[0] = parent_path.to_string();
151 }
152
153 complete_paths.push(use_path);
154 }
155 UseTree::Rename(r) => {
156 let mut use_path = incomplete_paths.pop().unwrap();
159 let name = r.ident.to_string();
161 if name != "self" {
162 use_path.path.push(name);
163 }
164 use_path.used_name = r.rename.to_string();
165 if use_path.path[0] == "crate" {
166 use_path.path[0] = crate_name.to_string();
167 }
168 complete_paths.push(use_path);
169 }
170 UseTree::Glob(_) => {
171 incomplete_paths.pop();
176 }
177 UseTree::Group(g) => {
178 let last = incomplete_paths.pop().unwrap();
187 for _ in 0..g.items.len() {
188 incomplete_paths.push(last.clone());
189 }
190 for item in &g.items {
191 item_stack.push(item);
192 }
193 }
194 }
195 }
196
197 UseDirective {
198 paths: complete_paths
199 .into_iter()
200 .map(|p| (p.used_name, p.path.join("::")))
201 .collect(),
202 reexport: if matches!(&item.vis, Visibility::Inherited) {
203 None
204 }
205 else {
206 Some(parent_path.into())
207 },
208 content: extract_doc_from_attrs(&item.attrs),
209 directive_visibility: DirectiveVisibility::from(&item.vis),
210 }
211 }
212
213 pub(crate) fn resolve_relative_paths(&mut self, modules: &Vec<ModuleDirective>) {
219 'path_loop: for path in self.paths.values_mut() {
220 for module in modules {
221 if path.starts_with(&format!("{}::", module.ident)) {
222 path.insert_str(
223 0,
224 &format!("{}::", &module.name[0..module.name.rfind("::").unwrap()]),
225 );
226 continue 'path_loop;
227 }
228 }
229 }
230 }
231
232 pub(crate) fn contains(&self, item_path: &str) -> bool {
237 self.paths.iter().any(|(_, path)| path == item_path)
238 }
239
240 pub(crate) fn find(&self, ident: &str) -> Option<&String> {
242 self.paths
243 .iter()
244 .find(|&(used_name, _)| used_name == ident)
245 .map(|(_, path)| path)
246 }
247
248 pub(crate) fn directive_visibility(&self) -> &DirectiveVisibility {
250 &self.directive_visibility
251 }
252
253 pub(crate) fn inline(&mut self, item_name: &str) -> Option<(String, String)> {
262 let mut found = None;
263 if let Some(reexport) = &self.reexport {
264 self.paths.retain(|used_name, path| {
265 if path == item_name {
266 found = Some((used_name.clone(), format!("{reexport}::{used_name}")));
267 return false;
268 }
269 true
270 });
271 }
272 found
273 }
274}
275
276impl RstDirective for UseDirective {
277 fn get_rst_text(self, level: usize, _: &DirectiveVisibility) -> Vec<String> {
278 let mut text = vec![];
279 for (used_name, path) in self.paths {
280 let mut options = vec![UseDirectiveOption::UsedName(used_name)];
281 if let Some(reexport) = &self.reexport {
282 options.push(UseDirectiveOption::Reexport(reexport.clone()));
283 }
284 text.extend(Self::make_rst_header(
285 Self::DIRECTIVE_NAME,
286 path,
287 &options,
288 level,
289 ));
290 }
291
292 text
293 }
294}
295
296impl MdDirective for UseDirective {
297 fn get_md_text(self, fence_size: usize, _: &DirectiveVisibility) -> Vec<String> {
298 let fence = Self::make_fence(fence_size);
299 let mut text = vec![];
300 for (used_name, path) in self.paths {
301 let mut options = vec![UseDirectiveOption::UsedName(used_name)];
302 if let Some(reexport) = &self.reexport {
303 options.push(UseDirectiveOption::Reexport(reexport.clone()));
304 }
305 text.extend(Self::make_md_header(
306 Self::DIRECTIVE_NAME,
307 path,
308 &options,
309 &fence,
310 ));
311 text.push(fence.clone());
312 }
313
314 text
315 }
316}