fusabi_stdlib_ext/
path.rs1use std::path::{Path, PathBuf};
6
7use fusabi_host::ExecutionContext;
8use fusabi_host::Value;
9
10pub fn join(
12 args: &[Value],
13 _ctx: &ExecutionContext,
14) -> fusabi_host::Result<Value> {
15 if args.is_empty() {
16 return Err(fusabi_host::Error::host_function("path.join: no arguments"));
17 }
18
19 let mut result = PathBuf::new();
20
21 for arg in args {
22 let part = arg
23 .as_str()
24 .ok_or_else(|| fusabi_host::Error::host_function("path.join: argument must be string"))?;
25 result.push(part);
26 }
27
28 Ok(Value::String(result.to_string_lossy().into_owned()))
29}
30
31pub fn dirname(
33 args: &[Value],
34 _ctx: &ExecutionContext,
35) -> fusabi_host::Result<Value> {
36 let path_str = args
37 .first()
38 .and_then(|v| v.as_str())
39 .ok_or_else(|| fusabi_host::Error::host_function("path.dirname: missing path argument"))?;
40
41 let path = Path::new(path_str);
42
43 match path.parent() {
44 Some(parent) => Ok(Value::String(parent.to_string_lossy().into_owned())),
45 None => Ok(Value::Null),
46 }
47}
48
49pub fn basename(
51 args: &[Value],
52 _ctx: &ExecutionContext,
53) -> fusabi_host::Result<Value> {
54 let path_str = args
55 .first()
56 .and_then(|v| v.as_str())
57 .ok_or_else(|| fusabi_host::Error::host_function("path.basename: missing path argument"))?;
58
59 let path = Path::new(path_str);
60
61 match path.file_name() {
62 Some(name) => Ok(Value::String(name.to_string_lossy().into_owned())),
63 None => Ok(Value::Null),
64 }
65}
66
67pub fn extension(
69 args: &[Value],
70 _ctx: &ExecutionContext,
71) -> fusabi_host::Result<Value> {
72 let path_str = args
73 .first()
74 .and_then(|v| v.as_str())
75 .ok_or_else(|| fusabi_host::Error::host_function("path.extension: missing path argument"))?;
76
77 let path = Path::new(path_str);
78
79 match path.extension() {
80 Some(ext) => Ok(Value::String(ext.to_string_lossy().into_owned())),
81 None => Ok(Value::Null),
82 }
83}
84
85pub fn normalize(
87 args: &[Value],
88 _ctx: &ExecutionContext,
89) -> fusabi_host::Result<Value> {
90 let path_str = args
91 .first()
92 .and_then(|v| v.as_str())
93 .ok_or_else(|| fusabi_host::Error::host_function("path.normalize: missing path argument"))?;
94
95 let path = Path::new(path_str);
97 let normalized = path.components().collect::<PathBuf>();
98
99 Ok(Value::String(normalized.to_string_lossy().into_owned()))
100}
101
102pub fn is_absolute(
104 args: &[Value],
105 _ctx: &ExecutionContext,
106) -> fusabi_host::Result<Value> {
107 let path_str = args
108 .first()
109 .and_then(|v| v.as_str())
110 .ok_or_else(|| fusabi_host::Error::host_function("path.is_absolute: missing path argument"))?;
111
112 let path = Path::new(path_str);
113 Ok(Value::Bool(path.is_absolute()))
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119 use fusabi_host::Capabilities;
120 use fusabi_host::{Sandbox, SandboxConfig};
121 use fusabi_host::Limits;
122
123 fn create_test_ctx() -> ExecutionContext {
124 let sandbox = Sandbox::new(SandboxConfig::default()).unwrap();
125 ExecutionContext::new(1, Capabilities::none(), Limits::default(), sandbox)
126 }
127
128 #[test]
129 fn test_join() {
130 let ctx = create_test_ctx();
131 let result = join(&[
132 Value::String("/home".into()),
133 Value::String("user".into()),
134 Value::String("file.txt".into()),
135 ], &ctx).unwrap();
136
137 let path = result.as_str().unwrap();
138 assert!(path.contains("home"));
139 assert!(path.contains("user"));
140 assert!(path.contains("file.txt"));
141 }
142
143 #[test]
144 fn test_dirname() {
145 let ctx = create_test_ctx();
146 let result = dirname(&[Value::String("/home/user/file.txt".into())], &ctx).unwrap();
147 assert_eq!(result.as_str().unwrap(), "/home/user");
148 }
149
150 #[test]
151 fn test_basename() {
152 let ctx = create_test_ctx();
153 let result = basename(&[Value::String("/home/user/file.txt".into())], &ctx).unwrap();
154 assert_eq!(result.as_str().unwrap(), "file.txt");
155 }
156
157 #[test]
158 fn test_extension() {
159 let ctx = create_test_ctx();
160 let result = extension(&[Value::String("/home/user/file.txt".into())], &ctx).unwrap();
161 assert_eq!(result.as_str().unwrap(), "txt");
162 }
163
164 #[test]
165 fn test_is_absolute() {
166 let ctx = create_test_ctx();
167
168 let result = is_absolute(&[Value::String("/absolute/path".into())], &ctx).unwrap();
169 assert_eq!(result.as_bool().unwrap(), true);
170
171 let result = is_absolute(&[Value::String("relative/path".into())], &ctx).unwrap();
172 assert_eq!(result.as_bool().unwrap(), false);
173 }
174}