//! Typed AST wrappers for the ink CST.
//!
//! Every struct is a zero-cost newtype around [`SyntaxNode`] that implements
//! [`AstNode`]. Use [`crate::Parse::tree()`] to get a [`SourceFile`] from
//! a parse result.

mod nodes;
mod ptr;
mod support;

pub use nodes::*;
pub use ptr::{AstPtr, SyntaxNodePtr};

use crate::SyntaxNode;

/// A typed wrapper around a [`SyntaxNode`].
///
/// Implementations are generated by the [`ast_node!`] macro.
pub trait AstNode: Sized {
    /// Returns `true` if a node with the given kind can be cast to `Self`.
    fn can_cast(kind: crate::SyntaxKind) -> bool;

    /// Try to cast a generic `SyntaxNode` into this typed wrapper.
    fn cast(node: SyntaxNode) -> Option<Self>;

    /// Access the underlying `SyntaxNode`.
    fn syntax(&self) -> &SyntaxNode;
}

/// Generates a zero-cost newtype struct implementing [`AstNode`].
///
/// ```ignore
/// ast_node!(SourceFile, SOURCE_FILE);
/// ```
///
/// expands to a struct wrapping `SyntaxNode` with `AstNode`, `Debug`,
/// `Clone`, `PartialEq`, `Eq`, and `Hash` implementations.
macro_rules! ast_node {
    ($name:ident, $kind:ident) => {
        #[derive(Clone, PartialEq, Eq, Hash)]
        pub struct $name {
            syntax: $crate::SyntaxNode,
        }

        impl std::fmt::Debug for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                std::fmt::Debug::fmt(&self.syntax, f)
            }
        }

        impl std::fmt::Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                std::fmt::Display::fmt(&self.syntax.text(), f)
            }
        }

        impl $crate::ast::AstNode for $name {
            fn can_cast(kind: $crate::SyntaxKind) -> bool {
                kind == $crate::SyntaxKind::$kind
            }

            fn cast(node: $crate::SyntaxNode) -> Option<Self> {
                if Self::can_cast(node.kind()) {
                    Some(Self { syntax: node })
                } else {
                    None
                }
            }

            fn syntax(&self) -> &$crate::SyntaxNode {
                &self.syntax
            }
        }
    };
}

pub(crate) use ast_node;

#[cfg(test)]
mod tests;
