pub(crate) mod functions;
use functions::*;
use std::sync::{Arc, RwLock};
use minijinja::{Environment, Value};
use taganak_core::{
graphs::{Graph, GraphView},
terms::Term,
};
use tokio::runtime::Handle;
use tracing::{debug, error, trace};
use crate::Params;
const MAX_TEMPLATE_RECURSION: usize = 3;
fn template_from_graph<G>(
name: &str,
graph: Arc<RwLock<G>>,
default_template: Option<Term>,
) -> Result<Option<String>, minijinja::Error>
where
G: Graph + std::fmt::Debug + Sync + 'static,
{
let handle = Handle::current();
let name = match name {
"default" => {
if let Some(ref template) = default_template {
debug!("Requested default template");
template.to_string()
} else {
error!("Requested default template but none provided");
return Ok(None);
}
}
_ => name.to_string(),
};
handle.block_on(async {
let graph_clone = graph.clone();
let graph_read = graph_clone.read().unwrap();
let mut name = name.to_string();
let mut recursion = 0usize;
debug!(?name, "Loading template for");
while recursion < MAX_TEMPLATE_RECURSION {
recursion += 1;
match graph_read
.view()
.await
.object(
Some(&name.clone().try_into().unwrap()),
Some(
&"<https://trinja.taganak.net/vocab/template>"
.try_into()
.expect("static IRI"),
),
)
.await
.ok()
.unwrap()
.as_deref()
{
Some(Term::Literal(literal)) => {
if literal.datatype() == "https://trinja.taganak.net/vocab/minijinja" {
debug!(?name, "specific template found for");
return Ok(Some(literal.lexical().to_string()));
} else {
error!(?name, "template with unsupported datatype for");
return Ok(None);
}
}
Some(node @ Term::NamedNode(_)) => {
name = node.to_string();
debug!(?name, "Recursing to new template");
continue;
}
None => {
if recursion > 1 {
error!(?name, "referenced template does not contain literal");
return Ok(None);
}
let rdf_type = graph_read
.view()
.await
.object(
Some(&name.clone().try_into().unwrap()),
Some(
&"<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>"
.try_into()
.expect("static IRI"),
),
)
.await
.unwrap()
.unwrap();
debug!(?rdf_type, "Finding template for RDF type of resource");
match graph_read
.view()
.await
.object(
Some(&rdf_type),
Some(
&"<https://trinja.taganak.net/vocab/genericTemplate>"
.try_into()
.expect("static IRI"),
),
)
.await
.ok()
.unwrap()
.as_deref()
{
Some(Term::Literal(literal)) => {
if literal.datatype() == "https://trinja.taganak.net/vocab/minijinja" {
debug!(?name, ?rdf_type, "generic template for");
return Ok(Some(literal.lexical().to_string()));
} else {
error!(?rdf_type, "generic template has unsupported datatype");
return Ok(None);
}
}
Some(node @ Term::NamedNode(_)) => {
name = node.to_string();
debug!(?name, "Recursing to new template");
continue;
}
None => {
if let Some(ref term) = default_template {
debug!(?term, "No type; trying to load default template");
name = term.to_string();
continue;
}
error!(?name, "no template found and no default template set");
return Ok(None);
}
_ => {
error!(?name, "no template found for");
return Ok(None);
}
}
}
Some(_) => {
error!(
?name,
"template is neither reference nor literal (schema error)"
);
return Ok(None);
}
};
}
Ok(None)
})
}
pub(crate) fn environment<'e, G>(
params: Params<'e, G>,
default_template: Option<Term>,
) -> Arc<RwLock<Environment<'static>>>
where
G: Graph + std::fmt::Debug + Sync + 'static,
{
trace!("Building new minijinja environment");
let env: Environment<'static> = Environment::new();
let g = params.graph.clone();
let env = Arc::new(RwLock::new(env.clone()));
env.write().unwrap().add_global(
"rdf_get",
Value::from_object(rdf_get::GraphFn::new(
params
.clone()
.environment(Some(env.clone()))
.to_with_env()
.unwrap(),
)),
);
env.write().unwrap().add_global(
"rdf_subjects",
Value::from_object(rdf_subjects::GraphFn::new(
params
.clone()
.environment(Some(env.clone()))
.to_with_env()
.unwrap(),
)),
);
env.write().unwrap().add_global(
"rdf_objects",
Value::from_object(rdf_objects::GraphFn::new(
params
.clone()
.environment(Some(env.clone()))
.to_with_env()
.unwrap(),
)),
);
env.write()
.unwrap()
.set_loader(move |name| template_from_graph(name, g.clone(), default_template.clone()));
env
}