use rustc_hash::FxHashMap;
pub type BlockId = u32;
pub type DocumentId = u32;
#[derive(Debug, Clone, PartialEq)]
pub struct Span {
pub start_line: usize,
pub start_col: usize,
pub end_line: usize,
pub end_col: usize,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum BlockType {
Heading,
Paragraph,
Code,
List,
TableCell,
TableRow,
TableAlign,
Blockquote,
HorizontalRule,
Html,
Yaml,
Toml,
Math,
Definition,
Footnote,
}
impl BlockType {
pub fn as_str(&self) -> &'static str {
match self {
BlockType::Heading => "heading",
BlockType::Paragraph => "paragraph",
BlockType::Code => "code",
BlockType::List => "list",
BlockType::TableCell => "table_cell",
BlockType::TableRow => "table_row",
BlockType::TableAlign => "table_align",
BlockType::Blockquote => "blockquote",
BlockType::HorizontalRule => "horizontal_rule",
BlockType::Html => "html",
BlockType::Yaml => "yaml",
BlockType::Toml => "toml",
BlockType::Math => "math",
BlockType::Definition => "definition",
BlockType::Footnote => "footnote",
}
}
}
impl std::fmt::Display for BlockType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum PropertyValue {
String(String),
Int(i64),
Float(f64),
Bool(bool),
Array(Vec<PropertyValue>),
Null,
}
impl PropertyValue {
pub fn as_str(&self) -> Option<&str> {
if let Self::String(s) = self {
Some(s)
} else {
None
}
}
pub fn as_int(&self) -> Option<i64> {
if let Self::Int(i) = self {
Some(*i)
} else {
None
}
}
pub fn as_bool(&self) -> Option<bool> {
if let Self::Bool(b) = self {
Some(*b)
} else {
None
}
}
pub fn as_array(&self) -> Option<&[PropertyValue]> {
if let Self::Array(a) = self {
Some(a)
} else {
None
}
}
}
impl From<String> for PropertyValue {
fn from(s: String) -> Self {
Self::String(s)
}
}
impl From<&str> for PropertyValue {
fn from(s: &str) -> Self {
Self::String(s.to_string())
}
}
impl From<i64> for PropertyValue {
fn from(i: i64) -> Self {
Self::Int(i)
}
}
impl From<u8> for PropertyValue {
fn from(i: u8) -> Self {
Self::Int(i as i64)
}
}
impl From<u32> for PropertyValue {
fn from(i: u32) -> Self {
Self::Int(i as i64)
}
}
impl From<usize> for PropertyValue {
fn from(i: usize) -> Self {
Self::Int(i as i64)
}
}
impl From<bool> for PropertyValue {
fn from(b: bool) -> Self {
Self::Bool(b)
}
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Properties(FxHashMap<String, PropertyValue>);
impl Properties {
pub fn new() -> Self {
Self::default()
}
pub fn get(&self, key: &str) -> Option<&PropertyValue> {
self.0.get(key)
}
pub fn set(&mut self, key: impl Into<String>, value: impl Into<PropertyValue>) {
self.0.insert(key.into(), value.into());
}
pub fn contains_key(&self, key: &str) -> bool {
self.0.contains_key(key)
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &PropertyValue)> {
self.0.iter()
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Block {
pub id: BlockId,
pub document_id: DocumentId,
pub block_type: BlockType,
pub content: String,
pub span: Option<Span>,
pub pre: u32,
pub post: u32,
pub properties: Properties,
}
impl Block {
pub fn contains(&self, other: &Block) -> bool {
self.pre < other.pre && other.post < self.post
}
pub fn is_under(&self, ancestor: &Block) -> bool {
ancestor.pre < self.pre && self.post < ancestor.post
}
pub fn is_under_interval(&self, anc_pre: u32, anc_post: u32) -> bool {
anc_pre < self.pre && self.post < anc_post
}
pub fn heading_depth(&self) -> Option<u8> {
self.properties.get("depth")?.as_int().map(|d| d as u8)
}
pub fn code_lang(&self) -> Option<&str> {
self.properties.get("lang")?.as_str()
}
}