jpx_core/extensions/
ids.rs1use std::collections::HashSet;
4
5use serde_json::Value;
6
7use crate::functions::{Function, number_value};
8use crate::interpreter::SearchResult;
9use crate::registry::register_if_enabled;
10use crate::{Context, Runtime, arg, defn};
11
12defn!(NanoidFn, vec![], Some(arg!(number)));
17
18impl Function for NanoidFn {
19 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
20 self.signature.validate(args, ctx)?;
21
22 let id = if args.is_empty() {
23 nanoid::nanoid!()
24 } else {
25 let size = args[0].as_f64().unwrap_or(21.0) as usize;
26 nanoid::nanoid!(size)
27 };
28
29 Ok(Value::String(id))
30 }
31}
32
33defn!(UlidFn, vec![], None);
38
39impl Function for UlidFn {
40 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
41 self.signature.validate(args, ctx)?;
42 let id = ulid::Ulid::new().to_string();
43 Ok(Value::String(id))
44 }
45}
46
47defn!(UlidTimestampFn, vec![arg!(string)], None);
52
53impl Function for UlidTimestampFn {
54 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
55 self.signature.validate(args, ctx)?;
56 let ulid_str = args[0].as_str().unwrap();
57
58 match ulid::Ulid::from_string(ulid_str) {
59 Ok(id) => {
60 let ts = id.timestamp_ms();
61 Ok(number_value(ts as f64))
62 }
63 Err(_) => Ok(Value::Null),
64 }
65 }
66}
67
68pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
70 register_if_enabled(runtime, "nanoid", enabled, Box::new(NanoidFn::new()));
71 register_if_enabled(runtime, "ulid", enabled, Box::new(UlidFn::new()));
72 register_if_enabled(
73 runtime,
74 "ulid_timestamp",
75 enabled,
76 Box::new(UlidTimestampFn::new()),
77 );
78}
79
80#[cfg(test)]
81mod tests {
82 use crate::Runtime;
83 use serde_json::json;
84
85 fn setup_runtime() -> Runtime {
86 Runtime::builder()
87 .with_standard()
88 .with_all_extensions()
89 .build()
90 }
91
92 #[test]
93 fn test_nanoid_default() {
94 let runtime = setup_runtime();
95 let data = json!(null);
96 let expr = runtime.compile("nanoid()").unwrap();
97 let result = expr.search(&data).unwrap();
98 let id = result.as_str().unwrap();
99 assert_eq!(id.len(), 21);
100 }
101
102 #[test]
103 fn test_nanoid_custom_size() {
104 let runtime = setup_runtime();
105 let data = json!(null);
106 let expr = runtime.compile("nanoid(`10`)").unwrap();
107 let result = expr.search(&data).unwrap();
108 let id = result.as_str().unwrap();
109 assert_eq!(id.len(), 10);
110 }
111
112 #[test]
113 fn test_nanoid_unique() {
114 let runtime = setup_runtime();
115 let data = json!(null);
116 let expr = runtime.compile("nanoid()").unwrap();
117 let id1 = expr.search(&data).unwrap();
118 let id2 = expr.search(&data).unwrap();
119 assert_ne!(id1.as_str().unwrap(), id2.as_str().unwrap());
120 }
121
122 #[test]
123 fn test_ulid() {
124 let runtime = setup_runtime();
125 let data = json!(null);
126 let expr = runtime.compile("ulid()").unwrap();
127 let result = expr.search(&data).unwrap();
128 let id = result.as_str().unwrap();
129 assert_eq!(id.len(), 26);
131 }
132
133 #[test]
134 fn test_ulid_unique() {
135 let runtime = setup_runtime();
136 let data = json!(null);
137 let expr = runtime.compile("ulid()").unwrap();
138 let id1 = expr.search(&data).unwrap();
139 let id2 = expr.search(&data).unwrap();
140 assert_ne!(id1.as_str().unwrap(), id2.as_str().unwrap());
141 }
142
143 #[test]
144 fn test_ulid_timestamp() {
145 let runtime = setup_runtime();
146 let ulid = ulid::Ulid::new();
148 let ulid_str = ulid.to_string();
149 let expected_ts = ulid.timestamp_ms();
150
151 let data = json!(ulid_str);
152 let expr = runtime.compile("ulid_timestamp(@)").unwrap();
153 let result = expr.search(&data).unwrap();
154 assert_eq!(result.as_f64().unwrap(), expected_ts as f64);
155 }
156
157 #[test]
158 fn test_ulid_timestamp_invalid() {
159 let runtime = setup_runtime();
160 let data = json!("not-a-ulid");
161 let expr = runtime.compile("ulid_timestamp(@)").unwrap();
162 let result = expr.search(&data).unwrap();
163 assert!(result.is_null());
164 }
165
166 #[test]
167 fn test_ulid_format() {
168 let runtime = setup_runtime();
169 let data = json!(null);
170 let expr = runtime.compile("ulid()").unwrap();
171 let result = expr.search(&data).unwrap();
172 let id = result.as_str().unwrap();
173
174 assert_eq!(id.len(), 26);
176 assert!(id.chars().all(|c| c.is_ascii_alphanumeric()));
178 }
179}