#![allow(clippy::missing_errors_doc)]
#![allow(clippy::needless_pass_by_value)]
use hcl::{eval::FuncArgs, Value};
type FnRes = Result<Value, String>;
macro_rules! must_let {
($left:pat = $right:expr) => {
let $left = $right else { unreachable!() };
};
}
#[ensan_proc_macro::ensan_internal_fn_mod(encoding)]
pub mod encoding {
use super::{FnRes, FuncArgs, Value};
use base64::prelude::*;
#[ensan_fn(String)]
pub fn yamldecode(args: FuncArgs) -> FnRes {
must_let!([Value::String(arg)] = &args[..]);
serde_yml::from_str(arg).map_err(|e| format!("Failed to deserialize YAML: {e}"))
}
#[test]
#[allow(clippy::expect_used)]
fn test_yamldecode() {
crate::parse(r"hi = yamldecode()").expect_err("yamldecode() runs without args");
crate::parse(r"hi = yamldecode(1)").expect_err("yamldecode() runs with wrong-type args");
}
#[ensan_fn(Any)]
pub fn yamlencode(args: FuncArgs) -> FnRes {
must_let!([arg] = &args[..]);
let ymlstring = serde_yml::to_string(arg)
.map_err(|e| format!("Failed to serialize YAML: {e}"))?
.trim()
.to_string();
Ok(Value::String(ymlstring))
}
#[ensan_fn(String)]
pub fn jsondecode(args: FuncArgs) -> FnRes {
must_let!([Value::String(arg)] = &args[..]);
serde_json::from_str(arg).map_err(|e| format!("Failed to deserialize JSON: {e}"))
}
#[test]
fn test_jsondecode() {
crate::parse(r"hi = jsondecode()").expect_err("jsondecode() runs without args");
crate::parse(r"hi = jsondecode(1)").expect_err("jsondecode() runs with wrong-type args");
}
#[ensan_fn(Any)]
pub fn jsonencode(args: FuncArgs) -> FnRes {
must_let!([arg] = &args[..]);
let jsonstring = serde_json::to_string(arg)
.map_err(|e| format!("Failed to serialize JSON: {e}"))?
.trim()
.to_string();
Ok(Value::String(jsonstring))
}
#[ensan_fn(String)]
pub fn base64encode(args: FuncArgs) -> FnRes {
must_let!([Value::String(arg)] = &args[..]);
let encoded = BASE64_STANDARD.encode(arg.as_bytes());
Ok(Value::String(encoded))
}
#[ensan_fn(String)]
pub fn base64decode(args: FuncArgs) -> FnRes {
must_let!([Value::String(arg)] = &args[..]);
let decoded = BASE64_STANDARD
.decode(arg.as_bytes())
.map_err(|e| format!("Failed to decode base64: {e}"))?;
let decoded_str = String::from_utf8(decoded)
.map_err(|e| format!("Failed to convert decoded bytes to string: {e}"))?;
Ok(Value::String(decoded_str))
}
}
#[ensan_proc_macro::ensan_internal_fn_mod(string_manipulation)]
pub mod string_manipulation {
use super::{FnRes, FuncArgs, Value};
use itertools::Itertools;
#[ensan_fn(String)]
pub fn lower(args: FuncArgs) -> FnRes {
must_let!([Value::String(arg)] = &args[..]);
Ok(Value::String(arg.to_lowercase()))
}
#[ensan_fn(String)]
pub fn upper(args: FuncArgs) -> FnRes {
must_let!([Value::String(arg)] = &args[..]);
Ok(Value::String(arg.to_uppercase()))
}
#[ensan_fn(String, String)]
pub fn split(args: FuncArgs) -> FnRes {
must_let!([Value::String(sep), Value::String(args)] = &args[..]);
Ok(Value::Array(
args.split(sep)
.map(ToString::to_string)
.map(Value::String)
.collect(),
))
}
#[ensan_fn(String, Array(String))]
pub fn join(args: FuncArgs) -> FnRes {
must_let!([Value::String(sep), Value::Array(args)] = &args[..]);
Ok(Value::String(
args.iter().filter_map(Value::as_str).join(sep),
))
}
#[ensan_fn(String)]
pub fn strlen(args: FuncArgs) -> FnRes {
must_let!([Value::String(s)] = &args[..]);
Ok(s.len().into())
}
#[ensan_fn(String)]
pub fn trimspace(args: FuncArgs) -> FnRes {
must_let!([Value::String(s)] = &args[..]);
Ok(s.trim().into())
}
#[ensan_fn(String)]
pub fn strrev(args: FuncArgs) -> FnRes {
must_let!([Value::String(s)] = &args[..]);
let reversed: String = s.chars().rev().collect();
Ok(Value::String(reversed))
}
}
#[ensan_proc_macro::ensan_internal_fn_mod(ensan_builtin_fns)]
pub mod ensan_internal_fns {
use super::{FnRes, FuncArgs, Value};
#[ensan_fn(String)]
pub fn env(args: FuncArgs) -> FnRes {
must_let!([Value::String(key)] = &args[..]);
Ok(std::env::var(key)
.map_err(|e| format!("Failed to get environment variable: {e}"))?
.into())
}
}
#[ensan_proc_macro::ensan_internal_fn_mod(hashing)]
pub mod hashing {
use super::{FnRes, FuncArgs, Value};
#[ensan_fn(String)]
pub fn md5(args: FuncArgs) -> FnRes {
use md5::{Digest, Md5};
must_let!([Value::String(s)] = &args[..]);
let mut hasher = Md5::new();
hasher.update(s);
Ok(format!("{:x}", hasher.finalize()).into())
}
#[ensan_fn(String)]
pub fn sha1(args: FuncArgs) -> FnRes {
use sha1::{Digest, Sha1};
must_let!([Value::String(s)] = &args[..]);
let mut hasher = Sha1::new();
hasher.update(s.as_bytes());
Ok(format!("{:x}", hasher.finalize()).into())
}
#[ensan_fn(String)]
pub fn sha256(args: FuncArgs) -> FnRes {
use sha2::{Digest, Sha256};
must_let!([Value::String(s)] = &args[..]);
let mut hasher = Sha256::new();
hasher.update(s.as_bytes());
Ok(format!("{:x}", hasher.finalize()).into())
}
#[ensan_fn(String)]
pub fn sha512(args: FuncArgs) -> FnRes {
use sha2::{Digest, Sha512};
must_let!([Value::String(s)] = &args[..]);
let mut hasher = Sha512::new();
hasher.update(s.as_bytes());
Ok(format!("{:x}", hasher.finalize()).into())
}
#[ensan_fn(String, Nullable(Number))]
pub fn bcrypt(args: FuncArgs) -> FnRes {
use bcrypt::hash;
must_let!([Value::String(s), cost] = &args[..]);
let cost = (cost.as_u64().unwrap_or(10).try_into())
.map_err(|e| format!("Cannot turn u64 → u32: {e}"))?;
Ok(hash(s, cost)
.map_err(|e| format!("Failed to hash string with bcrypt: {e}"))?
.into())
}
}
#[ensan_proc_macro::ensan_internal_fn_mod(uuid)]
pub mod uuid {
use super::{uuid, FnRes, FuncArgs, Value};
use ::uuid::Uuid;
#[ensan_fn()]
pub fn uuidv4(_args: FuncArgs) -> FnRes {
Ok(uuid::Uuid::new_v4().to_string().into())
}
#[ensan_fn(String, String)]
pub fn uuidv5(args: FuncArgs) -> FnRes {
must_let!([Value::String(ns), Value::String(name)] = &args[..]);
let ns = Uuid::parse_str(ns).map_err(|e| format!("Failed to parse UUID: {e}"))?;
Ok(Uuid::new_v5(&ns, name.as_bytes()).to_string().into())
}
}