marktwin 0.5.0

Marktwin format support for Eternaltwin.
Documentation
use std::convert::TryFrom;
use variant_count::VariantCount;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, VariantCount)]
#[repr(u16)]
pub enum SyntaxKind {
  /// Used in the parse-event stream to represent a cancelled node.
  #[doc(hidden)]
  Tombstone,

  /// Invalid token
  TokenError,

  /// A space (U+0020) character
  TokenSpace,

  /// A newline sequence: `"\n"` or `"\r\n"`
  TokenNewline,

  TokenText,

  /// `@`
  TokenAt,

  /// `{`
  TokenOpenBrace,

  /// `}`
  TokenCloseBrace,

  /// `[`
  TokenOpenBracket,

  /// `]`
  TokenCloseBracket,

  /// `(`
  TokenOpenParen,

  /// `)`
  TokenCloseParen,

  /// `/`
  TokenSlash,

  /// `\\`
  TokenBackslash,

  /// `*`
  TokenStar,

  /// `**`
  TokenStarStar,

  /// `~`
  TokenTilde,

  /// `~~`
  TokenTildeTilde,

  /// `_`
  TokenUnderscore,

  /// `:`
  TokenColon,

  /// The keyword `admin`
  TokenAdmin,

  /// The keyword `mod`
  TokenMod,

  /// Pictogram/icon sequence
  /// Examples:
  /// - `:foo:`
  /// - `:bar:`
  NodeIcon,

  /// A newline (`\n` or `\r\n`)
  NodeNewline,

  /// Wrapper node for a single text token
  NodeText,

  /// Wrapper node for a text token representing an icon key
  NodeIconKey,

  /// `[admin]foo[/admin]` block
  NodeAdmin,

  /// `[admin]` opening tag
  NodeAdminOpen,

  /// `[/admin]` closing tag
  NodeAdminClose,

  /// `[mod]foo[/mod]` block
  NodeMod,

  /// `[mod]` opening tag
  NodeModOpen,

  /// `[/mod]` closing tag
  NodeModClose,

  /// `_foo_` span
  NodeEmphasis,

  /// `~~foo~~` span
  NodeStrikethrough,

  /// `**foo**` span
  NodeStrong,

  /// Wrapper node for some inner inline content
  NodeInlineContent,

  /// Wrapper node for some inner content
  /// This is the kind of the message root.
  NodeContent,

  /// `[foo](bar)`.
  NodeLink,

  /// `[foo]` in `[foo](bar)`.
  NodeLinkContent,

  /// `(bar)` in `[foo](bar)`.
  NodeLinkUri,
}

impl From<SyntaxKind> for rowan::SyntaxKind {
  fn from(kind: SyntaxKind) -> Self {
    Self(u16::from(kind))
  }
}

impl From<SyntaxKind> for u16 {
  fn from(value: SyntaxKind) -> Self {
    value.into_u16()
  }
}

impl TryFrom<u16> for SyntaxKind {
  type Error = ();

  fn try_from(value: u16) -> Result<Self, Self::Error> {
    if usize::from(value) < SyntaxKind::VARIANT_COUNT {
      Ok(unsafe { std::mem::transmute::<u16, SyntaxKind>(value) })
    } else {
      Err(())
    }
  }
}

impl SyntaxKind {
  // TODO: Move this function to the `From` trait once possible (requires const fn in traits)
  //       (and remove this function)
  pub const fn into_u16(self) -> u16 {
    self as u16
  }

  pub fn is_uniline(self) -> bool {
    use SyntaxKind::*;
    matches!(self, TokenText | TokenOpenBracket)
  }

  pub fn is_token(self) -> bool {
    use SyntaxKind::*;
    matches!(
      self,
      TokenError | TokenText | TokenOpenBracket | TokenCloseBracket | TokenSlash
    )
  }

  pub fn is_block(self) -> bool {
    use SyntaxKind::*;
    match self {
      NodeAdmin | NodeMod => true,
      n if n.is_inline() => true,
      _ => false,
    }
  }

  pub fn is_inline(self) -> bool {
    use SyntaxKind::*;
    matches!(
      self,
      NodeEmphasis | NodeIcon | NodeLink | NodeNewline | NodeText | NodeStrikethrough | NodeStrong
    )
  }
}

/// Enum representing the Marktwin format
/// It is used as a bridge between Rowan's untyped node and Marktwin's syntax kinds.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MktLang {}

impl rowan::Language for MktLang {
  type Kind = SyntaxKind;

  fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
    SyntaxKind::try_from(raw.0).unwrap()
  }

  fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
    rowan::SyntaxKind(u16::from(kind))
  }
}

/// Represents a Marktwin syntax token
///
/// A token is a terminal symbol of the syntax tree. It has a kind, range and text.
/// The text is owned by the token and stored as a `rowan::SmolStr`.
pub type SyntaxToken = rowan::SyntaxToken<MktLang>;

/// Represents a Marktwin syntax node
///
/// A node is a non-terminal symbol of the syntax tree. It has a kind, range and
/// child symbols. It does not own text directly: the text is retrieved by
/// iterating on the descendant children.
pub type SyntaxNode = rowan::SyntaxNode<MktLang>;
/// Represents a Marktwin syntax symbol: token (terminal) or node (non-terminal).
pub type SyntaxSymbol = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;

#[cfg(test)]
mod tests {
  use super::SyntaxKind;

  #[test]
  fn test_syntax_kind_variant_count() {
    assert_eq!(SyntaxKind::VARIANT_COUNT, 40);
  }
}