summaryrefslogtreecommitdiff
path: root/library/src/utility/string.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-11-03 11:44:53 +0100
committerLaurenz <laurmaedje@gmail.com>2022-11-03 13:35:39 +0100
commit37a7afddfaffd44cb9bc013c9506599267e08983 (patch)
tree20e7d62d3c5418baff01a21d0406b91bf3096214 /library/src/utility/string.rs
parent56342bd972a13ffe21beaf2b87ab7eb1597704b4 (diff)
Split crates
Diffstat (limited to 'library/src/utility/string.rs')
-rw-r--r--library/src/utility/string.rs141
1 files changed, 141 insertions, 0 deletions
diff --git a/library/src/utility/string.rs b/library/src/utility/string.rs
new file mode 100644
index 00000000..ed444d35
--- /dev/null
+++ b/library/src/utility/string.rs
@@ -0,0 +1,141 @@
+use typst::model::Regex;
+
+use crate::prelude::*;
+
+/// The string representation of a value.
+pub fn repr(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
+ Ok(args.expect::<Value>("value")?.repr().into())
+}
+
+/// Convert a value to a string.
+pub fn str(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
+ let Spanned { v, span } = args.expect("value")?;
+ Ok(Value::Str(match v {
+ Value::Int(v) => format_str!("{}", v),
+ Value::Float(v) => format_str!("{}", v),
+ Value::Str(v) => v,
+ v => bail!(span, "cannot convert {} to string", v.type_name()),
+ }))
+}
+
+/// Create blind text.
+pub fn lorem(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
+ let words: usize = args.expect("number of words")?;
+ Ok(Value::Str(lipsum::lipsum(words).into()))
+}
+
+/// Create a regular expression.
+pub fn regex(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
+ let Spanned { v, span } = args.expect::<Spanned<EcoString>>("regular expression")?;
+ Ok(Regex::new(&v).at(span)?.into())
+}
+
+/// Converts an integer into one or multiple letters.
+pub fn letter(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
+ numbered(Numbering::Letter, args)
+}
+
+/// Converts an integer into a roman numeral.
+pub fn roman(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
+ numbered(Numbering::Roman, args)
+}
+
+/// Convert a number into a symbol.
+pub fn symbol(_: &mut Vm, args: &mut Args) -> SourceResult<Value> {
+ numbered(Numbering::Symbol, args)
+}
+
+fn numbered(numbering: Numbering, args: &mut Args) -> SourceResult<Value> {
+ let n = args.expect::<usize>("non-negative integer")?;
+ Ok(Value::Str(numbering.apply(n).into()))
+}
+
+/// Allows to convert a number into letters, roman numerals and symbols.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub enum Numbering {
+ Arabic,
+ Letter,
+ Roman,
+ Symbol,
+}
+
+impl Numbering {
+ /// Apply the numbering to the given number.
+ pub fn apply(self, mut n: usize) -> EcoString {
+ match self {
+ Self::Arabic => {
+ format_eco!("{}", n)
+ }
+ Self::Letter => {
+ if n == 0 {
+ return '-'.into();
+ }
+
+ n -= 1;
+
+ let mut letters = vec![];
+ loop {
+ letters.push(b'a' + (n % 26) as u8);
+ n /= 26;
+ if n == 0 {
+ break;
+ }
+ }
+
+ letters.reverse();
+ String::from_utf8(letters).unwrap().into()
+ }
+ Self::Roman => {
+ if n == 0 {
+ return 'N'.into();
+ }
+
+ // Adapted from Yann Villessuzanne's roman.rs under the Unlicense, at
+ // https://github.com/linfir/roman.rs/
+ let mut fmt = EcoString::new();
+ for &(name, value) in ROMANS {
+ while n >= value {
+ n -= value;
+ fmt.push_str(name);
+ }
+ }
+
+ fmt
+ }
+ Self::Symbol => {
+ if n == 0 {
+ return '-'.into();
+ }
+
+ let symbol = SYMBOLS[(n - 1) % SYMBOLS.len()];
+ let amount = ((n - 1) / SYMBOLS.len()) + 1;
+ std::iter::repeat(symbol).take(amount).collect()
+ }
+ }
+ }
+}
+
+const ROMANS: &[(&str, usize)] = &[
+ ("M̅", 1000000),
+ ("D̅", 500000),
+ ("C̅", 100000),
+ ("L̅", 50000),
+ ("X̅", 10000),
+ ("V̅", 5000),
+ ("I̅V̅", 4000),
+ ("M", 1000),
+ ("CM", 900),
+ ("D", 500),
+ ("CD", 400),
+ ("C", 100),
+ ("XC", 90),
+ ("L", 50),
+ ("XL", 40),
+ ("X", 10),
+ ("IX", 9),
+ ("V", 5),
+ ("IV", 4),
+ ("I", 1),
+];
+
+const SYMBOLS: &[char] = &['*', '†', '‡', '§', '‖', '¶'];