tatara_lisp_script/stdlib/
hash.rs1use std::sync::Arc;
11
12use sha2::{Digest, Sha256};
13use tatara_lisp_eval::{Arity, Interpreter, Value};
14
15use crate::script_ctx::ScriptCtx;
16use crate::stdlib::env::str_arg;
17
18pub fn install(interp: &mut Interpreter<ScriptCtx>) {
19 interp.register_fn(
20 "sha256",
21 Arity::Exact(1),
22 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
23 let s = str_arg(&args[0], "sha256", sp)?;
24 let digest = Sha256::digest(s.as_bytes());
25 Ok(Value::Str(Arc::from(hex::encode(digest))))
26 },
27 );
28
29 interp.register_fn(
30 "slugify",
31 Arity::Exact(2),
32 |args: &[Value], _ctx: &mut ScriptCtx, sp| {
33 let name = str_arg(&args[0], "slugify", sp)?;
34 let kind = str_arg(&args[1], "slugify", sp)?;
35 Ok(Value::Str(Arc::from(derive_slug(&name, &kind))))
36 },
37 );
38}
39
40pub fn derive_slug(name: &str, kind: &str) -> String {
45 let normalized = if name == "@" || name.is_empty() {
46 "root".to_string()
47 } else {
48 let mut s = name.replace('.', "_");
49 if let Some(stripped) = s.strip_prefix("*_") {
51 s = format!("wildcard_{stripped}");
52 } else if let Some(stripped) = s.strip_prefix("*") {
53 s = format!("wildcard{stripped}");
54 }
55 while s.contains("__") {
57 s = s.replace("__", "_");
58 }
59 s
60 };
61 format!("{}_{}", normalized, kind.to_lowercase())
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn apex_normalizes_to_root() {
70 assert_eq!(derive_slug("@", "CNAME"), "root_cname");
71 }
72
73 #[test]
74 fn dots_become_underscores() {
75 assert_eq!(derive_slug("api.staging", "CNAME"), "api_staging_cname");
76 }
77
78 #[test]
79 fn wildcard_expands() {
80 assert_eq!(derive_slug("*.staging", "CNAME"), "wildcard_staging_cname");
81 }
82
83 #[test]
84 fn underscore_prefixed_dkim_preserved() {
85 assert_eq!(
86 derive_slug("resend._domainkey", "TXT"),
87 "resend_domainkey_txt"
88 );
89 }
90
91 #[test]
92 fn plain_name_lowercases_type() {
93 assert_eq!(derive_slug("www", "CNAME"), "www_cname");
94 assert_eq!(derive_slug("send", "MX"), "send_mx");
95 }
96}