chore: replace some debug_assert with explicit panic
This commit is contained in:
parent
b9a3c7a889
commit
8a29a46095
16 changed files with 145 additions and 139 deletions
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
||||
///
|
||||
|
|
|
|||
|
|
@ -74,10 +74,19 @@ impl SourceBlock {
|
|||
/// ```rust
|
||||
/// use orgize::{Org, ast::SourceBlock};
|
||||
///
|
||||
/// let block = Org::parse("#+begin_src\n#+end_src").first_node::<SourceBlock>().unwrap();
|
||||
/// let block = Org::parse(r#"
|
||||
/// #+begin_src
|
||||
/// #+end_src
|
||||
/// "#).first_node::<SourceBlock>().unwrap();
|
||||
/// assert_eq!(block.value(), "");
|
||||
/// let block = Org::parse("#+begin_src\n,* foo \n,#+ bar\n#+end_src").first_node::<SourceBlock>().unwrap();
|
||||
/// assert_eq!(block.value(), "* foo \n#+ bar\n");
|
||||
///
|
||||
/// let block = Org::parse(r#"
|
||||
/// #+begin_src
|
||||
/// ,* foo
|
||||
/// ,#+ bar
|
||||
/// #+end_src
|
||||
/// "#).first_node::<SourceBlock>().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()
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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()?))
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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::<InlineCall>().unwrap();
|
||||
/// assert_eq!(call.inside_header().unwrap(), ":results output");
|
||||
///
|
||||
/// let call = Org::parse("call_square(4)[:results html]").first_node::<InlineCall>().unwrap();
|
||||
/// assert!(call.inside_header().is_none());
|
||||
/// ```
|
||||
pub fn inside_header(&self) -> Option<Token> {
|
||||
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::<InlineCall>().unwrap();
|
||||
/// assert_eq!(call.end_header().unwrap(), ":results html");
|
||||
///
|
||||
/// let call = Org::parse("call_square[:results output](4)").first_node::<InlineCall>().unwrap();
|
||||
/// assert!(call.end_header().is_none());
|
||||
/// ```
|
||||
pub fn end_header(&self) -> Option<Token> {
|
||||
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()?))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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<Token> {
|
|||
}
|
||||
|
||||
pub fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option<Token> {
|
||||
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<SyntaxNode, SyntaxToken>) -> Option<Token> {
|
||||
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<SyntaxToken>`
|
||||
/// 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<SyntaxToken>);
|
||||
/// It implements the `AsRef<str>` 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<str> for Token {
|
||||
fn as_ref(&self) -> &str {
|
||||
match &self.0 {
|
||||
Some(t) => t.text(),
|
||||
None => "",
|
||||
}
|
||||
self.0.text()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -114,12 +114,6 @@ impl Borrow<str> 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,11 +132,15 @@ impl Traverser for HtmlExport {
|
|||
Event::Leave(Container::Code(_)) => self.output += "</code>",
|
||||
|
||||
Event::Enter(Container::SourceBlock(block)) => {
|
||||
let _ = write!(
|
||||
&mut self.output,
|
||||
r#"<pre><code class="language-{}">"#,
|
||||
HtmlEscape(&block.language().unwrap_or_default())
|
||||
);
|
||||
if let Some(language) = block.language() {
|
||||
let _ = write!(
|
||||
&mut self.output,
|
||||
r#"<pre><code class="language-{}">"#,
|
||||
HtmlEscape(&language)
|
||||
);
|
||||
} else {
|
||||
self.output += r#"<pre><code>"#
|
||||
}
|
||||
}
|
||||
Event::Leave(Container::SourceBlock(_)) => self.output += "</code></pre>",
|
||||
|
||||
|
|
|
|||
|
|
@ -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!();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue