Skip to main content

asciimath_unicode/
lib.rs

1//! A module for converting asciimath to unicode
2//!
3//! To convert asciimath quickly, you can use [`parse_unicode`] to get an [`Asciimath`] value that
4//! implements [`fmt::Display`]. If you want more control, see the options exposed through [`Conf`]
5//! which can [`parse`][Conf::parse] input into [`Asciimath`] as well.
6//!
7//! # Usage
8//!
9//! ## Binary
10//!
11//! This crate provides a simple cli for converting asciimath to unicode:
12//!
13//! ```bash
14//! cargo install asciimath-unicode --features binary
15//! ```
16//!
17//! ```bash
18//! asciimath-unicode -h
19//! ```
20//!
21//! ## Library
22//!
23//! ```bash
24//! cargo add asciimath-unicode
25//! ```
26//!
27//! ```
28//! let res = asciimath_unicode::parse_unicode("1/2").to_string();
29//! assert_eq!(res, "½");
30//! ```
31//!
32//! ```
33//! use asciimath_unicode::Conf;
34//! let conf = Conf {
35//!     vulgar_fracs: false,
36//!     ..Default::default()
37//! };
38//! let res = conf.parse("1/2").to_string();
39//! assert_eq!(res, "¹⁄₂");
40//! ```
41#![forbid(unsafe_code)]
42#![warn(clippy::pedantic, missing_docs)]
43
44mod ast;
45mod block;
46mod inline;
47mod tokens;
48
49use asciimath_parser::tree::Expression;
50pub use emojis::SkinTone;
51use inline::Mapper;
52use std::fmt;
53
54/// Configuration for unicode rendering of asciimath
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
56#[allow(clippy::struct_excessive_bools)]
57pub struct Conf {
58    /// If true, this will strip unnecessary parenthesis in some contexts
59    pub strip_brackets: bool,
60    /// If true, this will try to render fractions as vulgar fractions
61    pub vulgar_fracs: bool,
62    /// If true, this will try to render fractions using super- and sub-scripts
63    pub script_fracs: bool,
64    /// Default skin tone for emojis
65    pub skin_tone: SkinTone,
66    /// If true, render as multi-line 2D block (stacked fractions, vertical scripts, matrix grids)
67    pub block: bool,
68}
69
70impl Default for Conf {
71    fn default() -> Self {
72        Conf {
73            strip_brackets: true,
74            vulgar_fracs: true,
75            script_fracs: true,
76            skin_tone: SkinTone::Default,
77            block: false,
78        }
79    }
80}
81
82impl Conf {
83    /// Parse an asciimath string into an [`Asciimath`] value that implements [`fmt::Display`]
84    #[must_use]
85    pub fn parse(self, inp: &str) -> Asciimath<'_> {
86        Asciimath {
87            conf: self,
88            expr: tokens::parse(inp),
89        }
90    }
91}
92
93/// Parsed asciimath expression ready for rendering
94///
95/// Implements [`fmt::Display`] so it can be used with `format!`, `write!`, or `.to_string()`.
96#[derive(Debug, Clone)]
97pub struct Asciimath<'a> {
98    /// Rendering configuration
99    pub conf: Conf,
100    /// The parsed expression
101    pub expr: Expression<'a>,
102}
103
104impl fmt::Display for Asciimath<'_> {
105    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106        if self.conf.block {
107            let block = self.conf.block_expression(&self.expr);
108            write!(f, "{block}")
109        } else {
110            self.conf.inline_expression(&self.expr, &mut Mapper::new(f))
111        }
112    }
113}
114
115/// Parse asciimath into an [`Asciimath`] value that implements [`fmt::Display`]
116#[must_use]
117pub fn parse_unicode(inp: &str) -> Asciimath<'_> {
118    Conf::default().parse(inp)
119}