use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use crate::component::{Component, ComponentNode, RustdvCtx};
use crate::config::ConfigDb;
use crate::port::PortOwner;
use linkme::distributed_slice;
pub type Maker = fn() -> Box<dyn ComponentNode>;
#[derive(Default)]
pub struct RustdvComp {
inner: Option<Box<dyn ComponentNode>>,
overridable: bool,
requested: Option<&'static str>,
}
impl RustdvComp {
pub fn fixed(node: Box<dyn ComponentNode>) -> RustdvComp {
RustdvComp {
inner: Some(node),
overridable: false,
requested: None,
}
}
pub fn overridable(node: Box<dyn ComponentNode>, requested: &'static str) -> RustdvComp {
RustdvComp {
inner: Some(node),
overridable: true,
requested: Some(requested),
}
}
pub fn as_node(&self) -> Option<&(dyn ComponentNode + 'static)> {
self.inner.as_deref()
}
pub fn as_node_mut(&mut self) -> Option<&mut (dyn ComponentNode + 'static)> {
self.inner.as_deref_mut()
}
pub fn take_node(&mut self) -> Option<Box<dyn ComponentNode>> {
self.inner.take()
}
pub fn put_node(&mut self, node: Box<dyn ComponentNode>) {
self.inner = Some(node);
}
pub fn resolve(&mut self, ctx: &RustdvCtx, name: &str) {
if !self.overridable {
return;
}
let Some(req) = self.requested else { return };
let path = if ctx.path().is_empty() {
name.to_string()
} else {
format!("{}.{}", ctx.path(), name)
};
if let Some(ov) = Factory::lookup_override(req, &path) {
self.inner = Some((ov.make)());
}
self.overridable = false;
}
}
impl PortOwner for RustdvComp {
fn owner_port_slot(&self, name: &str) -> Option<std::rc::Rc<dyn std::any::Any>> {
self.as_node()?.port_slot(name)
}
fn owner_label(&self) -> &'static str {
match self.as_node() {
Some(n) => n.node_name(),
None => "an unbuilt child slot",
}
}
}
#[derive(Clone, Copy)]
pub struct Override {
make: Maker,
to: &'static str,
}
impl fmt::Debug for Override {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "-> {}", self.to)
}
}
fn override_key(requested_name: &str) -> String {
format!("__factory_override__{requested_name}")
}
pub struct ComponentReg {
pub name: fn() -> &'static str,
pub make: Maker,
}
#[doc(hidden)]
#[distributed_slice]
pub static COMPONENT_REGISTRATIONS: [ComponentReg];
fn collect_registry() -> HashMap<&'static str, Maker> {
let mut map = HashMap::new();
for reg in COMPONENT_REGISTRATIONS {
map.insert((reg.name)(), reg.make);
}
map
}
thread_local! {
static REGISTRY: RefCell<Option<HashMap<&'static str, Maker>>> = const { RefCell::new(None) };
}
fn with_registry<R>(f: impl FnOnce(&HashMap<&'static str, Maker>) -> R) -> R {
REGISTRY.with(|r| {
let mut slot = r.borrow_mut();
if slot.is_none() {
*slot = Some(collect_registry());
}
f(slot.as_ref().unwrap())
})
}
pub struct Factory;
impl Factory {
pub fn create_by_name(name: &str) -> RustdvComp {
let make = with_registry(|reg| reg.get(name).copied());
match make {
Some(make) => {
let stored = with_registry(|reg| reg.keys().find(|k| **k == name).copied());
RustdvComp::overridable(make(), stored.expect("just found it"))
}
None => panic!("Factory::create_by_name: no component registered as \"{name}\""),
}
}
pub fn set_type_override<From, To>()
where
From: Component + ComponentNode + Default + 'static,
To: Component + ComponentNode + Default + 'static,
{
Self::store_override(None, "*", From::comp_name(), To::comp_name(), || {
Box::new(To::default())
});
}
pub fn set_type_override_by_name(from: &str, to: &str) {
let make = with_registry(|reg| reg.get(to).copied()).unwrap_or_else(|| {
panic!("Factory::set_type_override_by_name: \"{to}\" is not registered")
});
let to_static =
with_registry(|reg| reg.keys().find(|k| **k == to).copied()).expect("just found it");
Self::store_override(None, "*", from, to_static, make);
}
pub fn set_inst_override<From, To>(ctx: &RustdvCtx, path: &str)
where
From: Component + ComponentNode + Default + 'static,
To: Component + ComponentNode + Default + 'static,
{
Self::store_override(Some(ctx), path, From::comp_name(), To::comp_name(), || {
Box::new(To::default())
});
}
fn store_override(
ctx: Option<&RustdvCtx>,
offset: &str,
from_name: &str,
to_name: &'static str,
make: Maker,
) {
ConfigDb::set(
ctx,
offset,
&override_key(from_name),
Override { make, to: to_name },
);
}
pub(crate) fn lookup_override(requested_name: &str, abs_path: &str) -> Option<Override> {
ConfigDb::get::<Override>(None, abs_path, &override_key(requested_name)).ok()
}
pub fn print() {
rustdv_sim::log::info("Factory overrides:");
for (path, from, to) in ConfigDb::factory_overrides() {
rustdv_sim::log::info(&format!(" {path:<28}: {from} -> {to}"));
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::component::{Component, RustdvCtx};
use crate::config::ConfigDb;
use std::cell::RefCell;
use std::rc::Rc;
thread_local! {
static BUILT: RefCell<Vec<&'static str>> = const { RefCell::new(Vec::new()) };
}
fn record(name: &'static str) {
BUILT.with(|b| b.borrow_mut().push(name));
}
#[derive(Default)]
struct Base;
impl Component for Base {
fn build(&mut self, _ctx: &mut RustdvCtx) {
record("Base");
}
}
impl ComponentNode for Base {
fn node_name(&self) -> &'static str {
"Base"
}
fn children_mut(&mut self) -> Vec<(String, &mut (dyn ComponentNode + 'static))> {
Vec::new()
}
}
#[derive(Default)]
struct Derived;
impl Component for Derived {
fn build(&mut self, _ctx: &mut RustdvCtx) {
record("Derived");
}
}
impl ComponentNode for Derived {
fn node_name(&self) -> &'static str {
"Derived"
}
fn children_mut(&mut self) -> Vec<(String, &mut (dyn ComponentNode + 'static))> {
Vec::new()
}
}
fn fresh() {
ConfigDb::clear();
BUILT.with(|b| b.borrow_mut().clear());
}
#[test]
fn a_fixed_slot_holds_what_it_was_given() {
fresh();
let slot = RustdvComp::fixed(Box::new(Base));
assert_eq!(slot.as_node().unwrap().node_name(), "Base");
}
#[test]
fn an_empty_slot_reports_itself_by_name() {
fresh();
let slot = RustdvComp::default();
assert!(slot.as_node().is_none());
assert_eq!(slot.owner_label(), "an unbuilt child slot");
}
#[test]
fn a_type_override_swaps_a_create_slot_and_not_a_new_slot() {
fresh();
Factory::set_type_override::<Base, Derived>();
let ctx = RustdvCtx::for_test("env");
let mut overridable = RustdvComp::overridable(Box::new(Base), "Base");
overridable.resolve(&ctx, "tester");
assert_eq!(overridable.as_node().unwrap().node_name(), "Derived");
let mut fixed = RustdvComp::fixed(Box::new(Base));
fixed.resolve(&ctx, "scoreboard");
assert_eq!(
fixed.as_node().unwrap().node_name(),
"Base",
"new_comp is never swapped"
);
}
#[test]
fn no_override_leaves_the_requested_type() {
fresh();
let ctx = RustdvCtx::for_test("env");
let mut slot = RustdvComp::overridable(Box::new(Base), "Base");
slot.resolve(&ctx, "tester");
assert_eq!(slot.as_node().unwrap().node_name(), "Base");
}
#[test]
fn an_instance_override_beats_a_type_override() {
fresh();
let ctx = RustdvCtx::for_test("env");
Factory::set_type_override::<Base, Base>();
Factory::set_inst_override::<Base, Derived>(&ctx, "tester");
let mut slot = RustdvComp::overridable(Box::new(Base), "Base");
slot.resolve(&ctx, "tester");
assert_eq!(slot.as_node().unwrap().node_name(), "Derived");
}
#[test]
fn an_instance_override_applies_only_at_its_path() {
fresh();
let ctx = RustdvCtx::for_test("env");
Factory::set_inst_override::<Base, Derived>(&ctx, "tester");
let mut here = RustdvComp::overridable(Box::new(Base), "Base");
here.resolve(&ctx, "tester");
assert_eq!(here.as_node().unwrap().node_name(), "Derived");
let mut elsewhere = RustdvComp::overridable(Box::new(Base), "Base");
elsewhere.resolve(&ctx, "other");
assert_eq!(elsewhere.as_node().unwrap().node_name(), "Base");
}
#[test]
fn the_discarded_default_never_built() {
fresh();
Factory::set_type_override::<Base, Derived>();
let ctx = RustdvCtx::for_test("env");
let mut slot = RustdvComp::overridable(Box::new(Base), "Base");
slot.resolve(&ctx, "tester");
assert_eq!(BUILT.with(|b| b.borrow().len()), 0);
assert_eq!(slot.as_node().unwrap().node_name(), "Derived");
}
#[test]
fn a_slot_resolves_once() {
fresh();
let ctx = RustdvCtx::for_test("env");
let mut slot = RustdvComp::overridable(Box::new(Base), "Base");
slot.resolve(&ctx, "tester");
Factory::set_type_override::<Base, Derived>(); slot.resolve(&ctx, "tester");
assert_eq!(
slot.as_node().unwrap().node_name(),
"Base",
"an override installed after the walk passed does not apply"
);
}
#[test]
fn take_and_put_move_the_box_out_and_back() {
fresh();
let mut slot = RustdvComp::fixed(Box::new(Base));
let node = slot.take_node().expect("something to take");
assert!(
slot.as_node().is_none(),
"the slot is empty during the run phase"
);
slot.put_node(node);
assert_eq!(
slot.as_node().unwrap().node_name(),
"Base",
"and restored after"
);
}
use crate::sequence::{
SeqCtx, SeqError, Sequence, clear_seq_overrides, create_seq, set_seq_override,
};
#[derive(Default)]
struct BaseSeq;
#[derive(Default)]
struct RandomSeq;
impl Sequence for BaseSeq {
type Req = u8;
type Rsp = u8;
async fn body(&mut self, _c: &mut SeqCtx<u8, u8>) -> Result<(), SeqError> {
Ok(())
}
fn seq_name(&self) -> &'static str {
"BaseSeq"
}
}
impl Sequence for RandomSeq {
type Req = u8;
type Rsp = u8;
async fn body(&mut self, _c: &mut SeqCtx<u8, u8>) -> Result<(), SeqError> {
Ok(())
}
fn seq_name(&self) -> &'static str {
"RandomSeq"
}
}
#[test]
fn create_seq_builds_the_requested_type_by_default() {
clear_seq_overrides();
let seq = create_seq::<BaseSeq>();
assert_eq!(seq.name(), "BaseSeq");
}
#[test]
fn a_sequence_override_swaps_the_type() {
clear_seq_overrides();
set_seq_override::<BaseSeq, RandomSeq>();
let seq = create_seq::<BaseSeq>();
assert_eq!(
seq.name(),
"RandomSeq",
"the test asked for Base and got Random"
);
}
#[test]
fn clearing_sequence_overrides_restores_the_default() {
clear_seq_overrides();
set_seq_override::<BaseSeq, RandomSeq>();
clear_seq_overrides();
assert_eq!(create_seq::<BaseSeq>().name(), "BaseSeq");
}
#[test]
fn an_override_on_one_sequence_leaves_others_alone() {
clear_seq_overrides();
set_seq_override::<BaseSeq, RandomSeq>();
assert_eq!(create_seq::<RandomSeq>().name(), "RandomSeq");
}
#[allow(dead_code)]
fn _rc_in_scope(_: Rc<u8>) {}
}