Solve one concrete task
Post-process routes and assets
Add output-level processors for route artifacts and transformed assets without changing route handlers.
Add output-level processors
The framework has two processor hooks:
route processors, registered with
processor(...)asset processors, registered with
asset_processor(...)
Use them when the concern is about the final output bytes rather than about how a route or image is authored.
Minify route HTML after rendering
Route processors implement ArtifactProcessor.
The simplest example is the built-in IntertagWhitespaceMinifier:
use site_core::{IntertagWhitespaceMinifier, Site};
let site = Site::builder()
.processor(IntertagWhitespaceMinifier)
.build()?;
That keeps route handlers focused on producing Artifact::Html(...) instead of mixing rendering
with cleanup.
Write a custom route processor
For custom behavior, implement the trait yourself:
use anyhow::Result;
use site_core::{Artifact, ArtifactProcessor};
struct FeedBanner;
impl ArtifactProcessor for FeedBanner {
fn name(&self) -> &'static str {
"feed-banner"
}
fn process(&self, route_path: &str, artifact: Artifact) -> Result<Artifact> {
match (route_path, artifact) {
("/feed.json", Artifact::Json(mut json)) => {
json.push('\n');
Ok(Artifact::Json(json))
}
(_, artifact) => Ok(artifact),
}
}
}
The processor sees the route path and the current artifact, so it can be selective without making that logic every route handler's problem.
If a processor returns a plain error, the framework wraps it in a structured diagnostic that names the processor and points back to the processor registration site.
Post-process asset outputs
Asset processors implement AssetProcessor.
They receive an AssetProcessContext plus the final output bytes:
use anyhow::Result;
use site_core::{AssetProcessContext, AssetProcessor};
struct StripDebugMarker;
impl AssetProcessor for StripDebugMarker {
fn name(&self) -> &'static str {
"strip-debug-marker"
}
fn process(&self, _context: &AssetProcessContext<'_>, bytes: Vec<u8>) -> Result<Vec<u8>> {
Ok(bytes)
}
}
Register it through the site builder:
.asset_processor(StripDebugMarker)
Plain asset-processor errors are also wrapped into structured diagnostics so the failing processor, source asset, and registration site are visible in terminal output.
Make processor cache behavior explicit
Asset processors participate in the asset cache key through cache_key().
If the processor behavior depends on options, include those options in the returned value:
impl AssetProcessor for StripDebugMarker {
fn name(&self) -> &'static str {
"strip-debug-marker"
}
fn cache_key(&self) -> String {
"strip-debug-marker:v1".to_string()
}
fn process(&self, _context: &AssetProcessContext<'_>, bytes: Vec<u8>) -> Result<Vec<u8>> {
Ok(bytes)
}
}
Without that, the asset cache cannot tell that the processor configuration changed.
Choose the right layer
Use a route processor when the transformation is about a route artifact such as HTML, XML, JSON, or text.
Use an asset processor when the transformation is about one built asset file, such as a generated image or copied SVG.