vrl/stdlib/
split_path.rs

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}