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;
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,
}
fn sentinel_name() -> &'static str {
"__rustdv_component_sentinel"
}
fn sentinel_make() -> Box<dyn ComponentNode> {
panic!("the component-registry sentinel must never be built")
}
#[used]
#[cfg_attr(not(target_vendor = "apple"), link_section = "rustdv_comps")]
#[cfg_attr(target_vendor = "apple", link_section = "__DATA,rustdv_comps")]
static SENTINEL: &ComponentReg = &ComponentReg { name: sentinel_name, make: sentinel_make };
#[cfg(not(target_vendor = "apple"))]
extern "C" {
static __start_rustdv_comps: u8;
static __stop_rustdv_comps: u8;
}
#[cfg(target_vendor = "apple")]
extern "C" {
#[link_name = "\x01section$start$__DATA$rustdv_comps"]
static __start_rustdv_comps: u8;
#[link_name = "\x01section$end$__DATA$rustdv_comps"]
static __stop_rustdv_comps: u8;
}
fn collect_registry() -> HashMap<&'static str, Maker> {
std::hint::black_box(SENTINEL.name);
let mut map = HashMap::new();
unsafe {
let start = std::ptr::addr_of!(__start_rustdv_comps) as usize;
let stop = std::ptr::addr_of!(__stop_rustdv_comps) as usize;
let step = std::mem::size_of::<&ComponentReg>();
let base = start as *const &'static ComponentReg;
for i in 0..((stop - start) / step) {
let reg = *base.add(i);
let name = (reg.name)();
if name != sentinel_name() {
map.insert(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::{clear_seq_overrides, create_seq, set_seq_override, SeqCtx, SeqError, Sequence};
#[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>) {}
}