use syn::{ItemImpl, Visibility};
use crate::directives::directive_options::{DirectiveOption, DirectiveVisibility, IndexEntryType};
use crate::directives::{extract_doc_from_attrs, order_items, Directive};
use crate::formats::{MdContent, MdDirective, RstContent, RstDirective};
use crate::nodes::{
nodes_for_generics,
nodes_for_path,
nodes_for_type,
nodes_for_where_clause,
type_name,
Node,
};
#[derive(Clone, Debug)]
pub struct ImplDirective {
pub(crate) name: String,
pub(crate) self_ty: String,
pub(crate) resolved_self_ty: String,
pub(crate) trait_: Option<String>,
pub(crate) resolved_trait: Option<String>,
pub(crate) options: Vec<DirectiveOption>,
pub(crate) content: Vec<String>,
pub(crate) items: Vec<Directive>,
}
fn nodes_for_impl(item: &ItemImpl) -> Vec<Node> {
let mut nodes = vec![];
if item.unsafety.is_some() {
nodes.extend_from_slice(&[Node::Keyword("unsafe"), Node::Space]);
}
nodes.extend_from_slice(&[Node::Keyword("impl")]);
nodes.extend(nodes_for_generics(&item.generics));
nodes.push(Node::Space);
if let Some((bang, path, _)) = &item.trait_ {
if bang.is_some() {
nodes.push(Node::Operator("!"));
}
nodes.extend(nodes_for_path(path));
nodes.extend_from_slice(&[Node::Space, Node::Keyword("for"), Node::Space]);
}
nodes.extend(nodes_for_type(&item.self_ty));
if let Some(wc) = &item.generics.where_clause {
nodes.extend(nodes_for_where_clause(wc));
}
nodes
}
impl ImplDirective {
const DIRECTIVE_NAME: &'static str = "impl";
pub(crate) fn from_item(
parent_path: &str,
item: &ItemImpl,
inherited_visibility: &Option<&Visibility>,
) -> Self {
let self_ty = type_name(&item.self_ty);
let mut trait_ = String::new();
if let Some((bang, path, _)) = &item.trait_ {
if bang.is_some() {
trait_ += "!";
}
trait_ += &*path.segments.last().unwrap().ident.to_string();
};
let options = vec![
DirectiveOption::Index(IndexEntryType::None),
DirectiveOption::Vis(DirectiveVisibility::Pub), DirectiveOption::Layout(nodes_for_impl(item)),
if trait_.is_empty() {
DirectiveOption::Toc(format!("impl {self_ty}"))
}
else {
DirectiveOption::Toc(format!("impl {trait_} for {self_ty}"))
},
];
let name = if trait_.is_empty() {
format!("{parent_path}::{self_ty}")
}
else {
format!("{parent_path}::{self_ty}::{trait_}")
};
let items = Directive::from_impl_items(&name, item.items.iter(), inherited_visibility);
ImplDirective {
name,
self_ty,
resolved_self_ty: String::new(),
trait_: if trait_.is_empty() {
None
}
else {
Some(trait_)
},
resolved_trait: None,
options,
content: extract_doc_from_attrs(&item.attrs),
items,
}
}
pub(crate) fn directive_visibility(&self) -> &DirectiveVisibility {
if let DirectiveOption::Vis(v) = &self.options[1] {
return v;
}
unreachable!("Impl: order of options changed")
}
pub(crate) fn set_directive_visibility(&mut self, visibility: &DirectiveVisibility) {
self.options[1] = DirectiveOption::Vis(*visibility)
}
pub(crate) fn change_parent(&mut self, new_parent: &str) {
if let Some(t) = &self.trait_ {
self.name = format!("{new_parent}::{}::{t}", self.self_ty)
}
else {
self.name = format!("{new_parent}::{}", self.self_ty);
}
for item in &mut self.items {
item.change_parent(&self.name);
}
}
pub(crate) fn for_item(&self, name: &str) -> bool {
name == self.name
|| self.name.starts_with(&format!("{}::", name))
|| name == self.resolved_self_ty
}
pub(crate) fn for_trait(&self, name: &str, parent_name: &str) -> bool {
match &self.trait_ {
Some(trait_) => {
name == format!("{parent_name}::{trait_}") || name == self.resolved_trait.as_ref().unwrap_or(&String::new())
}
_ => false,
}
}
}
impl RstDirective for ImplDirective {
fn get_rst_text(self, level: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
let content_indent = Self::make_content_indent(level + 1);
let mut text =
Self::make_rst_header(Self::DIRECTIVE_NAME, &self.name, &self.options, level);
text.extend(self.content.get_rst_text(&content_indent));
for (section, items) in order_items(self.items) {
text.extend(Self::make_rst_section(
section,
level,
items,
max_visibility,
));
}
text
}
}
impl MdDirective for ImplDirective {
fn get_md_text(self, fence_size: usize, max_visibility: &DirectiveVisibility) -> Vec<String> {
let fence = Self::make_fence(fence_size);
let mut text =
Self::make_md_header(Self::DIRECTIVE_NAME, &self.name, &self.options, &fence);
text.extend(self.content.get_md_text());
for (section, items) in order_items(self.items) {
text.extend(Self::make_md_section(
section,
fence_size,
items,
max_visibility,
));
}
text.push(fence);
text
}
fn fence_size(&self) -> usize {
Self::calc_fence_size(&self.items)
}
}