camel_api/
script_source.rs1use std::path::Path;
4use std::path::PathBuf;
5
6use crate::error::CamelError;
7
8fn resolve_path(path: &Path) -> PathBuf {
12 if path.is_absolute() {
13 return path.to_path_buf();
14 }
15 match std::env::current_dir() {
16 Ok(cwd) => cwd.join(path),
17 Err(_) => path.to_path_buf(),
18 }
19}
20
21pub fn infer_script_language(path: &Path) -> Result<String, CamelError> {
26 let resolved = resolve_path(path);
27 let Some(extension) = path.extension().and_then(std::ffi::OsStr::to_str) else {
28 return Err(CamelError::RouteError(format!(
29 "cannot infer script language for `{}`: missing file extension; set `language:` explicitly",
30 resolved.display()
31 )));
32 };
33 let language = match extension {
34 "rhai" => "rhai",
35 "js" => "js",
36 "jsonpath" => "jsonpath",
37 "xpath" => "xpath",
38 "jinja" => "minijinja",
39 "jinja2" => "minijinja",
40 _ => {
41 return Err(CamelError::RouteError(format!(
42 "cannot infer script language for `{}`: unknown extension `.{extension}`; set `language:` explicitly",
43 resolved.display()
44 )));
45 }
46 };
47 Ok(language.to_string())
48}
49
50pub fn read_script_source(path: &Path) -> Result<String, CamelError> {
56 let resolved = resolve_path(path);
57 let contents = std::fs::read_to_string(&resolved).map_err(|error| {
58 CamelError::RouteError(format!(
59 "cannot read script file `{}`: {error}",
60 resolved.display()
61 ))
62 })?;
63 if contents.trim().is_empty() {
64 return Err(CamelError::RouteError(format!(
65 "script file `{}` is empty",
66 resolved.display()
67 )));
68 }
69 Ok(contents)
70}
71
72#[cfg(test)]
73mod tests {
74 use std::fs;
75
76 use tempfile::TempDir;
77
78 use super::infer_script_language;
79 use super::read_script_source;
80
81 #[test]
82 fn infer_script_language_maps_known_extensions() {
83 let dir: TempDir = tempfile::tempdir().expect("create tempdir");
84 let cases = [
85 ("a.rhai", "rhai"),
86 ("b.js", "js"),
87 ("c.jsonpath", "jsonpath"),
88 ("d.xpath", "xpath"),
89 ("e.jinja", "minijinja"),
90 ("f.jinja2", "minijinja"),
91 ];
92
93 for (name, expected) in cases {
94 let path = dir.path().join(name);
95 fs::write(&path, "x").expect("write fixture");
96
97 let language =
98 infer_script_language(&path).expect("infer language from known extension");
99
100 assert_eq!(language, expected, "language for {name}");
101 }
102 }
103
104 #[test]
105 fn infer_script_language_rejects_unknown_extension() {
106 let dir: TempDir = tempfile::tempdir().expect("create tempdir");
107 let path = dir.path().join("x.xyz");
108 fs::write(&path, "x").expect("write fixture");
109
110 let error = infer_script_language(&path).expect_err("unknown extension must fail");
111 let message = error.to_string();
112
113 assert!(
114 message.contains(&path.display().to_string()),
115 "message: {message}"
116 );
117 assert!(message.contains("xyz"), "message: {message}");
118 }
119
120 #[test]
121 fn read_script_source_rejects_missing_file() {
122 let dir: TempDir = tempfile::tempdir().expect("create tempdir");
123 let path = dir.path().join("absent.rhai");
124
125 let error = read_script_source(&path).expect_err("missing file must fail");
126 let message = error.to_string();
127
128 assert!(
129 message.contains(&path.display().to_string()),
130 "message: {message}"
131 );
132 }
133
134 #[test]
135 fn read_script_source_rejects_empty_file() {
136 let dir: TempDir = tempfile::tempdir().expect("create tempdir");
137 let path = dir.path().join("blank.rhai");
138 fs::write(&path, " \n\t\n").expect("write fixture");
139
140 let error = read_script_source(&path).expect_err("blank file must fail");
141 let message = error.to_string();
142
143 assert!(message.contains("empty"), "message: {message}");
144 assert!(
145 message.contains(&path.display().to_string()),
146 "message: {message}"
147 );
148 }
149
150 #[test]
151 fn read_script_source_returns_verbatim_contents() {
152 let dir: TempDir = tempfile::tempdir().expect("create tempdir");
153 let path = dir.path().join("verbatim.rhai");
154 let contents = "${env:SECRET}\n";
155 fs::write(&path, contents).expect("write fixture");
156
157 let read = read_script_source(&path).expect("read script source");
158
159 assert_eq!(read, contents);
160 }
161}