Skip to main content

malachite_base/strings/
typst.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8use crate::chars::typst::fmt_typst_chars;
9use alloc::string::{String, ToString};
10use core::fmt::{Display, Formatter, Result};
11
12/// Converts a value to a Typst math-mode fragment.
13///
14/// The output is a fragment rather than a complete expression: it carries no `$` of its own,
15/// leaving that to the caller. That is what lets one fragment be embedded in another, so that a
16/// value built out of smaller values can write its parts directly.
17///
18/// Every implementation guarantees that its output
19/// - is valid wherever Typst is in math mode, and remains valid when wrapped in parentheses, so
20///   that `(`output`)` is well-formed;
21/// - leaves nothing open behind it: delimiters and string literals are closed, and nothing is
22///   defined or redefined.
23pub trait ToTypst {
24    /// Writes a value as a Typst math-mode fragment.
25    ///
26    /// This is the method implementors define. It takes a [`Formatter`] rather than returning a
27    /// `String` so that a value can write its parts into a caller's buffer, which is what makes a
28    /// fragment embeddable without an allocation per level of nesting.
29    ///
30    /// # Examples
31    /// ```
32    /// use malachite_base::strings::typst::ToTypst;
33    /// use std::fmt::{Display, Formatter, Result};
34    ///
35    /// // A type that embeds another value's fragment inside its own.
36    /// struct Negated(i32);
37    ///
38    /// impl Display for Negated {
39    ///     fn fmt(&self, f: &mut Formatter) -> Result {
40    ///         f.write_str("-(")?;
41    ///         self.0.fmt_typst(f)?;
42    ///         f.write_str(")")
43    ///     }
44    /// }
45    ///
46    /// assert_eq!(Negated(5).to_string(), "-(5)");
47    /// ```
48    /// That fragment draws the negation of five.
49    fn fmt_typst(&self, f: &mut Formatter) -> Result;
50
51    /// Converts a value to a Typst math-mode fragment.
52    ///
53    /// The returned [`TypstWrapper`] implements [`Display`], so it can be converted to a `String`
54    /// with `to_string`, or written directly with `write!` and friends.
55    ///
56    /// # Worst-case complexity
57    /// Constant time and additional memory.
58    ///
59    /// # Examples
60    /// ```
61    /// use malachite_base::strings::typst::ToTypst;
62    ///
63    /// assert_eq!(123u32.to_typst_string(), "123");
64    /// assert_eq!((-45i16).to_typst_string(), "-45");
65    ///
66    /// // The output is a fragment, so it can be embedded in a larger expression.
67    /// assert_eq!(format!("x^({})", 10u8.to_typst()), "x^(10)");
68    /// ```
69    #[inline]
70    fn to_typst(&self) -> TypstWrapper<'_, Self>
71    where
72        Self: Sized,
73    {
74        TypstWrapper { x: self }
75    }
76
77    /// Converts a value to a Typst math-mode fragment, as a [`String`].
78    ///
79    /// This is `to_typst().to_string()`, which is what a caller who wants the fragment itself,
80    /// rather than something to write into a [`Formatter`], would otherwise have to say.
81    ///
82    /// # Worst-case complexity
83    /// Same as the time and additional memory complexity of `fmt_typst` for `Self`.
84    ///
85    /// # Examples
86    /// ```
87    /// use malachite_base::strings::typst::ToTypst;
88    ///
89    /// assert_eq!(123u32.to_typst_string(), "123");
90    /// assert_eq!((-45i16).to_typst_string(), "-45");
91    /// assert_eq!("100% α".to_typst_string(), r#""100% α""#);
92    /// ```
93    #[inline]
94    fn to_typst_string(&self) -> String
95    where
96        Self: Sized,
97    {
98        self.to_typst().to_string()
99    }
100}
101
102/// A `struct` that can be used to format a value as a Typst math-mode fragment.
103///
104/// It is returned by [`ToTypst::to_typst`].
105pub struct TypstWrapper<'a, T: ToTypst> {
106    pub(crate) x: &'a T,
107}
108
109impl<T: ToTypst> Display for TypstWrapper<'_, T> {
110    #[inline]
111    fn fmt(&self, f: &mut Formatter) -> Result {
112        self.x.fmt_typst(f)
113    }
114}
115
116impl ToTypst for &str {
117    /// Writes a string slice as a Typst math-mode fragment.
118    ///
119    /// The fragment depicts the string: it is one quoted string, which Typst typesets as text, with
120    /// `\` and `"` escaped and control characters spelled rather than written. Typst reads Unicode
121    /// natively, so no character needs a spelling of its own, and `"100% α"` comes out as itself.
122    /// No quotation marks beyond the string literal's own are added; the fragment is the string's
123    /// content and nothing else.
124    ///
125    /// A run of superscript or subscript characters is the exception, and becomes a single script:
126    /// `"2¹⁰"` is two raised to the tenth rather than the two characters, which a text font may
127    /// not have at all. A run of one kind does not run into the next: `"x¹₂"` keeps its one
128    /// beside its two rather than stacking them.
129    ///
130    /// The empty string becomes `""` rather than nothing at all.
131    ///
132    /// # Worst-case complexity
133    /// $T(n) = O(n)$
134    ///
135    /// $M(n) = O(1)$
136    ///
137    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.chars().count()`.
138    ///
139    /// # Examples
140    /// ```
141    /// use malachite_base::strings::typst::ToTypst;
142    ///
143    /// assert_eq!("hello".to_typst_string(), r#""hello""#);
144    /// assert_eq!("100%".to_typst_string(), r#""100%""#);
145    /// assert_eq!("100% α".to_typst_string(), r#""100% α""#);
146    /// assert_eq!("A ≤ B".to_typst_string(), r#""A ≤ B""#);
147    /// assert_eq!("--flag".to_typst_string(), r#""--flag""#);
148    /// assert_eq!("2¹⁰".to_typst_string(), r#""2"^("10")"#);
149    /// assert_eq!("H₂O".to_typst_string(), r#""H"_("2")"O""#);
150    /// ```
151    ///
152    /// | value      | fragment     |
153    /// |------------|--------------|
154    /// | `"hello"`  | `"hello"`    |
155    /// | `"100%"`   | `"100%"`     |
156    /// | `"100% α"` | `"100% α"`   |
157    /// | `"A ≤ B"`  | `"A ≤ B"`    |
158    /// | `"--flag"` | `"--flag"`   |
159    /// | `"2¹⁰"`    | `"2"^("10")`   |
160    /// | `"H₂O"`    | `"H"_("2")"O"` |
161    #[inline]
162    fn fmt_typst(&self, f: &mut Formatter) -> Result {
163        fmt_typst_chars(self.chars(), f)
164    }
165}
166
167impl ToTypst for String {
168    /// Writes a [`String`] as a Typst math-mode fragment.
169    ///
170    /// This is the same as the [`&str`] implementation.
171    ///
172    /// The fragment depicts the string: it is one quoted string, which Typst typesets as text, with
173    /// `\` and `"` escaped and control characters spelled rather than written. Typst reads Unicode
174    /// natively, so no character needs a spelling of its own, and `"100% α"` comes out as itself.
175    /// No quotation marks beyond the string literal's own are added; the fragment is the string's
176    /// content and nothing else.
177    ///
178    /// A run of superscript or subscript characters is the exception, and becomes a single script:
179    /// `"2¹⁰"` is two raised to the tenth rather than the two characters, which a text font may
180    /// not have at all. A run of one kind does not run into the next: `"x¹₂"` keeps its one
181    /// beside its two rather than stacking them.
182    ///
183    /// The empty string becomes `""` rather than nothing at all.
184    ///
185    /// # Worst-case complexity
186    /// $T(n) = O(n)$
187    ///
188    /// $M(n) = O(1)$
189    ///
190    /// where $T$ is time, $M$ is additional memory, and $n$ is `self.chars().count()`.
191    ///
192    /// # Examples
193    /// ```
194    /// use malachite_base::strings::typst::ToTypst;
195    ///
196    /// assert_eq!("hello".to_string().to_typst_string(), r#""hello""#);
197    /// assert_eq!("100%".to_string().to_typst_string(), r#""100%""#);
198    /// assert_eq!("100% α".to_string().to_typst_string(), r#""100% α""#);
199    /// assert_eq!("A ≤ B".to_string().to_typst_string(), r#""A ≤ B""#);
200    /// assert_eq!("--flag".to_string().to_typst_string(), r#""--flag""#);
201    /// assert_eq!("2¹⁰".to_string().to_typst_string(), r#""2"^("10")"#);
202    /// assert_eq!("H₂O".to_string().to_typst_string(), r#""H"_("2")"O""#);
203    /// ```
204    ///
205    /// | value      | fragment     |
206    /// |------------|--------------|
207    /// | `"hello"`  | `"hello"`    |
208    /// | `"100%"`   | `"100%"`     |
209    /// | `"100% α"` | `"100% α"`   |
210    /// | `"A ≤ B"`  | `"A ≤ B"`    |
211    /// | `"--flag"` | `"--flag"`   |
212    /// | `"2¹⁰"`    | `"2"^("10")`   |
213    /// | `"H₂O"`    | `"H"_("2")"O"` |
214    #[inline]
215    fn fmt_typst(&self, f: &mut Formatter) -> Result {
216        fmt_typst_chars(self.chars(), f)
217    }
218}
219
220impl<T: ToTypst> ToTypst for &T {
221    /// Writes a reference as a Typst math-mode fragment.
222    ///
223    /// The fragment is the referent's own, so a reference is invisible: `&5u8` and `5u8` have the
224    /// same fragment. That is what lets a value be written without being dereferenced first, and a
225    /// collection of references be written at all.
226    ///
227    /// [`&str`] and slices have implementations of their own rather than reaching this one, since
228    /// their referents are unsized. They write the same fragments either way.
229    ///
230    /// # Worst-case complexity
231    /// Same as the time and additional memory complexity of `fmt_typst` for `T`.
232    ///
233    /// # Examples
234    /// ```
235    /// use malachite_base::strings::typst::ToTypst;
236    ///
237    /// // A reference is invisible, which is what lets a collection of references be written.
238    /// assert_eq!(
239    ///     vec![&1u8, &2u8].to_typst_string(),
240    ///     vec![1u8, 2u8].to_typst_string()
241    /// );
242    ///
243    /// // A method call on a reference resolves to the referent's own implementation, so this is
244    /// // reached through a generic context instead.
245    /// fn fragment<T: ToTypst>(x: T) -> String {
246    ///     x.to_typst_string()
247    /// }
248    /// let n = 5u8;
249    /// let n_ref: &u8 = &n;
250    /// assert_eq!(fragment(n_ref), "5");
251    /// ```
252    #[inline]
253    fn fmt_typst(&self, f: &mut Formatter) -> Result {
254        (**self).fmt_typst(f)
255    }
256}