cfg_if::cfg_if! {
if #[cfg(feature = "enable_system_functions")] {
use relative_path::PathExt;
use std::{env, path::PathBuf};
}
}
use crate::value::{KeyString, ObjectMap, Value};
#[cfg(feature = "enable_crypto_functions")]
use bytes::Bytes;
#[cfg(feature = "enable_crypto_functions")]
use bytestring::ByteString;
pub(crate) const DYNAMIC_REGEX_NOTICE: &str = indoc::indoc! {"
When `pattern` is a dynamic expression (e.g. a variable or the result of `to_regex`),
the regex is compiled on every function call. For high-throughput pipelines, prefer
a regex literal so the pattern is compiled once at program compile time.
"};
#[cfg(feature = "enable_network_functions")]
pub(crate) const NETWORK_CALL_NOTICE: &str = indoc::indoc! {"
This function performs synchronous blocking operations and is not recommended for
frequent or performance-critical workflows due to potential network-related delays.
"};
#[inline]
#[allow(clippy::cast_precision_loss)] pub(crate) fn round_to_precision<F>(num: f64, precision: i64, fun: F) -> f64
where
F: Fn(f64) -> f64,
{
let multiplier = 10_f64.powf(precision as f64);
fun(num * multiplier) / multiplier
}
#[cfg(feature = "enable_crypto_functions")]
#[inline]
pub(crate) fn hex_encode<const N: usize>(digest: impl AsRef<[u8]>) -> ByteString {
let mut buf = [0u8; N];
hex::encode_to_slice(digest, &mut buf).expect("hex buffer sized for digest");
unsafe { ByteString::from_bytes_unchecked(Bytes::copy_from_slice(&buf)) }
}
#[derive(Debug, Clone)]
pub(crate) struct RegexWithCaptureInfo {
pub regex: regex::Regex,
pub capture_info: Vec<(KeyString, usize)>,
}
impl RegexWithCaptureInfo {
pub fn new(regex: regex::Regex) -> Self {
let capture_info = build_capture_info(®ex);
Self {
regex,
capture_info,
}
}
}
pub(crate) fn build_capture_info(regex: ®ex::Regex) -> Vec<(KeyString, usize)> {
regex
.capture_names()
.enumerate()
.filter_map(|(i, name)| name.map(|n| (KeyString::from(n), i)))
.collect()
}
pub(crate) fn capture_regex_to_map(
capture: ®ex::Captures,
capture_info: &[(KeyString, usize)],
numeric_groups: bool,
) -> ObjectMap {
let names = capture_info.iter().map(|(name, idx)| {
let value: Value = capture.get(*idx).map(|m| m.as_str()).into();
(name.clone(), value)
});
if numeric_groups {
let indexed = capture
.iter()
.flatten()
.enumerate()
.map(|(idx, c)| (KeyString::from(idx.to_string()), c.as_str().into()));
indexed.chain(names).collect()
} else {
names.collect()
}
}
pub(crate) fn regex_kind(
regex: ®ex::Regex,
) -> std::collections::BTreeMap<crate::value::kind::Field, crate::value::kind::Kind> {
use crate::value::kind::Kind;
let mut inner_type = std::collections::BTreeMap::new();
for num in 0..regex.captures_len() {
inner_type.insert(num.to_string().into(), Kind::bytes() | Kind::null());
}
for name in regex.capture_names().flatten() {
inner_type.insert(name.to_owned().into(), Kind::bytes());
}
inner_type
}
pub(crate) fn is_nullish(value: &Value) -> bool {
value.is_null()
|| value.to_str_lossy().is_some_and(|text| {
text.is_empty() || text == "-" || text.chars().all(char::is_whitespace)
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum Base64Charset {
#[default]
Standard,
UrlSafe,
}
impl Base64Charset {
pub(super) fn from_slice(bytes: &[u8]) -> Result<Self, &'static str> {
match bytes {
b"standard" => Ok(Self::Standard),
b"url_safe" => Ok(Self::UrlSafe),
_ => Err("unknown charset"),
}
}
}
#[cfg(feature = "enable_system_functions")]
pub(crate) fn example_path_or_basename(input: &'static str) -> String {
let manifest_dir =
env::var_os("CARGO_MANIFEST_DIR").map(|dir| PathBuf::from(dir).join("../.."));
let path = manifest_dir
.clone()
.map(|dir| dir.join("tests/data").join(input));
let not_found_default = || {
PathBuf::from("tests/data")
.join(input)
.display()
.to_string()
};
if let Some(manifest_dir) = manifest_dir
&& let Some(path) = path
&& path.exists()
{
path.relative_to(manifest_dir)
.map_or_else(|_| not_found_default(), String::from)
} else {
not_found_default()
}
}