use std::collections::BTreeMap;
use ::syn;
use syn::{ItemUse, UseTree, Visibility};
use crate::directives::directive_options::DirectiveVisibility;
use crate::directives::{
extract_doc_from_attrs,
Directive,
MdDirective,
ModuleDirective,
RstDirective,
};
use crate::formats::{MdOption, RstOption};
#[derive(Clone, Debug, Default)]
pub(crate) struct UsePathBuilder {
pub(crate) path: Vec<String>,
pub(crate) used_name: String,
}
enum UseDirectiveOption {
UsedName(String),
Reexport(String),
}
impl RstOption for UseDirectiveOption {
fn get_rst_text(&self, indent: &str) -> Option<String> {
match self {
UseDirectiveOption::UsedName(u) => Some(format!("{indent}:used_name: {u}")),
UseDirectiveOption::Reexport(r) => Some(format!("{indent}:reexport: {r}")),
}
}
}
impl MdOption for UseDirectiveOption {
fn get_md_text(&self) -> Option<String> {
match self {
UseDirectiveOption::UsedName(u) => Some(format!(":used_name: {u}")),
UseDirectiveOption::Reexport(r) => Some(format!(":reexport: {r}")),
}
}
}
#[derive(Clone, Debug)]
pub struct UseDirective {
pub(crate) paths: BTreeMap<String, String>,
pub(crate) reexport: Option<String>,
pub(crate) content: Vec<String>,
directive_visibility: DirectiveVisibility,
}
impl UseDirective {
const DIRECTIVE_NAME: &'static str = "use";
pub(crate) fn for_path(path: &str, target: &str) -> Self {
UseDirective {
paths: BTreeMap::from([(target.to_string(), path.to_string())]),
reexport: None,
content: vec![],
directive_visibility: DirectiveVisibility::Pvt,
}
}
pub(crate) fn for_use_paths(paths: BTreeMap<String, String>) -> Self {
UseDirective {
paths,
reexport: None,
content: vec![],
directive_visibility: DirectiveVisibility::Pvt,
}
}
pub(crate) fn from_item_as_directive(parent_path: &str, item: &ItemUse) -> Directive {
Directive::Use(Self::from_item(parent_path, item))
}
pub(crate) fn from_item(parent_path: &str, item: &ItemUse) -> Self {
let crate_name = &parent_path[0..parent_path.find("::").unwrap_or(parent_path.len())];
let mut complete_paths = vec![];
let mut incomplete_paths = vec![UsePathBuilder::default()];
let mut item_stack = vec![&item.tree];
while let Some(t) = item_stack.pop() {
match t {
UseTree::Path(p) => {
incomplete_paths
.last_mut()
.unwrap()
.path
.push(p.ident.to_string());
item_stack.push(&p.tree);
}
UseTree::Name(n) => {
let mut use_path = incomplete_paths.pop().unwrap();
let name = n.ident.to_string();
if name == "self" {
use_path.used_name.clone_from(use_path.path.last().unwrap());
}
else {
use_path.path.push(name.clone());
use_path.used_name = name;
}
if use_path.path[0] == "crate" {
use_path.path[0] = crate_name.to_string();
}
else if use_path.path[0] == "self" {
use_path.path[0] = parent_path.to_string();
}
complete_paths.push(use_path);
}
UseTree::Rename(r) => {
let mut use_path = incomplete_paths.pop().unwrap();
let name = r.ident.to_string();
if name != "self" {
use_path.path.push(name);
}
use_path.used_name = r.rename.to_string();
if use_path.path[0] == "crate" {
use_path.path[0] = crate_name.to_string();
}
complete_paths.push(use_path);
}
UseTree::Glob(_) => {
incomplete_paths.pop();
}
UseTree::Group(g) => {
let last = incomplete_paths.pop().unwrap();
for _ in 0..g.items.len() {
incomplete_paths.push(last.clone());
}
for item in &g.items {
item_stack.push(item);
}
}
}
}
UseDirective {
paths: complete_paths
.into_iter()
.map(|p| (p.used_name, p.path.join("::")))
.collect(),
reexport: if matches!(&item.vis, Visibility::Inherited) {
None
}
else {
Some(parent_path.into())
},
content: extract_doc_from_attrs(&item.attrs),
directive_visibility: DirectiveVisibility::from(&item.vis),
}
}
pub(crate) fn resolve_relative_paths(&mut self, modules: &Vec<ModuleDirective>) {
'path_loop: for path in self.paths.values_mut() {
for module in modules {
if path.starts_with(&format!("{}::", module.ident)) {
path.insert_str(
0,
&format!(
"{}::",
&module.name[0..module.name.rfind("::").unwrap_or(module.name.len())]
),
);
continue 'path_loop;
}
}
}
}
pub(crate) fn contains(&self, item_path: &str) -> bool {
self.paths.iter().any(|(_, path)| path == item_path)
}
pub(crate) fn find(&self, ident: &str) -> Option<&String> {
self.paths
.iter()
.find(|&(used_name, _)| used_name == ident)
.map(|(_, path)| path)
}
pub(crate) fn directive_visibility(&self) -> &DirectiveVisibility {
&self.directive_visibility
}
pub(crate) fn inline(&mut self, item_name: &str) -> Option<(String, String)> {
let mut found = None;
if let Some(reexport) = &self.reexport {
self.paths.retain(|used_name, path| {
if path == item_name {
found = Some((used_name.clone(), format!("{reexport}::{used_name}")));
return false;
}
true
});
}
found
}
}
impl RstDirective for UseDirective {
fn get_rst_text(self, level: usize, _: &DirectiveVisibility) -> Vec<String> {
let mut text = vec![];
for (used_name, path) in self.paths {
let mut options = vec![UseDirectiveOption::UsedName(used_name)];
if let Some(reexport) = &self.reexport {
options.push(UseDirectiveOption::Reexport(reexport.clone()));
}
text.extend(Self::make_rst_header(
Self::DIRECTIVE_NAME,
path,
&options,
level,
));
}
text
}
}
impl MdDirective for UseDirective {
fn get_md_text(self, fence_size: usize, _: &DirectiveVisibility) -> Vec<String> {
let fence = Self::make_fence(fence_size);
let mut text = vec![];
for (used_name, path) in self.paths {
let mut options = vec![UseDirectiveOption::UsedName(used_name)];
if let Some(reexport) = &self.reexport {
options.push(UseDirectiveOption::Reexport(reexport.clone()));
}
text.extend(Self::make_md_header(
Self::DIRECTIVE_NAME,
path,
&options,
&fence,
));
text.push(fence.clone());
}
text
}
}