use camino::{Utf8Path, Utf8PathBuf};
use pagefind::api::PagefindIndex;
use pagefind::options::PagefindServiceConfig;
use petgraph::graph::NodeIndex;
use std::collections::HashSet;
use tokio::runtime::Builder;
use crate::core::{Dynamic, Store};
use crate::engine::{TrackerPtr, Tracking, TypedCoarse};
use crate::output::{OutputData, OutputHandle};
use crate::{Blueprint, One, Output, TaskContext};
struct PagefindSource {
index: NodeIndex,
resolver: fn(&Dynamic) -> (Option<TrackerPtr>, Vec<&Output>),
}
pub struct PagefindBuilder<'a, G: Send + Sync> {
blueprint: &'a mut Blueprint<G>,
sources: Vec<PagefindSource>,
}
impl<'a, G: Send + Sync + 'static> PagefindBuilder<'a, G> {
pub(crate) fn new(blueprint: &'a mut Blueprint<G>) -> Self {
Self {
blueprint,
sources: Vec::new(),
}
}
pub fn index<H>(mut self, handle: H) -> Self
where
H: OutputHandle,
{
self.sources.push(PagefindSource {
index: handle.index(),
resolver: H::resolve_refs,
});
self
}
pub fn register(self) -> One<Vec<Output>> {
self.blueprint.add_task_coarse(PagefindTask {
sources: self.sources,
})
}
}
struct PagefindTask {
sources: Vec<PagefindSource>,
}
impl<G: Send + Sync> TypedCoarse<G> for PagefindTask {
type Output = Vec<Output>;
fn get_name(&self) -> String {
"pagefind".to_string()
}
fn dependencies(&self) -> Vec<NodeIndex> {
self.sources.iter().map(|s| s.index).collect()
}
fn get_watched(&self) -> Vec<Utf8PathBuf> {
vec![]
}
fn execute(
&self,
_: &TaskContext<G>,
_: &mut Store,
dependencies: &[Dynamic],
) -> anyhow::Result<(Tracking, Self::Output)> {
let mut tracking = Tracking::default();
let mut pages_to_index = Vec::new();
for (source, input) in self.sources.iter().zip(dependencies.iter()) {
let (tracker, items) = (source.resolver)(input);
tracking.edges.push(tracker);
for page in items {
if let OutputData::Utf8(data) = &page.data
&& matches!(page.path.extension(), Some("htm") | Some("html"))
{
pages_to_index.push((page.path.to_string(), data.clone()));
}
}
}
let output = Builder::new_current_thread()
.enable_all()
.build()?
.block_on(async move {
let config = PagefindServiceConfig::builder().build();
let mut index = PagefindIndex::new(Some(config))?;
for (path, content) in pages_to_index {
index.add_html_file(Some(path), None, content).await?;
}
index.build_indexes().await?;
let files = index.get_files().await?;
let mut artifacts = Vec::new();
for file in files {
let path = Utf8PathBuf::try_from(file.filename)?;
let path = Utf8Path::new("_pagefind").join(path);
artifacts.push(Output::binary(path, file.contents));
}
Ok::<_, anyhow::Error>(artifacts)
})?;
Ok((tracking, output))
}
fn is_valid(
&self,
_: &[Option<crate::engine::TrackerState>],
_: &[Dynamic],
updated: &HashSet<NodeIndex>,
) -> bool {
!self.sources.iter().any(|s| updated.contains(&s.index))
}
}
impl<G: Send + Sync + 'static> Blueprint<G> {
pub fn use_pagefind(&mut self) -> PagefindBuilder<'_, G> {
PagefindBuilder::new(self)
}
}