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