use std::fs;
use std::path::Path;
use crate::error::SsgError;
use crate::plugin::{Plugin, PluginContext};
pub const RPC_DTS_RELATIVE_PATH: &str = ".ssg/rpc.d.ts";
#[derive(Debug, Clone, Copy, Default)]
pub struct RpcSchemaPlugin;
impl RpcSchemaPlugin {
#[must_use]
pub const fn new() -> Self {
Self
}
}
impl Plugin for RpcSchemaPlugin {
fn name(&self) -> &'static str {
"rpc-schema"
}
fn after_compile(&self, ctx: &PluginContext) -> Result<(), SsgError> {
if ctx.dry_run {
return Ok(());
}
if ssg_rpc::dispatch::iter_descriptors().next().is_none() {
return Ok(());
}
let opts = ssg_rpc::EmitOptions::default();
let ts = ssg_rpc::emit_typescript(&opts);
let out_path = ctx.site_dir.join(RPC_DTS_RELATIVE_PATH);
ensure_parent(&out_path)?;
fs::write(&out_path, ts).map_err(|e| SsgError::Io {
path: out_path.clone(),
source: e,
})?;
Ok(())
}
}
fn ensure_parent(path: &Path) -> Result<(), SsgError> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).map_err(|e| SsgError::Io {
path: parent.to_path_buf(),
source: e,
})?;
}
Ok(())
}
#[cfg(test)]
#[allow(clippy::unwrap_used)]
mod tests {
use super::*;
use crate::plugin::PluginContext;
use tempfile::tempdir;
fn ctx_for(site_dir: &Path) -> PluginContext {
PluginContext {
content_dir: site_dir.to_path_buf(),
build_dir: site_dir.to_path_buf(),
site_dir: site_dir.to_path_buf(),
template_dir: site_dir.to_path_buf(),
config: None,
cache: None,
memory_budget: None,
html_files: None,
dep_graph: None,
dry_run: false,
}
}
#[test]
fn plugin_name_is_stable() {
assert_eq!(RpcSchemaPlugin::new().name(), "rpc-schema");
}
#[test]
fn dry_run_short_circuits() {
let dir = tempdir().unwrap();
let mut ctx = ctx_for(dir.path());
ctx.dry_run = true;
RpcSchemaPlugin::new().after_compile(&ctx).unwrap();
assert!(!dir.path().join(RPC_DTS_RELATIVE_PATH).exists());
}
#[test]
fn empty_inventory_is_a_noop_or_writes_valid_header() {
let dir = tempdir().unwrap();
let ctx = ctx_for(dir.path());
RpcSchemaPlugin::new().after_compile(&ctx).unwrap();
let path = dir.path().join(RPC_DTS_RELATIVE_PATH);
if path.exists() {
let txt = fs::read_to_string(&path).unwrap();
assert!(
txt.contains("AUTO-GENERATED"),
"emitted file should carry the header: {txt}"
);
}
}
}