trinja 0.7.1

HTML templating / SSG for RDF(S) resources
Documentation
use std::{collections::HashMap, path::PathBuf, sync::Arc};

use minijinja::{State, Value, value::Object};
use taganak_core::graphs::Graph;
use taganak_orm::{GraphORMMeta, re::Term};
use tokio::runtime::Handle;
use tracing::debug;

use crate::{
    Params,
    res::str_to_term,
    site::{GenericPage, Page},
};

#[derive(Debug, Clone)]
pub struct RdfLinkFn<G> {
    params: Params<'static, G>,
    page: GraphORMMeta<Page>,
    // FIXME get reference instead or make more efficient in other ways
    pages_map: HashMap<Arc<Term>, (GraphORMMeta<Page>, Option<GraphORMMeta<GenericPage>>)>,
}

impl<G> RdfLinkFn<G>
where
    G: Graph + std::fmt::Debug + Sync + 'static,
{
    pub fn new(
        params: Params<'static, G>,
        page: GraphORMMeta<Page>,
        pages_map: HashMap<Arc<Term>, (GraphORMMeta<Page>, Option<GraphORMMeta<GenericPage>>)>,
    ) -> Self {
        Self {
            params,
            page,
            pages_map,
        }
    }
}

impl<G> Object for RdfLinkFn<G>
where
    G: Graph + std::fmt::Debug + Sync + 'static,
{
    fn call(
        self: &Arc<Self>,
        _state: &State<'_, '_>,
        args: &[Value],
    ) -> Result<Value, minijinja::Error> {
        if args.is_empty() {
            return Err(minijinja::Error::new(
                minijinja::ErrorKind::MissingArgument,
                "needs subject",
            ));
        }

        if args.len() > 1 {
            return Err(minijinja::Error::new(
                minijinja::ErrorKind::TooManyArguments,
                "too many arguments",
            ));
        }

        let handle = Handle::current();
        let args_to_string = &args[0].to_string();
        handle.block_on(async {
            let target_subject = str_to_term(
                self.params.clone(),
                args_to_string,
            )
            .unwrap();
            debug!(
                "Finding page {} relative to page {}",
                target_subject, self.page.subject().unwrap(),
            );
            let page_iri = self.page.subject().unwrap().to_iri().unwrap().clone();

            let graph_clone = self.params.graph.clone();
            let graph = graph_clone.read().unwrap();
            let view = graph.view().await;
            let page = self.pages_map.get(&target_subject).unwrap().0.clone().into_deref();

            let out_rel = match page.output {
                Some(output) => {
                    let page_path = match &self.page.output {
                        None => PathBuf::from(
                            page_iri
                                .relative_to(&self.params.base.as_ref().unwrap().to_iri().unwrap())
                                .to_string(),
                        ),
                        Some(output) => PathBuf::from(output),
                    };
                    let output_path = PathBuf::from(output.clone());
                    debug!(
                        ?page_path,
                        ?output_path,
                        "Page has explicit output path, calculating relative to origin page output path"
                    );

                    let mut out_rel = PathBuf::new();

                    for (n, _) in page_path.components().enumerate() {
                        if n > 0 {
                            out_rel.push("..");
                        }
                    }
                    out_rel.push(output_path);

                    out_rel
                }
                None => {
                    debug!(
                        ?page_iri,
                        ?target_subject,
                        "Calculating target page path relative to origin page"
                    );
                    let mut out_rel = PathBuf::from(
                        target_subject
                            .to_iri()
                            .unwrap()
                            .relative_to(&page_iri)
                            .to_string(),
                    );
                    if out_rel.extension().is_none() {
                        out_rel.set_extension("html");
                    }
                    out_rel
                }
            };

            Ok(Value::from(out_rel.to_str().unwrap().to_string()))
        })
    }
}