1use crate::compiler::prelude::*;
2use std::path::Path;
3
4fn split_path(path_str: &str) -> Value {
5 let path = Path::new(path_str);
6
7 let split_path: Vec<_> = path
8 .components()
9 .map(|comp| comp.as_os_str().to_string_lossy().into_owned())
10 .collect();
11 split_path.into()
12}
13
14#[derive(Clone, Copy, Debug)]
15pub struct SplitPath;
16
17impl Function for SplitPath {
18 fn identifier(&self) -> &'static str {
19 "split_path"
20 }
21
22 fn usage(&self) -> &'static str {
23 "Splits the given `path` into its constituent components, returning an array of strings. Each component represents a part of the file system path hierarchy."
24 }
25
26 fn parameters(&self) -> &'static [Parameter] {
27 &[Parameter {
28 keyword: "value",
29 kind: kind::BYTES,
30 required: true,
31 }]
32 }
33
34 fn compile(
35 &self,
36 _state: &state::TypeState,
37 _ctx: &mut FunctionCompileContext,
38 arguments: ArgumentList,
39 ) -> Compiled {
40 let value = arguments.required("value");
41
42 Ok(SplitPathFn { value }.as_expr())
43 }
44
45 fn examples(&self) -> &'static [Example] {
46 &[
47 example! {
48 title: "Split path with trailing slash",
49 source: r#"split_path("/home/user/")"#,
50 result: Ok(r#"["/", "home", "user"]"#),
51 },
52 example! {
53 title: "Split path from file path",
54 source: r#"split_path("/home/user")"#,
55 result: Ok(r#"["/", "home", "user"]"#),
56 },
57 example! {
58 title: "Split path from root",
59 source: r#"split_path("/")"#,
60 result: Ok(r#"["/"]"#),
61 },
62 example! {
63 title: "Empty path returns empty array",
64 source: r#"split_path("")"#,
65 result: Ok("[]"),
66 },
67 ]
68 }
69}
70
71#[derive(Debug, Clone)]
72struct SplitPathFn {
73 value: Box<dyn Expression>,
74}
75
76impl FunctionExpression for SplitPathFn {
77 fn resolve(&self, ctx: &mut Context) -> Resolved {
78 let value = self.value.resolve(ctx)?;
79 let path_str = value.try_bytes_utf8_lossy()?;
80 Ok(split_path(&path_str))
81 }
82
83 fn type_def(&self, _: &state::TypeState) -> TypeDef {
84 TypeDef::array(Collection::any())
85 }
86}
87#[cfg(test)]
88mod tests {
89 use super::*;
90 use crate::value;
91
92 fn tdef() -> TypeDef {
93 TypeDef::array(Collection::any())
94 }
95
96 test_function![
97 split_path => SplitPath;
98
99 home_user_trailing_slash {
100 args: func_args![value: "/home/user/"],
101 want: Ok(value!(["/", "home", "user"])),
102 tdef: tdef(),
103 }
104
105 home_user_no_trailing_slash {
106 args: func_args![value: "/home/user"],
107 want: Ok(value!(["/", "home", "user"])),
108 tdef: tdef(),
109 }
110
111 root {
112 args: func_args![value: "/"],
113 want: Ok(value!(["/"])),
114 tdef: tdef(),
115 }
116
117 empty {
118 args: func_args![value: ""],
119 want: Ok(value!([])),
120 tdef: tdef(),
121 }
122
123 ];
124}