1use std::collections::BTreeMap;
37use std::rc::Rc;
38use std::sync::Arc;
39
40use harn_vm::VmValue;
41
42use crate::error::HostlibError;
43use crate::registry::{BuiltinRegistry, HostlibCapability, RegisteredBuiltin, SyncHandler};
44
45pub(crate) mod args;
46mod cancel_handle;
47mod diagnostics;
48mod file_io;
49mod git;
50mod inspect_test_results;
51mod lang;
52pub mod long_running;
53mod manage_packages;
54mod outline;
55mod payload;
56pub mod permissions;
57mod proc;
58mod response;
59mod run_build_command;
60mod run_command;
61mod run_test;
62mod search;
63mod test_parsers;
64
65pub use permissions::FEATURE_TOOLS_DETERMINISTIC;
66
67#[derive(Default)]
69pub struct ToolsCapability;
70
71impl HostlibCapability for ToolsCapability {
72 fn module_name(&self) -> &'static str {
73 "tools"
74 }
75
76 fn register_builtins(&self, registry: &mut BuiltinRegistry) {
77 long_running::register_cleanup_hook();
80
81 register_gated(registry, "hostlib_tools_search", "search", search::run);
82 register_gated(
83 registry,
84 "hostlib_tools_read_file",
85 "read_file",
86 file_io::read_file,
87 );
88 register_gated(
89 registry,
90 "hostlib_tools_write_file",
91 "write_file",
92 file_io::write_file,
93 );
94 register_gated(
95 registry,
96 "hostlib_tools_delete_file",
97 "delete_file",
98 file_io::delete_file,
99 );
100 register_gated(
101 registry,
102 "hostlib_tools_list_directory",
103 "list_directory",
104 file_io::list_directory,
105 );
106 register_gated(
107 registry,
108 "hostlib_tools_get_file_outline",
109 "get_file_outline",
110 outline::run,
111 );
112 register_gated(registry, "hostlib_tools_git", "git", git::run);
113
114 register_gated(
115 registry,
116 "hostlib_tools_run_command",
117 "run_command",
118 run_command::handle,
119 );
120 register_gated(
121 registry,
122 "hostlib_tools_run_test",
123 "run_test",
124 run_test::handle,
125 );
126 register_gated(
127 registry,
128 "hostlib_tools_run_build_command",
129 "run_build_command",
130 run_build_command::handle,
131 );
132 register_gated(
133 registry,
134 "hostlib_tools_inspect_test_results",
135 "inspect_test_results",
136 inspect_test_results::handle,
137 );
138 register_gated(
139 registry,
140 "hostlib_tools_manage_packages",
141 "manage_packages",
142 manage_packages::handle,
143 );
144 register_gated(
145 registry,
146 cancel_handle::NAME,
147 "cancel_handle",
148 cancel_handle::handle,
149 );
150
151 let handler: SyncHandler = Arc::new(handle_enable);
154 registry.register(RegisteredBuiltin {
155 name: "hostlib_enable",
156 module: "tools",
157 method: "enable",
158 handler,
159 });
160 }
161}
162
163fn register_gated(
166 registry: &mut BuiltinRegistry,
167 name: &'static str,
168 method: &'static str,
169 runner: fn(&[VmValue]) -> Result<VmValue, HostlibError>,
170) {
171 let handler: SyncHandler = Arc::new(move |args: &[VmValue]| {
172 if !permissions::is_enabled(permissions::FEATURE_TOOLS_DETERMINISTIC) {
173 return Err(HostlibError::Backend {
174 builtin: name,
175 message: format!(
176 "feature `{}` is not enabled in this session — call \
177 `hostlib_enable(\"{}\")` before invoking deterministic tools",
178 permissions::FEATURE_TOOLS_DETERMINISTIC,
179 permissions::FEATURE_TOOLS_DETERMINISTIC
180 ),
181 });
182 }
183 runner(args)
184 });
185 registry.register(RegisteredBuiltin {
186 name,
187 module: "tools",
188 method,
189 handler,
190 });
191}
192
193fn handle_enable(args: &[VmValue]) -> Result<VmValue, HostlibError> {
198 let feature = match args.first() {
199 Some(VmValue::String(s)) => s.to_string(),
200 Some(VmValue::Dict(dict)) => match dict.get("feature") {
201 Some(VmValue::String(s)) => s.to_string(),
202 _ => {
203 return Err(HostlibError::MissingParameter {
204 builtin: "hostlib_enable",
205 param: "feature",
206 });
207 }
208 },
209 _ => {
210 return Err(HostlibError::MissingParameter {
211 builtin: "hostlib_enable",
212 param: "feature",
213 });
214 }
215 };
216
217 match feature.as_str() {
218 permissions::FEATURE_TOOLS_DETERMINISTIC => {
219 let newly_enabled = permissions::enable(&feature);
220 let mut map: BTreeMap<String, VmValue> = BTreeMap::new();
221 map.insert("feature".to_string(), VmValue::String(Rc::from(feature)));
222 map.insert("enabled".to_string(), VmValue::Bool(true));
223 map.insert("newly_enabled".to_string(), VmValue::Bool(newly_enabled));
224 Ok(VmValue::Dict(Rc::new(map)))
225 }
226 other => Err(HostlibError::InvalidParameter {
227 builtin: "hostlib_enable",
228 param: "feature",
229 message: format!("unknown feature `{other}`; supported: [`tools:deterministic`]"),
230 }),
231 }
232}