graph-store 0.4.0

Access to graph stores over the SPARQL 1.1 Graph Store HTTP Protocol
use crate::http::Dataset;
use crate::table::Variable;
use crate::{GraphStore, Resource, Selection};
use rdf::node::Node;
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use std::fs;
use typed_html::dom::DOMTree;
use typed_html::{html, text};

pub async fn export_to_html<'a>(dataset: &'a Dataset<'a>) {
    #[derive(Debug)]
    struct ResourceProperties {
        label: String,
        file_name: String,
    }

    let map: HashMap<Resource, ResourceProperties> = dataset
        .select(Selection::of_resources_with_labels())
        .await
        .bindings
        .into_iter()
        .map(|r| {
            let resource = match r.get(&Variable::from("resource")) {
                Some(rdf::node::Node::UriNode { uri: id }) => id.to_string().clone(),
                _ => panic!("Unexpected resource"),
            };
            (
                Resource::from(resource.as_ref()),
                ResourceProperties {
                    label: match r.get(&Variable::from("label")) {
                        Some(rdf::node::Node::LiteralNode {
                            literal: s,
                            data_type: _,
                            language: _,
                        }) => s.to_string(),
                        _ => resource.to_string(),
                    },
                    file_name: {
                        let mut hasher = Sha256::new();
                        hasher.input(&resource.to_string());
                        format!("{}.html", hex::encode(hasher.result()))
                    },
                },
            )
        })
        .collect();

    fn resource_component(
        map: &HashMap<Resource, ResourceProperties>,
        hash_map: &HashMap<Variable, Node>,
        selector: &Variable,
    ) -> Box<typed_html::elements::a<String>> {
        match hash_map.get(selector) {
            Some(rdf::node::Node::UriNode { uri: u }) => {
                let resource = Resource::from(u.to_string().as_str());
                match map.get(&resource) {
                    Some(props) => {
                        let href = &props.file_name;
                        html!(<a href=href>{ text!("{}", props.label) }</a>)
                    }
                    None => html!(<a>{ text!("{}", &resource.0) }</a>),
                }
            }
            Some(rdf::node::Node::LiteralNode {
                literal: s,
                language: _,
                data_type: _,
            }) => html!(<a>{ text!("{}", s) }</a>),
            Some(rdf::node::Node::BlankNode { id }) => html!(<a>{ text!("Blank node {}", id) }</a>),
            r => panic!("Unexpected resource {:?}", r),
        }
    }

    for (r, props) in map.iter() {
        let links_from = dataset.select(Selection::of_relations_from(r)).await;
        let links_to = dataset.select(Selection::of_relations_to(r)).await;

        let links_from_html = links_from.bindings.iter().map(|link| {
            html!(
                <li>
                    { resource_component(&map, link, &Variable::from("predicate")) }
                    { text!("") }
                    { resource_component(&map, link, &Variable::from("object")) }
                </li>
            )
        });

        let links_to_html = links_to.bindings.iter().map(|link| {
            html!(
                <li>
                    { resource_component(&map, link, &Variable::from("subject")) }
                    { text!("") }
                    { resource_component(&map, link, &Variable::from("predicate")) }
                </li>
            )
        });

        let doc: DOMTree<String> = html!(
            <html>
                <head>
                    <title>{ text!("{}", props.label) }</title>
                    <link rel="stylesheet" href="main.css"/>
                </head>
                <body>
                    <h1>{ text!("{}", props.label) }</h1>
                    <ul>
                        { links_from_html }
                        { links_to_html }
                    </ul>
                    <a href="index.html">"Index"</a>
                </body>
            </html>
        );

        fs::write(
            props.file_name.to_string(),
            format!("<!doctype html>{}", doc.to_string()),
        )
        .expect("Could not write file");
    }

    let links = map.iter().map(|(_, props)| {
        let href = &props.file_name;
        let label = &props.label;
        html!(
            <li>
                <a href=href>{ text!("{}", label) }</a>
            </li>
        )
    });

    let doc: DOMTree<String> = html!(
        <html>
            <head>
                <title>"Index"</title>
                <link rel="stylesheet" href="main.css"/>
            </head>
            <body>
                <h1>"Index"</h1>
                <ul>
                    { links }
                </ul>
            </body>
        </html>
    );

    fs::write("index.html", format!("<!doctype html>{}", doc.to_string()))
        .expect("Could not write index");
}