use crate::dyn_mod::module::{ ComponentMap, ParameterMap };
use crate::dyn_mod::parameters::ComponentParameters;
use crate::dyn_mod::{
Component,
FullManifest as _,
DmRouter,
HasProvider,
Interface,
Provider,
ProviderFn,
Router,
};
use crate::dyn_mod::{ ComponentFn, Module };
use std::io;
use std::any::{ type_name, TypeId };
use std::fmt::{ self, Debug };
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
pub struct ModuleBuildContext<M: Module> {
resolved_components: ComponentMap,
component_fn_overrides: ComponentMap,
provider_overrides: ComponentMap,
parameters: ParameterMap,
submodules: M::Submodules,
resolve_chain: Vec<ResolveStep>,
router: Option<DmRouter>,
manifest_dir: PathBuf,
}
#[derive(PartialEq)]
struct ResolveStep {
component_type_name: &'static str,
component_type_id: TypeId,
interface_type_name: &'static str,
interface_type_id: TypeId,
}
impl Debug for ResolveStep {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.component_type_name)
}
}
impl<M: Module> ModuleBuildContext<M> {
pub(crate) fn new(
parameters: ParameterMap,
component_overrides: ComponentMap,
component_fn_overrides: ComponentMap,
provider_overrides: ComponentMap,
submodules: M::Submodules,
router: Option<DmRouter>,
manifest_dir: PathBuf
) -> Self {
ModuleBuildContext {
resolved_components: component_overrides,
component_fn_overrides,
provider_overrides,
parameters,
submodules,
resolve_chain: Vec::new(),
router,
manifest_dir,
}
}
pub fn submodules(&self) -> &M::Submodules {
&self.submodules
}
pub fn build_component<C: Component<M>>(&mut self) -> Arc<C::Interface> {
let comp = self.resolved_components
.get::<Arc<C::Interface>>()
.cloned()
.or_else(|| {
let component_fn = self.component_fn_overrides.remove::<
ComponentFn<M, C::Interface>
>()?;
self.add_resolve_step::<C>();
let component = component_fn(self);
let component = Arc::from(component);
self.resolved_components.insert::<Arc<C::Interface>>(Arc::clone(&component));
self.resolve_chain.pop();
Some(component)
})
.unwrap_or_else(|| {
self.add_resolve_step::<C>();
let parameters = self.parameters
.remove::<ComponentParameters<C, C::Parameters>>()
.unwrap_or_default();
let component = C::build(self, parameters.value);
let component = Arc::from(component);
self.resolved_components.insert::<Arc<C::Interface>>(Arc::clone(&component));
self.resolve_chain.pop();
component
});
if let Some(router) = &self.router {
router.push(comp.clone().route(Router::new()));
}
let _ = self
.output_manifest::<C>(comp.clone())
.inspect_err(|e| eprint!("failed to output manifest file, error={e}"));
comp
}
fn output_manifest<C: Component<M>>(&self, comp: Arc<C::Interface>) -> io::Result<()> {
fs::create_dir_all(self.manifest_dir.clone())?;
let mut f = fs::File::create(
self.manifest_dir.join(comp.manifest().name().to_owned() + ".xml")
)?;
f.write_all(r##"<?xml version="1.0" encoding="UTF-8"?>"##.as_bytes())?;
f.write(b"\n")?;
f.write_all(comp.as_ref().full_manifest().to_string_pretty().as_bytes())
}
pub fn provider_fn<P: Provider<M>>(&self) -> Arc<ProviderFn<M, P::Interface>>
where M: HasProvider<P::Interface>
{
self.provider_overrides
.get::<Arc<ProviderFn<M, P::Interface>>>()
.cloned()
.unwrap_or_else(|| Arc::new(Box::new(P::provide)))
}
fn add_resolve_step<C: Component<M>>(&mut self) {
let step = ResolveStep {
component_type_name: type_name::<C>(),
component_type_id: TypeId::of::<C>(),
interface_type_name: type_name::<C::Interface>(),
interface_type_id: TypeId::of::<C::Interface>(),
};
if self.resolve_chain.contains(&step) {
panic!(
"Circular dependency detected while resolving {}. Resolution chain: {:?}",
step.interface_type_name,
self.resolve_chain
);
}
self.resolve_chain.push(step);
}
}