jpx_core/extensions/
path.rs1use std::collections::HashSet;
4
5use serde_json::Value;
6
7use crate::functions::Function;
8use crate::interpreter::SearchResult;
9use crate::registry::register_if_enabled;
10use crate::{Context, Runtime, arg, defn};
11
12pub fn register_filtered(runtime: &mut Runtime, enabled: &HashSet<&str>) {
14 register_if_enabled(
15 runtime,
16 "path_basename",
17 enabled,
18 Box::new(PathBasenameFn::new()),
19 );
20 register_if_enabled(
21 runtime,
22 "path_dirname",
23 enabled,
24 Box::new(PathDirnameFn::new()),
25 );
26 register_if_enabled(runtime, "path_ext", enabled, Box::new(PathExtFn::new()));
27 register_if_enabled(runtime, "path_join", enabled, Box::new(PathJoinFn::new()));
28}
29
30defn!(PathBasenameFn, vec![arg!(string)], None);
35
36impl Function for PathBasenameFn {
37 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
38 self.signature.validate(args, ctx)?;
39 let path = args[0].as_str().unwrap();
40 let basename = std::path::Path::new(path)
41 .file_name()
42 .and_then(|s| s.to_str())
43 .unwrap_or("");
44 Ok(Value::String(basename.to_string()))
45 }
46}
47
48defn!(PathDirnameFn, vec![arg!(string)], None);
53
54impl Function for PathDirnameFn {
55 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
56 self.signature.validate(args, ctx)?;
57 let path = args[0].as_str().unwrap();
58 let dirname = std::path::Path::new(path)
59 .parent()
60 .and_then(|s| s.to_str())
61 .unwrap_or("");
62 Ok(Value::String(dirname.to_string()))
63 }
64}
65
66defn!(PathExtFn, vec![arg!(string)], None);
71
72impl Function for PathExtFn {
73 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
74 self.signature.validate(args, ctx)?;
75 let path = args[0].as_str().unwrap();
76 let ext = std::path::Path::new(path)
77 .extension()
78 .and_then(|s| s.to_str())
79 .map(|s| format!(".{}", s))
80 .unwrap_or_default();
81 Ok(Value::String(ext))
82 }
83}
84
85defn!(PathJoinFn, vec![arg!(array)], None);
90
91impl Function for PathJoinFn {
92 fn evaluate(&self, args: &[Value], ctx: &mut Context<'_>) -> SearchResult {
93 self.signature.validate(args, ctx)?;
94 let arr = args[0].as_array().unwrap();
95 let mut path = std::path::PathBuf::new();
96 for part in arr {
97 if let Some(s) = part.as_str() {
98 path.push(s);
99 }
100 }
101 let result = path.to_str().unwrap_or("").to_string();
102 Ok(Value::String(result))
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use crate::Runtime;
109 use serde_json::json;
110
111 fn setup_runtime() -> Runtime {
112 Runtime::builder()
113 .with_standard()
114 .with_all_extensions()
115 .build()
116 }
117
118 #[test]
119 fn test_path_basename() {
120 let runtime = setup_runtime();
121 let expr = runtime.compile("path_basename(@)").unwrap();
122 let data = json!("/path/to/file.txt");
123 let result = expr.search(&data).unwrap();
124 assert_eq!(result.as_str().unwrap(), "file.txt");
125 }
126
127 #[test]
128 fn test_path_dirname() {
129 let runtime = setup_runtime();
130 let expr = runtime.compile("path_dirname(@)").unwrap();
131 let data = json!("/path/to/file.txt");
132 let result = expr.search(&data).unwrap();
133 assert_eq!(result.as_str().unwrap(), "/path/to");
134 }
135
136 #[test]
137 fn test_path_ext() {
138 let runtime = setup_runtime();
139 let expr = runtime.compile("path_ext(@)").unwrap();
140 let data = json!("/path/to/file.txt");
141 let result = expr.search(&data).unwrap();
142 assert_eq!(result.as_str().unwrap(), ".txt");
143 }
144}