use std::borrow::Cow;
use autumn_web::app::AppBuilder;
use autumn_web::assets::PluginAssets;
use autumn_web::plugin::Plugin;
use autumn_web::plugin_contract::PluginContract;
use crate::assets::REACT_ASSETS;
pub const PLUGIN_NAME: &str = env!("CARGO_PKG_NAME");
#[derive(Debug, Default)]
#[must_use]
pub struct ReactPlugin {
bundles: Vec<&'static PluginAssets>,
}
impl ReactPlugin {
pub const fn new() -> Self {
Self {
bundles: Vec::new(),
}
}
pub fn bundle(mut self, bundle: &'static PluginAssets) -> Self {
assert!(
bundle.namespace() != REACT_ASSETS.namespace(),
"the namespace `react` belongs to {PLUGIN_NAME}; give the app bundle another namespace"
);
if !self.bundles.iter().any(|b| std::ptr::eq(*b, bundle)) {
self.bundles.push(bundle);
}
self
}
}
fn bundle_id(bundle: &PluginAssets) -> String {
let mut hash: u32 = 0x811c_9dc5;
for asset in bundle.iter() {
for byte in asset.url().bytes().chain([0]) {
hash ^= u32::from(byte);
hash = hash.wrapping_mul(0x0100_0193);
}
}
format!("{}@{hash:08x}", bundle.namespace())
}
impl Plugin for ReactPlugin {
fn name(&self) -> Cow<'static, str> {
if self.bundles.is_empty() {
return Cow::Borrowed(PLUGIN_NAME);
}
let mut ids: Vec<String> = self.bundles.iter().map(|b| bundle_id(b)).collect();
ids.sort_unstable();
ids.dedup();
Cow::Owned(format!("{PLUGIN_NAME}[{}]", ids.join(",")))
}
fn contract(&self) -> Option<PluginContract> {
Some(
PluginContract::new(PLUGIN_NAME)
.plugin_version(env!("CARGO_PKG_VERSION"))
.autumn_web("0.9"),
)
}
fn build(self, app: AppBuilder) -> AppBuilder {
self.bundles
.into_iter()
.fold(app.plugin_assets(&REACT_ASSETS), AppBuilder::plugin_assets)
}
}