use lazy_regex::regex;
use rudof_iri::iri;
use crate::ir::actions::semantic_action_error::SemanticActionError;
use crate::ir::actions::semantic_action_extension::SemanticActionExtension;
use crate::ir::semantic_action_context::SemanticActionContext;
#[derive(Debug, Clone)]
pub struct TestActionExtension {}
impl TestActionExtension {
pub fn new() -> Self {
TestActionExtension {}
}
}
impl Default for TestActionExtension {
fn default() -> Self {
Self::new()
}
}
impl SemanticActionExtension for TestActionExtension {
fn action_iri(&self) -> rudof_iri::IriS {
iri!("http://shex.io/extensions/Test/")
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
fn run_action(&self, parameter: Option<&str>, context: &SemanticActionContext) -> Result<(), SemanticActionError> {
let code = if let Some(parameter) = parameter {
parameter
} else {
return Ok(()); };
let re = regex!(r#"^ *(fail|print) *\( *(?:("(?:[^\\"]|\\\\|\\")*")|([spo])) *\) *$"#);
let caps = re
.captures(code)
.ok_or_else(|| SemanticActionError::InvalidTestParameter {
parameter: code.to_string(),
})?;
let directive = &caps[1];
let message: String = if let Some(quoted) = caps.get(2) {
let inner = "ed.as_str()[1..quoted.as_str().len() - 1];
inner.replace(r#"\\"#, r"\").replace(r#"\""#, "\"")
} else {
let particle = &caps[3];
let binding = match particle {
"s" => context.s().map(|o| o.to_string()),
"p" => context.p().map(|p| p.to_string()),
"o" => context.o().map(|o| o.to_string()),
_ => unreachable!("regex only matches s, p, or o"),
};
match binding {
Some(str) => str.to_string(),
None => {
eprintln!("Warning: Unresolved variable {particle} in Test semact: no binding provided");
String::new()
},
}
};
match directive {
"print" => {
println!("{message}");
Ok(())
},
"fail" => Err(SemanticActionError::FailAction { message }),
_ => unreachable!("regex only matches fail or print"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Node;
fn ext() -> TestActionExtension {
TestActionExtension {}
}
#[test]
fn print_literal() {
ext()
.run_action(Some(r#"print("hello world")"#), &SemanticActionContext::default())
.unwrap();
}
#[test]
fn print_escaped_literal() {
ext()
.run_action(Some(r#"print("say \"hi\"")"#), &SemanticActionContext::default())
.unwrap();
}
#[test]
fn print_subject() {
ext()
.run_action(
Some("print(s)"),
&SemanticActionContext::subject(&Node::iri(iri!("http://example.org/s"))),
)
.unwrap();
}
#[test]
fn fail_literal() {
let err = ext()
.run_action(Some(r#"fail("bad value")"#), &SemanticActionContext::default())
.unwrap_err();
assert!(matches!(err, SemanticActionError::FailAction { message } if message == "bad value"));
}
#[test]
fn fail_object() {
let err = ext()
.run_action(
Some("fail(o)"),
&SemanticActionContext::object(&Node::iri(iri!("http://example.org/bad"))),
)
.unwrap_err();
assert!(matches!(err, SemanticActionError::FailAction { message } if message == "http://example.org/bad"));
}
#[test]
fn invalid_parameter() {
let err = ext()
.run_action(Some("unknown(s)"), &SemanticActionContext::default())
.unwrap_err();
assert!(matches!(err, SemanticActionError::InvalidTestParameter { .. }));
}
#[test]
fn empty_parameter() {
ext().run_action(None, &SemanticActionContext::default()).unwrap();
}
}