/* * SPDX-FileCopyrightText: 2020 Stalwart Labs LLC * * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ use compact_str::{CompactString, ToCompactString, format_compact}; use sha1::Sha1; use sha2::{Sha256, Sha512}; use utils::HexEncode; use crate::expr::{StringCow, Variable}; pub(crate) fn fn_trim(mut v: Vec) -> Variable { v.remove(0).transform(|s| match s { StringCow::Borrowed(s) => Variable::from(s.trim()), StringCow::Owned(s) => Variable::from(s.trim().to_compact_string()), }) } pub(crate) fn fn_trim_end(mut v: Vec) -> Variable { v.remove(0).transform(|s| match s { StringCow::Borrowed(s) => Variable::from(s.trim_end()), StringCow::Owned(s) => Variable::from(s.trim_end().to_compact_string()), }) } pub(crate) fn fn_trim_start(mut v: Vec) -> Variable { v.remove(0).transform(|s| match s { StringCow::Borrowed(s) => Variable::from(s.trim_start()), StringCow::Owned(s) => Variable::from(s.trim_start().to_compact_string()), }) } pub(crate) fn fn_len(v: Vec) -> Variable { match &v[0] { Variable::String(s) => s.len(), Variable::Array(a) => a.len(), v => v.to_string().len(), } .into() } pub(crate) fn fn_to_lowercase(mut v: Vec) -> Variable { v.remove(0) .transform(|s| Variable::from(CompactString::from_str_to_lowercase(s.as_str()))) } pub(crate) fn fn_to_uppercase(mut v: Vec) -> Variable { v.remove(0) .transform(|s| Variable::from(CompactString::from_str_to_uppercase(s.as_str()))) } pub(crate) fn fn_is_uppercase(mut v: Vec) -> Variable { v.remove(0).transform(|s| { s.as_str() .chars() .filter(|c| c.is_alphabetic()) .all(|c| c.is_uppercase()) .into() }) } pub(crate) fn fn_is_lowercase(mut v: Vec) -> Variable { v.remove(0).transform(|s| { s.as_str() .chars() .filter(|c| c.is_alphabetic()) .all(|c| c.is_lowercase()) .into() }) } pub(crate) fn fn_has_digits(mut v: Vec) -> Variable { v.remove(0) .transform(|s| s.as_str().chars().any(|c| c.is_ascii_digit()).into()) } pub(crate) fn fn_split_words(v: Vec) -> Variable { v[0].to_string() .as_str() .split_whitespace() .filter(|word| word.chars().all(|c| c.is_alphanumeric())) .map(|word| Variable::from(CompactString::new(word))) .collect::>() .into() } pub(crate) fn fn_count_spaces(v: Vec) -> Variable { v[0].to_string() .as_str() .chars() .filter(|c| c.is_whitespace()) .count() .into() } pub(crate) fn fn_count_uppercase(v: Vec) -> Variable { v[0].to_string() .as_str() .chars() .filter(|c| c.is_alphabetic() && c.is_uppercase()) .count() .into() } pub(crate) fn fn_count_lowercase(v: Vec) -> Variable { v[0].to_string() .as_str() .chars() .filter(|c| c.is_alphabetic() && c.is_lowercase()) .count() .into() } pub(crate) fn fn_count_chars(v: Vec) -> Variable { v[0].to_string().as_str().chars().count().into() } pub(crate) fn fn_eq_ignore_case(v: Vec) -> Variable { v[0].to_string() .as_str() .eq_ignore_ascii_case(v[1].to_string().as_str()) .into() } pub(crate) fn fn_contains(v: Vec) -> Variable { match &v[0] { Variable::String(s) => s.as_str().contains(v[1].to_string().as_str()), Variable::Array(arr) => arr.contains(&v[1]), val => val.to_string().as_str().contains(v[1].to_string().as_str()), } .into() } pub(crate) fn fn_contains_ignore_case(v: Vec) -> Variable { let needle = v[1].to_string(); match &v[0] { Variable::String(s) => s .as_str() .to_lowercase() .contains(&needle.as_str().to_lowercase()), Variable::Array(arr) => arr.iter().any(|v| match v { Variable::String(s) => s.as_str().eq_ignore_ascii_case(needle.as_str()), _ => false, }), val => val.to_string().as_str().contains(needle.as_str()), } .into() } pub(crate) fn fn_starts_with(v: Vec) -> Variable { v[0].to_string() .as_str() .starts_with(v[1].to_string().as_str()) .into() } pub(crate) fn fn_ends_with(v: Vec) -> Variable { v[0].to_string() .as_str() .ends_with(v[1].to_string().as_str()) .into() } pub(crate) fn fn_lines(mut v: Vec) -> Variable { match v.remove(0) { Variable::String(s) => s .as_str() .lines() .map(|s| Variable::from(CompactString::new(s))) .collect::>() .into(), val => val, } } pub(crate) fn fn_substring(v: Vec) -> Variable { v[0].to_string() .as_str() .chars() .skip(v[1].to_usize().unwrap_or_default()) .take(v[2].to_usize().unwrap_or_default()) .collect::() .into() } pub(crate) fn fn_strip_prefix(v: Vec) -> Variable { let mut v = v.into_iter(); let value = v.next().unwrap(); let prefix = v.next().unwrap().into_string(); value.transform(|s| match s { StringCow::Borrowed(s) => s .strip_prefix(prefix.as_str()) .map(Variable::from) .unwrap_or_default(), StringCow::Owned(s) => s .strip_prefix(prefix.as_str()) .map(|s| Variable::from(CompactString::new(s))) .unwrap_or_default(), }) } pub(crate) fn fn_strip_suffix(v: Vec) -> Variable { let mut v = v.into_iter(); let value = v.next().unwrap(); let suffix = v.next().unwrap().into_string(); value.transform(|s| match s { StringCow::Borrowed(s) => s .strip_suffix(suffix.as_str()) .map(Variable::from) .unwrap_or_default(), StringCow::Owned(s) => s .strip_suffix(suffix.as_str()) .map(|s| Variable::from(CompactString::new(s))) .unwrap_or_default(), }) } pub(crate) fn fn_split(v: Vec) -> Variable { let mut v = v.into_iter(); let value = v.next().unwrap().into_string(); let arg = v.next().unwrap().into_string(); match value { StringCow::Borrowed(s) => s .split(arg.as_str()) .map(Variable::from) .collect::>() .into(), StringCow::Owned(s) => s .split(arg.as_str()) .map(|s| Variable::from(CompactString::new(s))) .collect::>() .into(), } } pub(crate) fn fn_rsplit(v: Vec) -> Variable { let mut v = v.into_iter(); let value = v.next().unwrap().into_string(); let arg = v.next().unwrap().into_string(); match value { StringCow::Borrowed(s) => s .rsplit(arg.as_str()) .map(Variable::from) .collect::>() .into(), StringCow::Owned(s) => s .rsplit(arg.as_str()) .map(|s| Variable::from(CompactString::new(s))) .collect::>() .into(), } } pub(crate) fn fn_split_n(v: Vec) -> Variable { let mut v = v.into_iter(); let value = v.next().unwrap().into_string(); let arg = v.next().unwrap().into_string(); let num = v.next().unwrap().to_integer().unwrap_or_default() as usize; fn split_n<'x, 'y>(s: &'x str, arg: &'y str, num: usize, mut f: impl FnMut(&'x str)) { let mut s = s; for _ in 0..num { if let Some((a, b)) = s.split_once(arg) { f(a); s = b; } else { break; } } f(s); } let mut result = Vec::new(); match value { StringCow::Borrowed(s) => split_n(s, arg.as_str(), num, |s| result.push(Variable::from(s))), StringCow::Owned(s) => split_n(&s, arg.as_str(), num, |s| { result.push(Variable::from(CompactString::new(s))) }), } result.into() } pub(crate) fn fn_split_once(v: Vec) -> Variable { let mut v = v.into_iter(); let value = v.next().unwrap().into_string(); let arg = v.next().unwrap().into_string(); match value { StringCow::Borrowed(s) => s .split_once(arg.as_str()) .map(|(a, b)| Variable::Array(vec![Variable::from(a), Variable::from(b)])) .unwrap_or_default(), StringCow::Owned(s) => s .split_once(arg.as_str()) .map(|(a, b)| { Variable::Array(vec![ Variable::from(CompactString::new(a)), Variable::from(CompactString::new(b)), ]) }) .unwrap_or_default(), } } pub(crate) fn fn_rsplit_once(v: Vec) -> Variable { let mut v = v.into_iter(); let value = v.next().unwrap().into_string(); let arg = v.next().unwrap().into_string(); match value { StringCow::Borrowed(s) => s .rsplit_once(arg.as_str()) .map(|(a, b)| Variable::Array(vec![Variable::from(a), Variable::from(b)])) .unwrap_or_default(), StringCow::Owned(s) => s .rsplit_once(arg.as_str()) .map(|(a, b)| { Variable::Array(vec![ Variable::from(CompactString::new(a)), Variable::from(CompactString::new(b)), ]) }) .unwrap_or_default(), } } pub(crate) fn fn_hash(v: Vec) -> Variable { use sha1::Digest; let mut v = v.into_iter(); let value = v.next().unwrap().into_string(); let algo = v.next().unwrap().into_string(); match algo.as_str() { "md5" => format_compact!("{:x}", md5::compute(value.as_bytes())).into(), "sha1" => { let mut hasher = Sha1::new(); hasher.update(value.as_bytes()); hasher.finalize().hex_encode().to_compact_string().into() } "sha256" => { let mut hasher = Sha256::new(); hasher.update(value.as_bytes()); hasher.finalize().hex_encode().to_compact_string().into() } "sha512" => { let mut hasher = Sha512::new(); hasher.update(value.as_bytes()); hasher.finalize().hex_encode().to_compact_string().into() } _ => Variable::default(), } }