use std::any::Any;
use std::cell::RefCell;
use std::fmt;
use std::future::Future;
use std::marker::PhantomData;
use std::pin::Pin;
use std::rc::Rc;
pub trait PutIf<T: 'static>: 'static {
fn put(&self, item: T) -> Pin<Box<dyn Future<Output = ()> + '_>>;
fn try_put(&self, item: T) -> Result<(), T>;
fn can_put(&self) -> bool;
}
pub trait GetIf<T: 'static>: 'static {
fn get(&self) -> Pin<Box<dyn Future<Output = T> + '_>>;
fn try_get(&self) -> Option<T>;
fn can_get(&self) -> bool;
}
pub trait PeekIf<T: 'static>: 'static {
fn peek(&self) -> Pin<Box<dyn Future<Output = T> + '_>>;
fn try_peek(&self) -> Option<T>;
fn can_peek(&self) -> bool;
}
pub trait Subscriber<T>: 'static {
fn write(&mut self, item: &T);
}
pub trait SinkHandle<T: 'static>: 'static {
fn deliver(&self, item: &T);
}
impl<T: 'static, S: Subscriber<T>> SinkHandle<T> for crate::shared::RustdvShared<S> {
fn deliver(&self, item: &T) {
self.get_mut().write(item);
}
}
pub trait PublishIf<T: 'static>: 'static {
fn write(&self, item: &T);
}
pub struct PortName<I: ?Sized> {
name: &'static str,
_marker: PhantomData<fn(&I)>,
}
impl<I: ?Sized> PortName<I> {
pub const fn new(name: &'static str) -> PortName<I> {
PortName { name, _marker: PhantomData }
}
pub const fn as_str(&self) -> &'static str {
self.name
}
}
impl<I: ?Sized> Clone for PortName<I> {
fn clone(&self) -> Self {
*self
}
}
impl<I: ?Sized> Copy for PortName<I> {}
impl<I: ?Sized> fmt::Debug for PortName<I> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PortName({})", self.name)
}
}
pub struct Port<I: ?Sized + 'static> {
slot: Rc<RefCell<Option<Rc<I>>>>,
}
pub type PutPort<T> = Port<dyn PutIf<T>>;
pub type GetPort<T> = Port<dyn GetIf<T>>;
pub type PeekPort<T> = Port<dyn PeekIf<T>>;
pub type PublishPort<T> = Port<dyn PublishIf<T>>;
pub type SubscribePort<T> = Port<dyn SinkHandle<T>>;
impl<I: ?Sized + 'static> Clone for Port<I> {
fn clone(&self) -> Self {
Port { slot: self.slot.clone() }
}
}
impl<I: ?Sized + 'static> Default for Port<I> {
fn default() -> Self {
Port { slot: Rc::new(RefCell::new(None)) }
}
}
impl<I: ?Sized + 'static> Port<I> {
pub fn new() -> Port<I> {
Port::default()
}
pub fn is_bound(&self) -> bool {
self.slot.borrow().is_some()
}
fn iface(&self) -> Rc<I> {
match self.slot.borrow().as_ref() {
Some(i) => i.clone(),
None => panic!(
"a TLM port was used before it was connected — connect it in \
the parent's connect phase with \
`fifo.<kind>_export().connect(owner, Owner::PORT_NAME)`"
),
}
}
}
impl<T: 'static> PutPort<T> {
pub async fn put(&self, item: T) {
let iface = self.iface();
iface.put(item).await
}
pub fn try_put(&self, item: T) -> Result<(), T> {
self.iface().try_put(item)
}
pub fn can_put(&self) -> bool {
self.iface().can_put()
}
}
impl<T: 'static> GetPort<T> {
pub async fn get(&self) -> T {
let iface = self.iface();
iface.get().await
}
pub fn try_get(&self) -> Option<T> {
self.iface().try_get()
}
pub fn can_get(&self) -> bool {
self.iface().can_get()
}
}
impl<T: 'static> PeekPort<T> {
pub async fn peek(&self) -> T {
let iface = self.iface();
iface.peek().await
}
pub fn try_peek(&self) -> Option<T> {
self.iface().try_peek()
}
pub fn can_peek(&self) -> bool {
self.iface().can_peek()
}
}
impl<T: 'static> PublishPort<T> {
pub fn write(&self, item: &T) {
let iface = self.slot.borrow().as_ref().cloned();
if let Some(iface) = iface {
iface.write(item);
}
}
pub fn has_subscribers(&self) -> bool {
self.is_bound()
}
}
impl<T: 'static> SubscribePort<T> {
pub fn subscribe<S: Subscriber<T>>(&self, subscriber: crate::shared::RustdvShared<S>) {
*self.slot.borrow_mut() = Some(Rc::new(subscriber));
}
pub fn subscriber(&self) -> Option<Rc<dyn SinkHandle<T>>> {
self.slot.borrow().clone()
}
}
pub(crate) fn sink_of<T: 'static>(
owner: &dyn PortOwner,
name: PortName<dyn SinkHandle<T>>,
) -> Result<Rc<dyn SinkHandle<T>>, ConnectError> {
let label = owner.owner_label();
let slot = owner
.owner_port_slot(name.as_str())
.ok_or(ConnectError::NoSuchPort { owner: label, name: name.as_str() })?;
let slot = slot
.downcast::<RefCell<Option<Rc<dyn SinkHandle<T>>>>>()
.map_err(|_| ConnectError::WrongInterface { owner: label, name: name.as_str() })?;
let sink = slot.borrow().clone();
sink.ok_or(ConnectError::NoSubscriber { owner: label, name: name.as_str() })
}
impl<REQ: 'static, RSP: 'static> Port<dyn crate::sequence::SeqItemIf<REQ, RSP>> {
pub async fn get_next_item(&self) -> crate::sequence::SeqItem<REQ> {
let iface = self.iface();
iface.get_next_item().await
}
pub fn try_next_item(&self) -> Option<crate::sequence::SeqItem<REQ>> {
self.iface().try_next_item()
}
pub fn item_done(&self, rsp: Option<RSP>) {
self.iface().item_done(rsp)
}
pub fn put_response(&self, id: crate::sequence::TxnId, rsp: RSP) {
self.iface().put_response(id, rsp)
}
pub fn bind_iface(&self, iface: Rc<dyn crate::sequence::SeqItemIf<REQ, RSP>>) {
*self.slot.borrow_mut() = Some(iface);
}
}
pub trait PortField {
type Iface: ?Sized + 'static;
fn slot_any(&self) -> Rc<dyn Any>;
fn bound(&self) -> bool;
}
impl<I: ?Sized + 'static> PortField for Port<I> {
type Iface = I;
fn slot_any(&self) -> Rc<dyn Any> {
self.slot.clone()
}
fn bound(&self) -> bool {
self.is_bound()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PortInfo {
pub name: &'static str,
pub kind: &'static str,
pub required: bool,
pub connected: bool,
}
pub trait PortOwner {
fn owner_port_slot(&self, name: &str) -> Option<Rc<dyn Any>>;
fn owner_label(&self) -> &'static str;
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ConnectError {
NoSuchPort { owner: &'static str, name: &'static str },
WrongInterface { owner: &'static str, name: &'static str },
NoSubscriber { owner: &'static str, name: &'static str },
}
impl fmt::Display for ConnectError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ConnectError::NoSuchPort { owner, name } => write!(
f,
"connect: {owner} has no port named '{name}' \
(is the field marked #[port(..)]? is the child slot built?)"
),
ConnectError::WrongInterface { owner, name } => write!(
f,
"connect: {owner}'s port '{name}' wants a different interface \
(put/get/peek mismatch, or a different transaction type)"
),
ConnectError::NoSubscriber { owner, name } => write!(
f,
"connect: {owner}'s subscribe port '{name}' has no subscriber — call \
`self.{name}.subscribe(handle)` in {owner}'s build phase"
),
}
}
}
pub fn bind<I: ?Sized + 'static>(
owner: &dyn PortOwner,
name: PortName<I>,
iface: Rc<I>,
) -> Result<(), ConnectError> {
let label = owner.owner_label();
let slot = owner
.owner_port_slot(name.as_str())
.ok_or(ConnectError::NoSuchPort { owner: label, name: name.as_str() })?;
let slot = slot
.downcast::<RefCell<Option<Rc<I>>>>()
.map_err(|_| ConnectError::WrongInterface { owner: label, name: name.as_str() })?;
*slot.borrow_mut() = Some(iface);
Ok(())
}
pub(crate) fn bind_or_panic<I: ?Sized + 'static>(
owner: &dyn PortOwner,
name: PortName<I>,
iface: Rc<I>,
) {
if let Err(e) = bind(owner, name, iface) {
panic!("{e}");
}
}