use harn_parser::{Node, SNode};
pub(super) fn harness_handle_field(node: &SNode) -> Option<&str> {
match &node.node {
Node::PropertyAccess { object, property } if matches!(&object.node, Node::Identifier(root) if root == "harness") => {
Some(property)
}
Node::Identifier(name)
if harn_builtin_meta::CapabilityId::from_field_name(name).is_some() =>
{
Some(name)
}
_ => None,
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) struct HarnessMethodReceiver<'a> {
pub(super) field: &'a str,
pub(super) capability: harn_builtin_meta::CapabilityId,
}
pub(super) fn harness_method_receiver(object: &SNode) -> Option<HarnessMethodReceiver<'_>> {
let Node::PropertyAccess {
object: root,
property,
} = &object.node
else {
return None;
};
if !matches!(&root.node, Node::Identifier(name) if name == "harness") {
return None;
}
let capability = harn_builtin_meta::CapabilityId::from_field_name(property)?;
Some(HarnessMethodReceiver {
field: property,
capability,
})
}
#[cfg(test)]
mod tests {
use super::*;
use harn_lexer::Lexer;
use harn_parser::Parser;
fn first_method_call(source: &str) -> (SNode, String) {
let tokens = Lexer::new(source).tokenize().expect("lex");
let program = Parser::new(tokens).parse().expect("parse");
let mut found = None;
harn_parser::visit::walk_program(&program, &mut |node| {
if found.is_some() {
return;
}
if let Node::MethodCall { object, method, .. } = &node.node {
found = Some(((**object).clone(), method.clone()));
}
});
found.expect("a method call")
}
fn receiver_of(source: &str) -> Option<harn_builtin_meta::CapabilityId> {
let (object, _) = first_method_call(source);
harness_method_receiver(&object).map(|receiver| receiver.capability)
}
#[test]
fn resolves_explicit_harness_capability_receivers() {
assert_eq!(
receiver_of("fn main(harness: Harness) {\n harness.llm.call(\"u\", \"s\")\n}\n"),
Some(harn_builtin_meta::CapabilityId::Llm)
);
assert_eq!(
receiver_of(
"fn main(harness: Harness) {\n harness.process.exec_at(\"d\", \"ls\")\n}\n"
),
Some(harn_builtin_meta::CapabilityId::Process)
);
}
#[test]
fn rejects_a_non_harness_receiver_with_a_colliding_method_name() {
assert_eq!(
receiver_of("fn main(harness: Harness) {\n client.exec_at(\"d\", \"ls\")\n}\n"),
None
);
assert_eq!(
receiver_of("fn main(harness: Harness) {\n client.call(\"u\", \"s\")\n}\n"),
None
);
}
#[test]
fn declines_a_bare_capability_named_receiver_but_handle_field_accepts_it() {
assert_eq!(
receiver_of("fn main(harness: Harness) {\n process.exec_at(\"d\", \"ls\")\n}\n"),
None
);
let (object, _) =
first_method_call("fn main(harness: Harness) {\n process.exec_at(\"d\")\n}\n");
assert_eq!(harness_handle_field(&object), Some("process"));
}
}