use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
use crate::error::ScrapsResult;
use crate::service::tera_render::{render_to_file, user_template_glob};
use crate::usecase::build::model::backlinks_map::BacklinksMap;
use crate::usecase::build::model::html::HtmlMetadata;
use crate::usecase::build::model::scrap_detail::ScrapDetail;
use crate::usecase::build::model::scrap_graph::{ScrapGraph, MAX_NODES};
use crate::usecase::build::model::site_nav::SiteNav;
use scraps_libs::model::base_url::BaseUrl;
use scraps_libs::model::file::ScrapFileStem;
use scraps_libs::model::key::ScrapKey;
use scraps_libs::model::scrap::Scrap;
use tera::Tera;
use crate::usecase::build::html::templates;
use super::serde::link_scraps::LinkScrapsTera;
use super::serde::scrap_detail::ScrapDetailTera;
use super::serde::scrap_graph::ScrapGraphTera;
use super::serde::tag::TagTera;
pub struct ScrapRender {
tera: Tera,
output_scraps_dir_path: PathBuf,
}
impl ScrapRender {
pub fn new(static_dir_path: &Path, output_dir_path: &Path) -> ScrapsResult<ScrapRender> {
let tera = templates::scrap(&user_template_glob(static_dir_path, "*.html"))?;
Ok(ScrapRender {
tera,
output_scraps_dir_path: output_dir_path.join("scraps"),
})
}
pub fn run(
&self,
base_url: &BaseUrl,
metadata: &HtmlMetadata,
scrap_detail: &ScrapDetail,
backlinks_map: &BacklinksMap,
scraps_by_key: &HashMap<ScrapKey, Scrap>,
site_nav: &SiteNav,
) -> ScrapsResult<()> {
let mut context = templates::context(base_url, metadata);
templates::insert_site_nav(&mut context, "", site_nav, backlinks_map);
let scrap = &scrap_detail.scrap();
context.insert("scrap", &ScrapDetailTera::from(scrap_detail.clone()));
let scrap_tags = scrap
.tags()
.iter()
.map(|tag| TagTera::new(tag, backlinks_map))
.collect::<Vec<_>>();
context.insert("scrap_tags", &scrap_tags);
let linked_scraps = backlinks_map.get(&scrap.self_key());
context.insert("linked_scraps", &LinkScrapsTera::new(&linked_scraps));
let mut seen = HashSet::new();
let outbound_scraps = scrap
.links()
.iter()
.filter(|key| seen.insert((*key).clone()))
.filter_map(|key| scraps_by_key.get(key).cloned())
.collect::<Vec<_>>();
context.insert("outbound_scraps", &LinkScrapsTera::new(&outbound_scraps));
if let Some(graph) = ScrapGraph::new(
&scrap.title().to_string(),
&linked_scraps,
&outbound_scraps,
MAX_NODES,
) {
context.insert("scrap_graph", &ScrapGraphTera::from(&graph));
}
let file_path = self
.output_scraps_dir_path
.join(format!("{}.html", ScrapFileStem::from(scrap.self_key())));
render_to_file(&self.tera, "__builtins/scrap.html", &context, &file_path)
}
}
#[cfg(test)]
mod tests {
use std::fs;
use url::Url;
use crate::usecase::build::model::backlinks_map::BacklinksMap;
use crate::usecase::build::model::html::HtmlMetadata;
use scraps_libs::lang::LangCode;
use scraps_libs::model::base_url::BaseUrl;
use scraps_libs::model::scrap::Scrap;
use scraps_libs::model::tags::Tags;
use super::*;
#[test]
fn it_run() {
let base_url = &BaseUrl::new(Url::parse("http://localhost:1112/").unwrap()).unwrap();
let metadata = HtmlMetadata::new(
&LangCode::default(),
"Scrap",
&Some("Scrap Wiki".to_string()),
&Some(Url::parse("https://github.io/image.png").unwrap()),
);
let test_resource_path =
PathBuf::from("tests/resource/build/html/render/it_render_scrap_htmls");
let static_dir_path = test_resource_path.join("static");
let output_dir_path = test_resource_path.join("_site");
let commited_ts1 = None;
let scrap1 = &Scrap::new("scrap 1", &None, "# header1");
let scrap2 = &Scrap::new(
"scrap 2",
&Some("Context".into()),
"[[scrap 1]] #[[design]]",
);
let scraps = vec![scrap1.to_owned(), scrap2.to_owned()];
let scrap_texts = scraps
.iter()
.map(|scrap| (scrap.self_key(), scrap.md_text().to_string()))
.collect();
let backlinks_map = BacklinksMap::new(&scraps);
let site_nav = SiteNav::new(
scraps.len(),
Tags::new(&scraps),
true,
chrono_tz::UTC,
false,
);
let scraps_by_key: HashMap<_, _> = scraps
.iter()
.map(|scrap| (scrap.self_key(), scrap.clone()))
.collect();
let scrap1_html_path = output_dir_path.join("scraps/scrap-1.html");
let scrap2_html_path = output_dir_path.join("scraps/context/scrap-2.html");
let render = ScrapRender::new(&static_dir_path, &output_dir_path).unwrap();
render
.run(
base_url,
&metadata,
&ScrapDetail::new(scrap1, &commited_ts1, base_url, &scrap_texts),
&backlinks_map,
&scraps_by_key,
&site_nav,
)
.unwrap();
let result2 = fs::read_to_string(scrap1_html_path).unwrap();
assert!(result2.contains(">backlinks · 1"));
assert!(!result2.contains(">links ·"));
assert!(!result2.contains("corsproxy"));
render
.run(
base_url,
&metadata,
&ScrapDetail::new(scrap2, &commited_ts1, base_url, &scrap_texts),
&backlinks_map,
&scraps_by_key,
&site_nav,
)
.unwrap();
let result4 = fs::read_to_string(scrap2_html_path).unwrap();
assert!(result4.contains(">links · 1"));
assert!(result4.contains("tags/design.html"));
assert!(result4.contains("#[["));
}
#[test]
fn it_draws_the_neighbourhood_only_when_there_is_one() {
let base_url = &BaseUrl::new(Url::parse("http://localhost:1112/").unwrap()).unwrap();
let metadata = HtmlMetadata::new(&LangCode::default(), "Scrap", &None, &None);
let linked = &Scrap::new("linked", &None, "");
let linking = &Scrap::new("linking", &None, "[[linked]]");
let alone = &Scrap::new("alone", &None, "no wiki links here");
let scraps = vec![linked.to_owned(), linking.to_owned(), alone.to_owned()];
let scrap_texts = scraps
.iter()
.map(|scrap| (scrap.self_key(), scrap.md_text().to_string()))
.collect();
let backlinks_map = BacklinksMap::new(&scraps);
let scraps_by_key: HashMap<_, _> = scraps
.iter()
.map(|scrap| (scrap.self_key(), scrap.clone()))
.collect();
let site_nav = SiteNav::new(
scraps.len(),
Tags::new(&scraps),
false,
chrono_tz::UTC,
false,
);
let project = crate::test_fixtures::TempScrapProject::new();
let render = ScrapRender::new(&project.static_dir, &project.output_dir).unwrap();
let render_one = |scrap: &Scrap| {
render
.run(
base_url,
&metadata,
&ScrapDetail::new(scrap, &None, base_url, &scrap_texts),
&backlinks_map,
&scraps_by_key,
&site_nav,
)
.unwrap();
};
render_one(linked);
render_one(linking);
render_one(alone);
let read = |stem: &str| {
fs::read_to_string(project.output_dir.join(format!("scraps/{stem}.html"))).unwrap()
};
let backlinked = read("linked");
assert!(backlinked.contains("<svg class=\"scrap-graph\""));
assert!(backlinked.contains("marker-start=\"url(#scrap-graph-arrow)\""));
assert!(!backlinked.contains("marker-end=\"url(#scrap-graph-arrow)\""));
let outbound = read("linking");
assert!(outbound.contains("marker-end=\"url(#scrap-graph-arrow)\""));
assert!(!outbound.contains("marker-start=\"url(#scrap-graph-arrow)\""));
assert!(!read("alone").contains("scrap-graph"));
}
}