harn_parser/builtin_signatures/
lookup.rs1use crate::ast::TypeExpr;
10use harn_builtin_meta::{BuiltinExposure, CapabilityId};
11
12use super::signatures;
13use super::{BuiltinMetadata, BuiltinSignature, Ty, TyExt};
14
15pub fn lookup(name: &str) -> Option<&'static BuiltinSignature> {
21 lookup_with_privileged_wire(name, false)
22}
23
24pub fn lookup_with_privileged_wire(
28 name: &str,
29 allow_privileged_wire: bool,
30) -> Option<&'static BuiltinSignature> {
31 if let Some(entry) = harn_builtin_registry::builtin_entry(name) {
32 return matches!(
33 entry.contract.exposure,
34 BuiltinExposure::PureGlobal | BuiltinExposure::CapabilityFunction { .. }
35 )
36 .then_some(entry.signature)
37 .or_else(|| {
38 (allow_privileged_wire && entry.contract.exposure == BuiltinExposure::PrivilegedWire)
39 .then_some(entry.signature)
40 })
41 .or_else(|| {
42 (crate::legacy_ambient_capabilities_enabled()
43 && matches!(
44 entry.contract.exposure,
45 BuiltinExposure::HarnessMethod { .. }
46 | BuiltinExposure::PrivilegedWire
47 | BuiltinExposure::RuntimeInternal
48 ))
49 .then_some(entry.signature)
50 });
51 }
52 if crate::legacy_ambient_capabilities_enabled() {
53 if let Some(entry) = legacy_capability_method_entry(name) {
54 return Some(entry.signature);
55 }
56 if let Some(entry) = legacy_ambient_cap_global_entry(name) {
57 return Some(entry.signature);
58 }
59 if let Some(canonical) = crate::legacy_builtin_alias_target(name) {
60 return lookup(canonical);
61 }
62 }
63 for group in signatures::groups() {
64 for sig in group {
65 if sig.name == name {
66 return Some(sig);
67 }
68 }
69 }
70 None
71}
72
73pub fn legacy_capability_method_entry(
77 name: &str,
78) -> Option<&'static harn_builtin_registry::BuiltinManifestEntry> {
79 let mut matches = ambient_harness_method_entries()
80 .into_iter()
81 .filter(|entry| {
82 matches!(
83 entry.contract.exposure,
84 BuiltinExposure::HarnessMethod { method, .. } if method == name
85 )
86 });
87 let entry = matches.next()?;
88 matches.next().is_none().then_some(entry)
89}
90
91pub fn legacy_ambient_cap_global_entry(
95 name: &str,
96) -> Option<&'static harn_builtin_registry::BuiltinManifestEntry> {
97 ambient_harness_method_entries()
98 .into_iter()
99 .find(|entry| entry.name.strip_prefix("__cap_") == Some(name))
100}
101
102pub fn legacy_privileged_wire_entry(
105 name: &str,
106) -> Option<&'static harn_builtin_registry::BuiltinManifestEntry> {
107 harn_builtin_registry::installed_manifest()
108 .into_iter()
109 .find(|entry| {
110 matches!(entry.contract.exposure, BuiltinExposure::PrivilegedWire)
111 && entry.name.strip_prefix("__") == Some(name)
112 })
113}
114
115pub fn legacy_ambient_runtime_name(name: &str) -> Option<&'static str> {
123 if let Some(target) = crate::legacy_builtin_alias_target(name) {
124 return Some(target);
125 }
126 legacy_privileged_wire_entry(name).map(|entry| entry.name)
127}
128
129fn ambient_harness_method_entries() -> Vec<&'static harn_builtin_registry::BuiltinManifestEntry> {
130 let installed = harn_builtin_registry::installed_manifest();
135 if installed.is_empty() {
136 harn_capability_contracts::manifest().to_vec()
137 } else {
138 installed
139 }
140}
141
142pub fn lookup_capability_method(
144 capability: CapabilityId,
145 method: &str,
146) -> Option<&'static BuiltinSignature> {
147 capability_method_entry(capability.field_name(), method).map(|entry| entry.signature)
148}
149
150pub fn capability_method_entry(
154 field: &str,
155 method: &str,
156) -> Option<&'static harn_builtin_registry::BuiltinManifestEntry> {
157 let capability = CapabilityId::from_field_name(field)?;
158 harn_builtin_registry::installed_manifest()
159 .iter()
160 .copied()
161 .find(|entry| {
162 matches!(
163 entry.contract.exposure,
164 BuiltinExposure::HarnessMethod {
165 capability: candidate,
166 method: candidate_method,
167 } if candidate == capability && candidate_method == method
168 )
169 })
170 .or_else(|| harn_capability_contracts::capability_method_entry(field, method))
171}
172
173pub fn is_builtin(name: &str) -> bool {
175 lookup(name).is_some()
176 || (crate::legacy_ambient_capabilities_enabled()
177 && crate::is_registered_legacy_hostlib_name(name))
178}
179
180pub fn is_builtin_with_privileged_wire(name: &str, allow_privileged_wire: bool) -> bool {
181 lookup_with_privileged_wire(name, allow_privileged_wire).is_some()
182 || (crate::legacy_ambient_capabilities_enabled()
183 && crate::is_registered_legacy_hostlib_name(name))
184}
185
186pub fn iter_builtin_names() -> impl Iterator<Item = &'static str> {
190 let installed: Vec<_> = harn_builtin_registry::installed_manifest()
191 .into_iter()
192 .filter(|entry| {
193 matches!(
194 entry.contract.exposure,
195 BuiltinExposure::PureGlobal | BuiltinExposure::CapabilityFunction { .. }
196 )
197 })
198 .collect();
199 let installed_names: std::collections::HashSet<&'static str> =
200 harn_builtin_registry::installed_manifest()
201 .into_iter()
202 .map(|entry| entry.name)
203 .collect();
204 installed.into_iter().map(|entry| entry.name).chain(
205 signatures::groups()
206 .into_iter()
207 .flat_map(|g| g.iter())
208 .filter(move |s| !installed_names.contains(s.name))
209 .map(|s| s.name),
210 )
211}
212
213pub fn static_signature_names() -> impl Iterator<Item = &'static str> {
222 signatures::groups()
223 .into_iter()
224 .flat_map(|g| g.iter())
225 .map(|s| s.name)
226}
227
228pub fn iter_builtin_metadata() -> impl Iterator<Item = BuiltinMetadata> {
233 let installed: Vec<_> = harn_builtin_registry::installed_manifest()
234 .into_iter()
235 .filter(|entry| {
236 matches!(
237 entry.contract.exposure,
238 BuiltinExposure::PureGlobal | BuiltinExposure::CapabilityFunction { .. }
239 )
240 })
241 .collect();
242 let installed_names: std::collections::HashSet<&'static str> =
243 harn_builtin_registry::installed_manifest()
244 .into_iter()
245 .map(|entry| entry.name)
246 .collect();
247 installed
248 .into_iter()
249 .map(|entry| BuiltinMetadata {
250 name: entry.name,
251 return_types: builtin_return_type_names(entry.signature),
252 })
253 .chain(
254 signatures::groups()
255 .into_iter()
256 .flat_map(|g| g.iter())
257 .filter(move |s| !installed_names.contains(s.name))
258 .map(|sig| BuiltinMetadata {
259 name: sig.name,
260 return_types: builtin_return_type_names(sig),
261 }),
262 )
263}
264
265pub fn builtin_return_type(name: &str) -> Option<TypeExpr> {
269 let sig = lookup(name)?;
270 if sig.returns.is_any() {
271 return None;
272 }
273 Some(sig.returns.to_type_expr())
274}
275
276pub fn is_untyped_boundary_source(name: &str) -> bool {
283 matches!(
284 name,
285 "json_parse"
286 | "json_extract"
287 | "yaml_parse"
288 | "toml_parse"
289 | "llm_call"
290 | "llm_call_safe"
291 | "llm_completion"
292 | "http_get"
293 | "http_post"
294 | "http_put"
295 | "http_patch"
296 | "http_delete"
297 | "http_download"
298 | "http_request"
299 | "http_session_request"
300 | "http_stream_info"
301 | "sse_receive"
302 | "sse_server_mock_receive"
303 | "sse_server_response"
304 | "sse_server_status"
305 | "websocket_accept"
306 | "websocket_receive"
307 | "host_call"
308 | "connector_call"
309 | "host_tool_call"
310 )
311}
312
313fn builtin_return_type_names(sig: &BuiltinSignature) -> &'static [&'static str] {
319 match &sig.returns {
320 Ty::Named(name) => match *name {
321 "bool" => &["bool"],
322 "bytes" => &["bytes"],
323 "dict" => &["dict"],
324 "float" => &["float"],
325 "int" => &["int"],
326 "list" => &["list"],
327 "nil" => &["nil"],
328 "string" => &["string"],
329 _ => &[],
330 },
331 Ty::Union(members) => match *members {
332 [Ty::Named("string"), Ty::Named("nil")] => &["string", "nil"],
333 [Ty::Named("nil"), Ty::Named("string")] => &["string", "nil"],
334 [Ty::Named("int"), Ty::Named("nil")] => &["int", "nil"],
335 [Ty::Named("nil"), Ty::Named("int")] => &["int", "nil"],
336 [Ty::Named("dict"), Ty::Named("nil")] => &["dict", "nil"],
337 [Ty::Named("nil"), Ty::Named("dict")] => &["dict", "nil"],
338 [Ty::Named("bytes"), Ty::Named("nil")] => &["bytes", "nil"],
339 [Ty::Named("nil"), Ty::Named("bytes")] => &["bytes", "nil"],
340 _ => &[],
341 },
342 Ty::Never => &["never"],
343 _ => &[],
344 }
345}
346
347#[cfg(test)]
348mod ambient_install_regression {
349 use super::*;
350
351 #[test]
352 fn installed_manifest_does_not_shadow_ambient_capability_methods() {
353 std::env::set_var("HARN_LEGACY_AMBIENT_CAPABILITIES", "1");
354 assert!(
355 is_builtin("store_set"),
356 "capability-contracts fallback must resolve ambient store_set"
357 );
358
359 let entries: &'static [&'static harn_builtin_registry::BuiltinManifestEntry] = Box::leak(
361 harn_capability_contracts::manifest()
362 .to_vec()
363 .into_boxed_slice(),
364 );
365 harn_builtin_registry::install_builtin_manifest(entries);
366
367 assert!(
368 is_builtin("store_set"),
369 "after manifest install, ambient store_set must still resolve uniquely"
370 );
371 assert!(
372 legacy_capability_method_entry("store_set").is_some(),
373 "legacy_capability_method_entry must stay unique after install"
374 );
375 }
376}