1use std::collections::BTreeMap;
36use std::rc::Rc;
37use std::sync::Arc;
38
39use harn_vm::VmValue;
40
41use crate::error::HostlibError;
42use crate::registry::{BuiltinRegistry, HostlibCapability, RegisteredBuiltin, SyncHandler};
43
44pub(crate) mod args;
45mod cancel_handle;
46mod diagnostics;
47mod file_io;
48mod git;
49mod inspect_test_results;
50mod lang;
51pub mod long_running;
52mod manage_packages;
53mod outline;
54mod payload;
55pub mod permissions;
56mod proc;
57mod read_command_output;
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 read_command_output::NAME,
123 "read_command_output",
124 read_command_output::handle,
125 );
126 register_gated(
127 registry,
128 "hostlib_tools_run_test",
129 "run_test",
130 run_test::handle,
131 );
132 register_gated(
133 registry,
134 "hostlib_tools_run_build_command",
135 "run_build_command",
136 run_build_command::handle,
137 );
138 register_gated(
139 registry,
140 "hostlib_tools_inspect_test_results",
141 "inspect_test_results",
142 inspect_test_results::handle,
143 );
144 register_gated(
145 registry,
146 "hostlib_tools_manage_packages",
147 "manage_packages",
148 manage_packages::handle,
149 );
150 register_gated(
151 registry,
152 cancel_handle::NAME,
153 "cancel_handle",
154 cancel_handle::handle,
155 );
156
157 let handler: SyncHandler = Arc::new(handle_enable);
160 registry.register(RegisteredBuiltin {
161 name: "hostlib_enable",
162 module: "tools",
163 method: "enable",
164 handler,
165 });
166 }
167}
168
169fn register_gated(
172 registry: &mut BuiltinRegistry,
173 name: &'static str,
174 method: &'static str,
175 runner: fn(&[VmValue]) -> Result<VmValue, HostlibError>,
176) {
177 let handler: SyncHandler = Arc::new(move |args: &[VmValue]| {
178 if !permissions::is_enabled(permissions::FEATURE_TOOLS_DETERMINISTIC) {
179 return Err(HostlibError::Backend {
180 builtin: name,
181 message: format!(
182 "feature `{}` is not enabled in this session — call \
183 `hostlib_enable(\"{}\")` before invoking deterministic tools",
184 permissions::FEATURE_TOOLS_DETERMINISTIC,
185 permissions::FEATURE_TOOLS_DETERMINISTIC
186 ),
187 });
188 }
189 runner(args)
190 });
191 registry.register(RegisteredBuiltin {
192 name,
193 module: "tools",
194 method,
195 handler,
196 });
197}
198
199fn handle_enable(args: &[VmValue]) -> Result<VmValue, HostlibError> {
204 let feature = match args.first() {
205 Some(VmValue::String(s)) => s.to_string(),
206 Some(VmValue::Dict(dict)) => match dict.get("feature") {
207 Some(VmValue::String(s)) => s.to_string(),
208 _ => {
209 return Err(HostlibError::MissingParameter {
210 builtin: "hostlib_enable",
211 param: "feature",
212 });
213 }
214 },
215 _ => {
216 return Err(HostlibError::MissingParameter {
217 builtin: "hostlib_enable",
218 param: "feature",
219 });
220 }
221 };
222
223 match feature.as_str() {
224 permissions::FEATURE_TOOLS_DETERMINISTIC => {
225 let newly_enabled = permissions::enable(&feature);
226 let mut map: BTreeMap<String, VmValue> = BTreeMap::new();
227 map.insert("feature".to_string(), VmValue::String(Rc::from(feature)));
228 map.insert("enabled".to_string(), VmValue::Bool(true));
229 map.insert("newly_enabled".to_string(), VmValue::Bool(newly_enabled));
230 Ok(VmValue::Dict(Rc::new(map)))
231 }
232 other => Err(HostlibError::InvalidParameter {
233 builtin: "hostlib_enable",
234 param: "feature",
235 message: format!("unknown feature `{other}`; supported: [`tools:deterministic`]"),
236 }),
237 }
238}