use crate::parse::EntryRole;
use crate::parse::Style;
use crate::parse::google::nodes::GoogleArg;
use crate::parse::google::nodes::GoogleAttribute;
use crate::parse::google::nodes::GoogleDeprecation;
use crate::parse::google::nodes::GoogleDirective;
use crate::parse::google::nodes::GoogleDocstring;
use crate::parse::google::nodes::GoogleException;
use crate::parse::google::nodes::GoogleMethod;
use crate::parse::google::nodes::GoogleReference;
use crate::parse::google::nodes::GoogleReturn;
use crate::parse::google::nodes::GoogleSection;
use crate::parse::google::nodes::GoogleSeeAlsoItem;
use crate::parse::google::nodes::GoogleWarning;
use crate::parse::google::nodes::GoogleYield;
use crate::parse::numpy::nodes::NumPyAttribute;
use crate::parse::numpy::nodes::NumPyDeprecation;
use crate::parse::numpy::nodes::NumPyDirective;
use crate::parse::numpy::nodes::NumPyDocstring;
use crate::parse::numpy::nodes::NumPyException;
use crate::parse::numpy::nodes::NumPyMethod;
use crate::parse::numpy::nodes::NumPyParameter;
use crate::parse::numpy::nodes::NumPyReference;
use crate::parse::numpy::nodes::NumPyReturns;
use crate::parse::numpy::nodes::NumPySection;
use crate::parse::numpy::nodes::NumPySeeAlsoItem;
use crate::parse::numpy::nodes::NumPyWarning;
use crate::parse::numpy::nodes::NumPyYields;
use crate::parse::plain::nodes::PlainDocstring;
use crate::parse::utils::directive_is_deprecated;
use crate::syntax::Parsed;
use crate::syntax::SyntaxElement;
use crate::syntax::SyntaxKind;
use crate::syntax::SyntaxNode;
pub trait DocstringVisitor: Sized {
type Error;
fn visit_plain_docstring(&mut self, parsed: &Parsed, doc: &PlainDocstring<'_>) -> Result<(), Self::Error> {
let _ = (parsed, doc);
Ok(())
}
fn visit_google_docstring(&mut self, parsed: &Parsed, doc: &GoogleDocstring<'_>) -> Result<(), Self::Error> {
walk_children(parsed, doc.syntax(), self)
}
fn visit_google_directive(&mut self, parsed: &Parsed, dir: &GoogleDirective<'_>) -> Result<(), Self::Error> {
walk_children(parsed, dir.syntax(), self)
}
#[allow(unused_variables)]
fn visit_google_deprecation(&mut self, parsed: &Parsed, dep: &GoogleDeprecation<'_>) -> Result<(), Self::Error> {
Ok(())
}
fn visit_google_section(&mut self, parsed: &Parsed, sec: &GoogleSection<'_>) -> Result<(), Self::Error> {
walk_children(parsed, sec.syntax(), self)
}
fn visit_google_arg(&mut self, parsed: &Parsed, arg: &GoogleArg<'_>) -> Result<(), Self::Error> {
walk_children(parsed, arg.syntax(), self)
}
fn visit_google_return(&mut self, parsed: &Parsed, rtn: &GoogleReturn<'_>) -> Result<(), Self::Error> {
walk_children(parsed, rtn.syntax(), self)
}
fn visit_google_yield(&mut self, parsed: &Parsed, yld: &GoogleYield<'_>) -> Result<(), Self::Error> {
walk_children(parsed, yld.syntax(), self)
}
fn visit_google_exception(&mut self, parsed: &Parsed, exc: &GoogleException<'_>) -> Result<(), Self::Error> {
walk_children(parsed, exc.syntax(), self)
}
fn visit_google_warning(&mut self, parsed: &Parsed, wrn: &GoogleWarning<'_>) -> Result<(), Self::Error> {
walk_children(parsed, wrn.syntax(), self)
}
fn visit_google_see_also_item(&mut self, parsed: &Parsed, sai: &GoogleSeeAlsoItem<'_>) -> Result<(), Self::Error> {
walk_children(parsed, sai.syntax(), self)
}
fn visit_google_reference(&mut self, parsed: &Parsed, r#ref: &GoogleReference<'_>) -> Result<(), Self::Error> {
walk_children(parsed, r#ref.syntax(), self)
}
fn visit_google_attribute(&mut self, parsed: &Parsed, att: &GoogleAttribute<'_>) -> Result<(), Self::Error> {
walk_children(parsed, att.syntax(), self)
}
fn visit_google_method(&mut self, parsed: &Parsed, mtd: &GoogleMethod<'_>) -> Result<(), Self::Error> {
walk_children(parsed, mtd.syntax(), self)
}
fn visit_numpy_docstring(&mut self, parsed: &Parsed, doc: &NumPyDocstring<'_>) -> Result<(), Self::Error> {
walk_children(parsed, doc.syntax(), self)
}
fn visit_numpy_directive(&mut self, parsed: &Parsed, dir: &NumPyDirective<'_>) -> Result<(), Self::Error> {
walk_children(parsed, dir.syntax(), self)
}
#[allow(unused_variables)]
fn visit_numpy_deprecation(&mut self, parsed: &Parsed, dep: &NumPyDeprecation<'_>) -> Result<(), Self::Error> {
Ok(())
}
fn visit_numpy_section(&mut self, parsed: &Parsed, sec: &NumPySection<'_>) -> Result<(), Self::Error> {
walk_children(parsed, sec.syntax(), self)
}
fn visit_numpy_parameter(&mut self, parsed: &Parsed, prm: &NumPyParameter<'_>) -> Result<(), Self::Error> {
walk_children(parsed, prm.syntax(), self)
}
fn visit_numpy_returns(&mut self, parsed: &Parsed, rtn: &NumPyReturns<'_>) -> Result<(), Self::Error> {
walk_children(parsed, rtn.syntax(), self)
}
fn visit_numpy_yields(&mut self, parsed: &Parsed, yld: &NumPyYields<'_>) -> Result<(), Self::Error> {
walk_children(parsed, yld.syntax(), self)
}
fn visit_numpy_exception(&mut self, parsed: &Parsed, exc: &NumPyException<'_>) -> Result<(), Self::Error> {
walk_children(parsed, exc.syntax(), self)
}
fn visit_numpy_warning(&mut self, parsed: &Parsed, wrn: &NumPyWarning<'_>) -> Result<(), Self::Error> {
walk_children(parsed, wrn.syntax(), self)
}
fn visit_numpy_see_also_item(&mut self, parsed: &Parsed, sai: &NumPySeeAlsoItem<'_>) -> Result<(), Self::Error> {
walk_children(parsed, sai.syntax(), self)
}
fn visit_numpy_reference(&mut self, parsed: &Parsed, r#ref: &NumPyReference<'_>) -> Result<(), Self::Error> {
walk_children(parsed, r#ref.syntax(), self)
}
fn visit_numpy_attribute(&mut self, parsed: &Parsed, att: &NumPyAttribute<'_>) -> Result<(), Self::Error> {
walk_children(parsed, att.syntax(), self)
}
fn visit_numpy_method(&mut self, parsed: &Parsed, mtd: &NumPyMethod<'_>) -> Result<(), Self::Error> {
walk_children(parsed, mtd.syntax(), self)
}
}
pub fn walk<V: DocstringVisitor>(parsed: &Parsed, node: &SyntaxNode, visitor: &mut V) -> Result<(), V::Error> {
walk_in(parsed, node, None, visitor)
}
fn walk_in<V: DocstringVisitor>(
parsed: &Parsed,
node: &SyntaxNode,
section: Option<&SyntaxNode>,
visitor: &mut V,
) -> Result<(), V::Error> {
match (node.kind(), parsed.style()) {
(SyntaxKind::DOCUMENT, Style::Plain) => {
visitor.visit_plain_docstring(parsed, &PlainDocstring { parsed, node })?
}
(SyntaxKind::DOCUMENT, Style::Google) => {
visitor.visit_google_docstring(parsed, &GoogleDocstring { parsed, node })?
}
(SyntaxKind::DOCUMENT, Style::NumPy) => {
visitor.visit_numpy_docstring(parsed, &NumPyDocstring { parsed, node })?
}
(SyntaxKind::SECTION, Style::Google) => {
visitor.visit_google_section(parsed, &GoogleSection { parsed, node })?
}
(SyntaxKind::SECTION, Style::NumPy) => visitor.visit_numpy_section(parsed, &NumPySection { parsed, node })?,
(SyntaxKind::DIRECTIVE, Style::Google) => {
visitor.visit_google_directive(parsed, &GoogleDirective { parsed, node })?;
if directive_is_deprecated(parsed, node) {
visitor.visit_google_deprecation(parsed, &GoogleDeprecation { parsed, node })?;
}
}
(SyntaxKind::DIRECTIVE, Style::NumPy) => {
visitor.visit_numpy_directive(parsed, &NumPyDirective { parsed, node })?;
if directive_is_deprecated(parsed, node) {
visitor.visit_numpy_deprecation(parsed, &NumPyDeprecation { parsed, node })?;
}
}
(SyntaxKind::CITATION, Style::Google) => {
visitor.visit_google_reference(parsed, &GoogleReference { parsed, node })?
}
(SyntaxKind::CITATION, Style::NumPy) => {
visitor.visit_numpy_reference(parsed, &NumPyReference { parsed, node })?
}
(SyntaxKind::ENTRY, Style::Google) => walk_google_entry(parsed, node, section, visitor)?,
(SyntaxKind::ENTRY, Style::NumPy) => walk_numpy_entry(parsed, node, section, visitor)?,
_ => {}
}
Ok(())
}
fn find_parent<'a>(root: &'a SyntaxNode, target: &SyntaxNode) -> Option<&'a SyntaxNode> {
for child in root.children() {
if let SyntaxElement::Node(n) = child {
if core::ptr::eq(n, target) {
return Some(root);
}
if let Some(found) = find_parent(n, target) {
return Some(found);
}
}
}
None
}
fn enclosing_section<'a>(parsed: &'a Parsed, entry: &SyntaxNode) -> Option<&'a SyntaxNode> {
find_parent(parsed.root(), entry).filter(|parent| parent.kind() == SyntaxKind::SECTION)
}
fn walk_google_entry<V: DocstringVisitor>(
parsed: &Parsed,
node: &SyntaxNode,
section: Option<&SyntaxNode>,
visitor: &mut V,
) -> Result<(), V::Error> {
let Some(section) = section
.or_else(|| enclosing_section(parsed, node))
.and_then(|n| GoogleSection::cast(parsed, n))
else {
return Ok(());
};
match section.section_kind().entry_role() {
EntryRole::Parameter => visitor.visit_google_arg(parsed, &GoogleArg { parsed, node }),
EntryRole::Return => visitor.visit_google_return(parsed, &GoogleReturn { parsed, node }),
EntryRole::Yield => visitor.visit_google_yield(parsed, &GoogleYield { parsed, node }),
EntryRole::Exception => visitor.visit_google_exception(parsed, &GoogleException { parsed, node }),
EntryRole::Warning => visitor.visit_google_warning(parsed, &GoogleWarning { parsed, node }),
EntryRole::SeeAlsoItem => visitor.visit_google_see_also_item(parsed, &GoogleSeeAlsoItem { parsed, node }),
EntryRole::Attribute => visitor.visit_google_attribute(parsed, &GoogleAttribute { parsed, node }),
EntryRole::Method => visitor.visit_google_method(parsed, &GoogleMethod { parsed, node }),
EntryRole::Citation | EntryRole::FreeText => Ok(()),
}
}
fn walk_numpy_entry<V: DocstringVisitor>(
parsed: &Parsed,
node: &SyntaxNode,
section: Option<&SyntaxNode>,
visitor: &mut V,
) -> Result<(), V::Error> {
let Some(section) = section
.or_else(|| enclosing_section(parsed, node))
.and_then(|n| NumPySection::cast(parsed, n))
else {
return Ok(());
};
match section.section_kind().entry_role() {
EntryRole::Parameter => visitor.visit_numpy_parameter(parsed, &NumPyParameter { parsed, node }),
EntryRole::Return => visitor.visit_numpy_returns(parsed, &NumPyReturns { parsed, node }),
EntryRole::Yield => visitor.visit_numpy_yields(parsed, &NumPyYields { parsed, node }),
EntryRole::Exception => visitor.visit_numpy_exception(parsed, &NumPyException { parsed, node }),
EntryRole::Warning => visitor.visit_numpy_warning(parsed, &NumPyWarning { parsed, node }),
EntryRole::SeeAlsoItem => visitor.visit_numpy_see_also_item(parsed, &NumPySeeAlsoItem { parsed, node }),
EntryRole::Attribute => visitor.visit_numpy_attribute(parsed, &NumPyAttribute { parsed, node }),
EntryRole::Method => visitor.visit_numpy_method(parsed, &NumPyMethod { parsed, node }),
EntryRole::Citation | EntryRole::FreeText => Ok(()),
}
}
#[inline]
pub fn walk_children<V: DocstringVisitor>(parsed: &Parsed, node: &SyntaxNode, visitor: &mut V) -> Result<(), V::Error> {
let section = (node.kind() == SyntaxKind::SECTION).then_some(node);
for child in node.children() {
if let SyntaxElement::Node(n) = child {
walk_in(parsed, n, section, visitor)?;
}
}
Ok(())
}