diff options
Diffstat (limited to 'src/util')
-rw-r--r-- | src/util/line_ending.rs | 129 | ||||
-rw-r--r-- | src/util/mdx.rs | 131 | ||||
-rw-r--r-- | src/util/mod.rs | 2 |
3 files changed, 262 insertions, 0 deletions
diff --git a/src/util/line_ending.rs b/src/util/line_ending.rs new file mode 100644 index 0000000..a018fe8 --- /dev/null +++ b/src/util/line_ending.rs @@ -0,0 +1,129 @@ +extern crate alloc; + +use alloc::{str::FromStr, string::String}; + +/// Type of line endings in markdown. +/// +/// Particularly when working with Windows, you might want to use +/// `LineEnding::CarriageReturnLineFeed`. +/// +/// ## Examples +/// +/// ``` +/// use markdown::LineEnding; +/// # fn main() { +/// +/// // Use a CR + LF combination: +/// let crlf = LineEnding::CarriageReturnLineFeed; +/// # } +/// ``` +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub enum LineEnding { + /// Both a carriage return (`\r`) and a line feed (`\n`). + /// + /// ## Example + /// + /// ```markdown + /// a␍␊ + /// b + /// ``` + CarriageReturnLineFeed, + /// Sole carriage return (`\r`). + /// + /// ## Example + /// + /// ```markdown + /// a␍ + /// b + /// ``` + CarriageReturn, + /// Sole line feed (`\n`). + /// + /// ## Example + /// + /// ```markdown + /// a␊ + /// b + /// ``` + #[default] + LineFeed, +} + +// xxxxxxxxxxxxxxx +impl LineEnding { + /// Turn the line ending into a [str]. + #[must_use] + pub fn as_str(&self) -> &str { + match self { + LineEnding::CarriageReturnLineFeed => "\r\n", + LineEnding::CarriageReturn => "\r", + LineEnding::LineFeed => "\n", + } + } +} + +impl FromStr for LineEnding { + type Err = String; + + /// Turn a string into a line ending. + /// + /// ## Panics + /// + /// Panics if `code` is not `\r\n`, `\r`, or `\n`. + fn from_str(s: &str) -> Result<Self, Self::Err> { + match s { + "\r\n" => Ok(LineEnding::CarriageReturnLineFeed), + "\r" => Ok(LineEnding::CarriageReturn), + "\n" => Ok(LineEnding::LineFeed), + _ => Err("Expected CR, LF, or CRLF".into()), + } + } +} + +#[cfg(test)] +mod tests { + extern crate std; + use super::*; + + #[test] + fn test_line_ending() { + assert_eq!( + "\r".parse(), + Ok(LineEnding::CarriageReturn), + "should support turning a string into a carriage return" + ); + assert_eq!( + LineEnding::CarriageReturn.as_str(), + "\r", + "should support turning a carriage return into a string" + ); + + assert_eq!( + "\n".parse(), + Ok(LineEnding::LineFeed), + "should support turning a string into a line feed" + ); + assert_eq!( + LineEnding::LineFeed.as_str(), + "\n", + "should support turning a line feed into a string" + ); + + assert_eq!( + "\r\n".parse(), + Ok(LineEnding::CarriageReturnLineFeed), + "should support turning a string into a carriage return + line feed" + ); + assert_eq!( + LineEnding::CarriageReturnLineFeed.as_str(), + "\r\n", + "should support turning a carriage return + line feed into a string" + ); + + assert_eq!( + "aaa".parse::<LineEnding>(), + Err("Expected CR, LF, or CRLF".into()), + "should error when parsing a non-eol" + ); + } +} diff --git a/src/util/mdx.rs b/src/util/mdx.rs new file mode 100644 index 0000000..712b2c7 --- /dev/null +++ b/src/util/mdx.rs @@ -0,0 +1,131 @@ +extern crate alloc; + +use alloc::string::String; + +/// Signal used as feedback when parsing MDX ESM/expressions. +#[derive(Clone, Debug)] +pub enum Signal { + /// A syntax error. + /// + /// `markdown-rs` will crash with error message `String`, and convert the + /// `usize` (byte offset into `&str` passed to `MdxExpressionParse` or + /// `MdxEsmParse`) to where it happened in the whole document. + /// + /// ## Examples + /// + /// ```rust ignore + /// Signal::Error("Unexpected `\"`, expected identifier".into(), 1) + /// ``` + Error(String, usize), + /// An error at the end of the (partial?) expression. + /// + /// `markdown-rs` will either crash with error message `String` if it + /// doesn’t have any more text, or it will try again later when more text + /// is available. + /// + /// ## Examples + /// + /// ```rust ignore + /// Signal::Eof("Unexpected end of file in string literal".into()) + /// ``` + Eof(String), + /// Done, successfully. + /// + /// `markdown-rs` knows that this is the end of a valid expression/esm and + /// continues with markdown. + /// + /// ## Examples + /// + /// ```rust ignore + /// Signal::Ok + /// ``` + Ok, +} + +/// Signature of a function that parses MDX ESM. +/// +/// Can be passed as `mdx_esm_parse` in [`ParseOptions`][] to support +/// ESM according to a certain grammar (typically, a programming language). +pub type EsmParse = dyn Fn(&str) -> Signal; + +/// Expression kind. +#[derive(Clone, Debug)] +pub enum ExpressionKind { + /// Kind of expressions in prose. + /// + /// ```mdx + /// > | # {Math.PI} + /// ^^^^^^^^^ + /// | + /// > | {Math.PI} + /// ^^^^^^^^^ + /// ``` + Expression, + /// Kind of expressions as attributes. + /// + /// ```mdx + /// > | <a {...b}> + /// ^^^^^^ + /// ``` + AttributeExpression, + /// Kind of expressions as attribute values. + /// + /// ```mdx + /// > | <a b={c}> + /// ^^^ + /// ``` + AttributeValueExpression, +} + +/// Signature of a function that parses MDX expressions. +/// +/// Can be passed as `mdx_expression_parse` in [`ParseOptions`][] to support +/// expressions according to a certain grammar (typically, a programming +/// language). +/// +pub type ExpressionParse = dyn Fn(&str, &ExpressionKind) -> Signal; + +#[cfg(test)] +mod tests { + extern crate std; + use super::*; + use alloc::boxed::Box; + + #[test] + fn test_mdx_expression_parse() { + fn func(_value: &str, _kind: &ExpressionKind) -> Signal { + Signal::Ok + } + + let func_accepting = |_a: Box<ExpressionParse>| true; + + assert!( + matches!(func("a", &ExpressionKind::Expression), Signal::Ok), + "should expose an `ExpressionParse` type (1)" + ); + + assert!( + func_accepting(Box::new(func)), + "should expose an `ExpressionParse` type (2)" + ); + } + + #[test] + fn test_mdx_esm_parse() { + fn func(_value: &str) -> Signal { + Signal::Ok + } + + let func_accepting = |_a: Box<EsmParse>| true; + + assert!( + matches!(func("a"), Signal::Ok), + "should expose an `EsmParse` type (1)" + ); + + assert!( + func_accepting(Box::new(func)), + "should expose an `EsmParse` type (2)" + ); + } +} diff --git a/src/util/mod.rs b/src/util/mod.rs index f44e183..cb9a40b 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -8,7 +8,9 @@ pub mod encode; pub mod gfm_tagfilter; pub mod identifier; pub mod infer; +pub mod line_ending; pub mod location; +pub mod mdx; pub mod mdx_collect; pub mod normalize_identifier; pub mod sanitize_uri; |