summaryrefslogtreecommitdiff
path: root/src/library/utility/string.rs
diff options
context:
space:
mode:
authorLaurenz <laurmaedje@gmail.com>2022-03-18 23:36:18 +0100
committerLaurenz <laurmaedje@gmail.com>2022-03-18 23:43:58 +0100
commitbeca01c826ee51c9ee6d5eadd7e5ef10f7fb9f58 (patch)
treee0ebb40b8775bba3b4be7bc47dceda3d349e2ac0 /src/library/utility/string.rs
parent77d153d315a2a5909840ebcd47491e4cef14428b (diff)
Methods
Diffstat (limited to 'src/library/utility/string.rs')
-rw-r--r--src/library/utility/string.rs148
1 files changed, 148 insertions, 0 deletions
diff --git a/src/library/utility/string.rs b/src/library/utility/string.rs
new file mode 100644
index 00000000..92d80be2
--- /dev/null
+++ b/src/library/utility/string.rs
@@ -0,0 +1,148 @@
+use crate::library::prelude::*;
+use crate::library::text::{Case, TextNode};
+
+/// The string representation of a value.
+pub fn repr(_: &mut Context, args: &mut Args) -> TypResult<Value> {
+ Ok(args.expect::<Value>("value")?.repr().into())
+}
+
+/// Cconvert a value to a string.
+pub fn str(_: &mut Context, args: &mut Args) -> TypResult<Value> {
+ let Spanned { v, span } = args.expect("value")?;
+ Ok(Value::Str(match v {
+ Value::Int(v) => format_eco!("{}", v),
+ Value::Float(v) => format_eco!("{}", v),
+ Value::Str(v) => v,
+ v => bail!(span, "cannot convert {} to string", v.type_name()),
+ }))
+}
+
+/// Convert a string to lowercase.
+pub fn lower(_: &mut Context, args: &mut Args) -> TypResult<Value> {
+ case(Case::Lower, args)
+}
+
+/// Convert a string to uppercase.
+pub fn upper(_: &mut Context, args: &mut Args) -> TypResult<Value> {
+ case(Case::Upper, args)
+}
+
+/// Change the case of a string or content.
+fn case(case: Case, args: &mut Args) -> TypResult<Value> {
+ let Spanned { v, span } = args.expect("string or content")?;
+ Ok(match v {
+ Value::Str(v) => Value::Str(case.apply(&v).into()),
+ Value::Content(v) => Value::Content(v.styled(TextNode::CASE, Some(case))),
+ v => bail!(span, "expected string or content, found {}", v.type_name()),
+ })
+}
+
+/// Converts an integer into one or multiple letters.
+pub fn letter(_: &mut Context, args: &mut Args) -> TypResult<Value> {
+ convert(Numbering::Letter, args)
+}
+
+/// Converts an integer into a roman numeral.
+pub fn roman(_: &mut Context, args: &mut Args) -> TypResult<Value> {
+ convert(Numbering::Roman, args)
+}
+
+/// Convert a number into a symbol.
+pub fn symbol(_: &mut Context, args: &mut Args) -> TypResult<Value> {
+ convert(Numbering::Symbol, args)
+}
+
+fn convert(numbering: Numbering, args: &mut Args) -> TypResult<Value> {
+ let n = args.expect::<usize>("non-negative integer")?;
+ Ok(Value::Str(numbering.apply(n)))
+}
+
+/// 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()
+ }
+ }
+ }
+}
+
+static ROMANS: &'static [(&'static 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),
+];
+
+static SYMBOLS: &'static [char] = &['*', '†', '‡', '§', '‖', '¶'];