summaryrefslogtreecommitdiff
path: root/src/eval/str.rs
blob: 514bf318f31d54a106fd985ec9e22ea95e764c64 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Deref;

use super::{Array, Value};
use crate::diag::StrResult;
use crate::util::EcoString;

/// Extra methods on strings.
pub trait StrExt {
    /// Repeat a string a number of times.
    fn repeat(&self, n: i64) -> StrResult<EcoString>;

    /// Split this string at whitespace or a specific pattern.
    fn split(&self, at: Option<EcoString>) -> Array;
}

impl StrExt for EcoString {
    fn repeat(&self, n: i64) -> StrResult<EcoString> {
        let n = usize::try_from(n)
            .ok()
            .and_then(|n| self.len().checked_mul(n).map(|_| n))
            .ok_or_else(|| format!("cannot repeat this string {} times", n))?;

        Ok(self.repeat(n))
    }

    fn split(&self, at: Option<EcoString>) -> Array {
        if let Some(pat) = at {
            self.as_str()
                .split(pat.as_str())
                .map(|s| Value::Str(s.into()))
                .collect()
        } else {
            self.as_str()
                .split_whitespace()
                .map(|s| Value::Str(s.into()))
                .collect()
        }
    }
}

/// A regular expression.
#[derive(Clone)]
pub struct Regex(regex::Regex);

impl Regex {
    /// Create a new regex.
    pub fn new(re: &str) -> StrResult<Self> {
        regex::Regex::new(re).map(Self).map_err(|err| err.to_string())
    }

    /// Whether the regex matches the given `text`.
    pub fn matches(&self, text: &str) -> bool {
        self.0.is_match(text)
    }
}

impl Deref for Regex {
    type Target = regex::Regex;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl Debug for Regex {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "regex({:?})", self.0.as_str())
    }
}

impl PartialEq for Regex {
    fn eq(&self, other: &Self) -> bool {
        self.0.as_str() == other.0.as_str()
    }
}

impl Hash for Regex {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.0.as_str().hash(state);
    }
}