diff options
Diffstat (limited to 'library/src/utility/string.rs')
| -rw-r--r-- | library/src/utility/string.rs | 141 |
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] = &['*', '†', '‡', '§', '‖', '¶']; |
