From 8a29a460959ac3a65c45eef227294d14e8e59536 Mon Sep 17 00:00:00 2001 From: PoiScript Date: Thu, 9 May 2024 11:02:46 +0800 Subject: [PATCH] chore: replace some debug_assert with explicit panic --- examples/markdown.rs | 2 +- src/ast/affiliated_keyword.rs | 5 +-- src/ast/block.rs | 31 ++++++++++--- src/ast/clock.rs | 6 +-- src/ast/drawer.rs | 46 +++++++++++++------- src/ast/headline.rs | 4 +- src/ast/inline_call.rs | 37 +++++++--------- src/ast/inline_src.rs | 16 +++---- src/ast/keyword.rs | 7 +-- src/ast/link.rs | 12 ++--- src/ast/list.rs | 5 +-- src/ast/macros.rs | 5 +-- src/ast/mod.rs | 82 +++++++++++++++++++---------------- src/ast/snippet.rs | 10 +---- src/export/html.rs | 14 +++--- src/export/traverse.rs | 2 +- 16 files changed, 145 insertions(+), 139 deletions(-) diff --git a/examples/markdown.rs b/examples/markdown.rs index 353785a..137c1db 100644 --- a/examples/markdown.rs +++ b/examples/markdown.rs @@ -15,7 +15,7 @@ fn main() { let content = fs::read_to_string(&args[1]).unwrap(); let mut export = MarkdownExport::default(); - Org::parse(&content).traverse(&mut export); + Org::parse(content).traverse(&mut export); fs::write(format!("{}.md", &args[1]), export.finish()).unwrap(); diff --git a/src/ast/affiliated_keyword.rs b/src/ast/affiliated_keyword.rs index ed8ff6c..556c002 100644 --- a/src/ast/affiliated_keyword.rs +++ b/src/ast/affiliated_keyword.rs @@ -14,10 +14,7 @@ impl AffiliatedKeyword { self.syntax .children_with_tokens() .find_map(filter_token(SyntaxKind::TEXT)) - .unwrap_or_else(|| { - debug_assert!(false, "keyword must contains TEXT"); - Token::default() - }) + .expect("keyword must contains TEXT") } /// diff --git a/src/ast/block.rs b/src/ast/block.rs index 4b39b0f..bd0579c 100644 --- a/src/ast/block.rs +++ b/src/ast/block.rs @@ -74,10 +74,19 @@ impl SourceBlock { /// ```rust /// use orgize::{Org, ast::SourceBlock}; /// - /// let block = Org::parse("#+begin_src\n#+end_src").first_node::().unwrap(); + /// let block = Org::parse(r#" + /// #+begin_src + /// #+end_src + /// "#).first_node::().unwrap(); /// assert_eq!(block.value(), ""); - /// let block = Org::parse("#+begin_src\n,* foo \n,#+ bar\n#+end_src").first_node::().unwrap(); - /// assert_eq!(block.value(), "* foo \n#+ bar\n"); + /// + /// let block = Org::parse(r#" + /// #+begin_src + /// ,* foo + /// ,#+ bar + /// #+end_src + /// "#).first_node::().unwrap(); + /// assert_eq!(block.value(), "* foo\n#+ bar\n"); /// ```` pub fn value(&self) -> String { self.syntax @@ -116,17 +125,25 @@ macro_rules! impl_content_border { /// Beginning position of block content pub fn content_start(&self) -> TextSize { self.syntax - .first_child() + .children() + .find(|n| n.kind() == SyntaxKind::BLOCK_BEGIN) .map(|n| n.text_range().end()) - .unwrap_or_else(|| self.syntax.text_range().start()) + .unwrap_or_else(|| { + debug_assert!(false, "block must contains BLOCK_BEGIN"); + TextSize::default() + }) } /// Ending position of block content pub fn content_end(&self) -> TextSize { self.syntax - .last_child() + .children() + .find(|n| n.kind() == SyntaxKind::BLOCK_END) .map(|n| n.text_range().start()) - .unwrap_or_else(|| self.syntax.text_range().end()) + .unwrap_or_else(|| { + debug_assert!(false, "block must contains BLOCK_END"); + TextSize::default() + }) } } }; diff --git a/src/ast/clock.rs b/src/ast/clock.rs index 5317ff6..e8df645 100644 --- a/src/ast/clock.rs +++ b/src/ast/clock.rs @@ -24,9 +24,9 @@ impl Clock { .skip_while(|t| t.kind() != SyntaxKind::DOUBLE_ARROW) .skip(1) .find(|t| t.kind() != SyntaxKind::WHITESPACE) - .map(|e| { - debug_assert!(e.kind() == SyntaxKind::TEXT); - Token(e.into_token()) + .and_then(|e| { + debug_assert_eq!(e.kind(), SyntaxKind::TEXT); + Some(Token(e.into_token()?)) }) } diff --git a/src/ast/drawer.rs b/src/ast/drawer.rs index 4ee6071..3111ee6 100644 --- a/src/ast/drawer.rs +++ b/src/ast/drawer.rs @@ -65,17 +65,25 @@ impl PropertyDrawer { /// Beginning position of drawer content pub fn content_start(&self) -> TextSize { self.syntax - .first_child() + .children() + .find(|n| n.kind() == SyntaxKind::DRAWER_BEGIN) .map(|n| n.text_range().end()) - .unwrap_or_else(|| self.syntax.text_range().start()) + .unwrap_or_else(|| { + debug_assert!(false, "property drawer must contains DRAWER_BEGIN"); + TextSize::default() + }) } /// Ending position of drawer content pub fn content_end(&self) -> TextSize { self.syntax - .last_child() + .children() + .find(|n| n.kind() == SyntaxKind::DRAWER_END) .map(|n| n.text_range().start()) - .unwrap_or_else(|| self.syntax.text_range().end()) + .unwrap_or_else(|| { + debug_assert!(false, "property drawer must contains DRAWER_END"); + TextSize::default() + }) } } @@ -89,30 +97,36 @@ impl Drawer { /// ``` pub fn name(&self) -> Token { self.syntax - .first_child() - .and_then(|n| { - n.children_with_tokens() - .filter_map(|e| e.into_token()) - .find(|e| e.kind() == SyntaxKind::TEXT) - }) - .map(|t| Token(Some(t))) - .unwrap_or_default() + .children() + .find(|n| n.kind() == SyntaxKind::DRAWER_BEGIN) + .expect("drawer must contains DRAWER_BEGIN") + .children_with_tokens() + .find_map(filter_token(SyntaxKind::TEXT)) + .expect("drawer begin must contains TEXT") } /// Beginning position of drawer content pub fn content_start(&self) -> TextSize { self.syntax - .first_child() + .children() + .find(|n| n.kind() == SyntaxKind::DRAWER_BEGIN) .map(|n| n.text_range().end()) - .unwrap_or_else(|| self.syntax.text_range().start()) + .unwrap_or_else(|| { + debug_assert!(false, "drawer must contains DRAWER_BEGIN"); + TextSize::default() + }) } /// Ending position of drawer content pub fn content_end(&self) -> TextSize { self.syntax - .last_child() + .children() + .find(|n| n.kind() == SyntaxKind::DRAWER_END) .map(|n| n.text_range().start()) - .unwrap_or_else(|| self.syntax.text_range().end()) + .unwrap_or_else(|| { + debug_assert!(false, "drawer must contains DRAWER_END"); + TextSize::default() + }) } /// Raw text of drawer content diff --git a/src/ast/headline.rs b/src/ast/headline.rs index 304914b..a465053 100644 --- a/src/ast/headline.rs +++ b/src/ast/headline.rs @@ -27,7 +27,7 @@ impl Headline { .find_map(filter_token(SyntaxKind::HEADLINE_STARS)) .map_or_else( || { - debug_assert!(false, "headline must contains starts token"); + debug_assert!(false, "headline must contains HEADLINE_STARS"); 0 }, |stars| stars.len(), @@ -48,7 +48,7 @@ impl Headline { if tk.kind() == SyntaxKind::HEADLINE_KEYWORD_TODO || tk.kind() == SyntaxKind::HEADLINE_KEYWORD_DONE => { - Some(Token(Some(tk))) + Some(Token(tk)) } _ => None, }) diff --git a/src/ast/inline_call.rs b/src/ast/inline_call.rs index 6994295..4dc2c04 100644 --- a/src/ast/inline_call.rs +++ b/src/ast/inline_call.rs @@ -15,10 +15,7 @@ impl InlineCall { .children_with_tokens() .filter_map(filter_token(SyntaxKind::TEXT)) .nth(1) - .unwrap_or_else(|| { - debug_assert!(false, "inline call must contains two TEXT"); - Token::default() - }) + .expect("inline call must contains two TEXT") } /// @@ -27,15 +24,19 @@ impl InlineCall { /// /// let call = Org::parse("call_square[:results output](4)").first_node::().unwrap(); /// assert_eq!(call.inside_header().unwrap(), ":results output"); + /// + /// let call = Org::parse("call_square(4)[:results html]").first_node::().unwrap(); + /// assert!(call.inside_header().is_none()); /// ``` pub fn inside_header(&self) -> Option { self.syntax .children_with_tokens() + .take_while(|e| e.kind() != SyntaxKind::L_PARENS) .skip_while(|e| e.kind() != SyntaxKind::L_BRACKET) .nth(1) - .map(|e| { - debug_assert!(e.kind() == SyntaxKind::TEXT); - Token(e.into_token()) + .and_then(|e| { + debug_assert_eq!(e.kind(), SyntaxKind::TEXT); + Some(Token(e.into_token()?)) }) } @@ -50,17 +51,8 @@ impl InlineCall { self.syntax .children_with_tokens() .skip_while(|e| e.kind() != SyntaxKind::L_PARENS) - .nth(1) - .map_or_else( - || { - debug_assert!(false); - Token::default() - }, - |e| { - debug_assert!(e.kind() == SyntaxKind::TEXT); - Token(e.into_token()) - }, - ) + .find_map(filter_token(SyntaxKind::TEXT)) + .expect("inline call must contains TEXT after L_PARENS") } /// @@ -69,6 +61,9 @@ impl InlineCall { /// /// let call = Org::parse("call_square[:results output](4)[:results html]").first_node::().unwrap(); /// assert_eq!(call.end_header().unwrap(), ":results html"); + /// + /// let call = Org::parse("call_square[:results output](4)").first_node::().unwrap(); + /// assert!(call.end_header().is_none()); /// ``` pub fn end_header(&self) -> Option { self.syntax @@ -77,9 +72,9 @@ impl InlineCall { .skip(1) .skip_while(|e| e.kind() != SyntaxKind::L_BRACKET) .nth(1) - .map(|e| { - debug_assert!(e.kind() == SyntaxKind::TEXT); - Token(e.into_token()) + .and_then(|e| { + debug_assert_eq!(e.kind(), SyntaxKind::TEXT); + Some(Token(e.into_token()?)) }) } } diff --git a/src/ast/inline_src.rs b/src/ast/inline_src.rs index 163e845..87aff35 100644 --- a/src/ast/inline_src.rs +++ b/src/ast/inline_src.rs @@ -18,10 +18,7 @@ impl InlineSrc { .children_with_tokens() .nth(1) .and_then(filter_token(SyntaxKind::TEXT)) - .unwrap_or_else(|| { - debug_assert!(false, "inline src must contains TEXT"); - Token::default() - }) + .expect("inline src must contains TEXT") } /// Optional header arguments @@ -39,9 +36,9 @@ impl InlineSrc { .children_with_tokens() .skip_while(|n| n.kind() != SyntaxKind::L_BRACKET) .nth(1) - .map(|n| { - debug_assert!(n.kind() == SyntaxKind::TEXT); - Token(n.into_token()) + .and_then(|n| { + debug_assert_eq!(n.kind(), SyntaxKind::TEXT); + Some(Token(n.into_token()?)) }) } @@ -60,9 +57,6 @@ impl InlineSrc { .children_with_tokens() .filter_map(filter_token(SyntaxKind::TEXT)) .last() - .unwrap_or_else(|| { - debug_assert!(false, "inline src must contains TEXT"); - Token::default() - }) + .expect("inline src must contains TEXT") } } diff --git a/src/ast/keyword.rs b/src/ast/keyword.rs index 86c7aba..cf0a6f4 100644 --- a/src/ast/keyword.rs +++ b/src/ast/keyword.rs @@ -14,10 +14,7 @@ impl Keyword { self.syntax .children_with_tokens() .find_map(filter_token(SyntaxKind::TEXT)) - .unwrap_or_else(|| { - debug_assert!(false, "keyword must contains TEXT"); - Token::default() - }) + .expect("keyword must contains TEXT") } /// @@ -34,6 +31,6 @@ impl Keyword { .children_with_tokens() .filter_map(filter_token(SyntaxKind::TEXT)) .nth(1) - .unwrap_or_default() + .expect("keyword must contains two TEXT") } } diff --git a/src/ast/link.rs b/src/ast/link.rs index 48f55d2..daff761 100644 --- a/src/ast/link.rs +++ b/src/ast/link.rs @@ -1,6 +1,6 @@ -use rowan::ast::{support, AstNode}; +use rowan::ast::AstNode; -use super::{AffiliatedKeyword, Link, Paragraph, Token}; +use super::{token, AffiliatedKeyword, Link, Paragraph, Token}; use crate::{syntax::SyntaxKind, SyntaxElement}; impl Link { @@ -17,13 +17,7 @@ impl Link { /// assert_eq!(link.path(), "https://google.com"); /// ``` pub fn path(&self) -> Token { - support::token(&self.syntax, SyntaxKind::LINK_PATH).map_or_else( - || { - debug_assert!(false, "link must contains LINK_PATH"); - Token::default() - }, - |e| Token(Some(e)), - ) + token(&self.syntax, SyntaxKind::LINK_PATH).expect("link must contains LINK_PATH") } /// Returns `true` if link contains description diff --git a/src/ast/list.rs b/src/ast/list.rs index 27aec7c..d53bbc9 100644 --- a/src/ast/list.rs +++ b/src/ast/list.rs @@ -85,10 +85,7 @@ impl ListItem { self.syntax .children_with_tokens() .find_map(filter_token(SyntaxKind::LIST_ITEM_BULLET)) - .unwrap_or_else(|| { - debug_assert!(false, "list item must contains LIST_ITEM_BULLET"); - Token::default() - }) + .expect("list item must contains LIST_ITEM_BULLET") } /// ```rust diff --git a/src/ast/macros.rs b/src/ast/macros.rs index e315e4c..91dfa57 100644 --- a/src/ast/macros.rs +++ b/src/ast/macros.rs @@ -15,10 +15,7 @@ impl Macros { self.syntax .children_with_tokens() .find_map(filter_token(SyntaxKind::TEXT)) - .unwrap_or_else(|| { - debug_assert!(false, "macros must contains TEXT"); - Token::default() - }) + .expect("macros must contains TEXT") } /// ```rust diff --git a/src/ast/mod.rs b/src/ast/mod.rs index b54acd7..a4c7355 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -1,7 +1,5 @@ -#[rustfmt::skip] mod generated; - mod affiliated_keyword; mod block; mod clock; @@ -22,13 +20,6 @@ mod snippet; mod table; mod timestamp; -use std::{ - borrow::{Borrow, Cow}, - fmt::Debug, - hash::Hash, - ops::Deref, -}; - pub use generated::*; pub use headline::*; pub use rowan::ast::support::*; @@ -38,7 +29,13 @@ use crate::{ syntax::{SyntaxKind, SyntaxNode}, SyntaxToken, }; -use rowan::{ast::AstNode, NodeOrToken, TextSize}; +use rowan::{ast::AstNode, NodeOrToken, TextRange, TextSize}; +use std::{ + borrow::{Borrow, Cow}, + fmt, + hash::Hash, + ops::Deref, +}; pub fn blank_lines(parent: &SyntaxNode) -> usize { parent @@ -59,52 +56,55 @@ pub fn last_token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { } pub fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { - rowan::ast::support::token(parent, kind).map(|t| Token(Some(t))) + rowan::ast::support::token(parent, kind).map(Token) } pub fn filter_token( kind: SyntaxKind, ) -> impl Fn(NodeOrToken) -> Option { move |elem| match elem { - NodeOrToken::Token(tk) if tk.kind() == kind => Some(Token(Some(tk))), + NodeOrToken::Token(tk) if tk.kind() == kind => Some(Token(tk)), _ => None, } } -/// A simple wrapper of `Option` +/// A simple wrapper of `SyntaxToken` /// -/// It acts like a `token.text()` when inner is `Some(token)`, and an empty string when `None`. -#[derive(Default, Eq, Clone)] -pub struct Token(pub(crate) Option); +/// It implements the `AsRef` and `Display` trait, +/// allowing to directly use some `str` methods. +/// +/// Also it implements `Hash` and `Eq` traits, so can be +/// used as keys in `HashMap`. However, note that it only +/// compares the underlying text inside `SyntaxToken`, +/// meaning two `Token`s from different positions +/// might be considered equal. +#[derive(Eq, Clone)] +pub struct Token(pub(crate) SyntaxToken); impl Token { - pub fn syntax(&self) -> Option<&SyntaxToken> { - self.0.as_ref() + pub fn syntax(&self) -> &SyntaxToken { + &self.0 } -} -impl Token { + /// Range of this token + pub fn text_range(&self) -> TextRange { + self.0.text_range() + } + + /// Beginning position of this token pub fn start(&self) -> TextSize { - match &self.0 { - Some(t) => t.text_range().start(), - None => TextSize::new(0), - } + self.0.text_range().start() } + /// Ending position of this token pub fn end(&self) -> TextSize { - match &self.0 { - Some(t) => t.text_range().end(), - None => TextSize::new(0), - } + self.0.text_range().end() } } impl AsRef for Token { fn as_ref(&self) -> &str { - match &self.0 { - Some(t) => t.text(), - None => "", - } + self.0.text() } } @@ -114,12 +114,6 @@ impl Borrow for Token { } } -impl Debug for Token { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.as_ref().fmt(f) - } -} - impl<'a> PartialEq<&'a str> for Token { fn eq(&self, other: &&'a str) -> bool { self.as_ref() == *other @@ -164,3 +158,15 @@ impl Deref for Token { self.as_ref() } } + +impl fmt::Debug for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(self.0.text(), f) + } +} + +impl fmt::Display for Token { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Display::fmt(self.0.text(), f) + } +} diff --git a/src/ast/snippet.rs b/src/ast/snippet.rs index e80e7e1..393713b 100644 --- a/src/ast/snippet.rs +++ b/src/ast/snippet.rs @@ -13,10 +13,7 @@ impl Snippet { self.syntax .children_with_tokens() .find_map(filter_token(SyntaxKind::TEXT)) - .unwrap_or_else(|| { - debug_assert!(false, "snippet must contains TEXT"); - Token::default() - }) + .expect("snippet must contains TEXT") } /// ```rust @@ -32,9 +29,6 @@ impl Snippet { .children_with_tokens() .filter_map(filter_token(SyntaxKind::TEXT)) .nth(1) - .unwrap_or_else(|| { - debug_assert!(false, "snippet must contains two TEXT"); - Token::default() - }) + .expect("snippet must contains two TEXT") } } diff --git a/src/export/html.rs b/src/export/html.rs index 9e3bdc5..fa31d42 100644 --- a/src/export/html.rs +++ b/src/export/html.rs @@ -132,11 +132,15 @@ impl Traverser for HtmlExport { Event::Leave(Container::Code(_)) => self.output += "", Event::Enter(Container::SourceBlock(block)) => { - let _ = write!( - &mut self.output, - r#"
"#,
-                    HtmlEscape(&block.language().unwrap_or_default())
-                );
+                if let Some(language) = block.language() {
+                    let _ = write!(
+                        &mut self.output,
+                        r#"
"#,
+                        HtmlEscape(&language)
+                    );
+                } else {
+                    self.output += r#"
"#
+                }
             }
             Event::Leave(Container::SourceBlock(_)) => self.output += "
", diff --git a/src/export/traverse.rs b/src/export/traverse.rs index 2ad752c..1cff4a1 100644 --- a/src/export/traverse.rs +++ b/src/export/traverse.rs @@ -210,7 +210,7 @@ pub trait Traverser { } SyntaxElement::Token(token) => { if token.kind() == TEXT { - self.event(Event::Text(Token(Some(token))), ctx); + self.event(Event::Text(Token(token)), ctx); take_control!(); } }