use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use crate::bus::{
AskQuery, ContractBody, DEFAULT_QUERY_TIMEOUT, Latest, OwnerCap, Publish, Publisher, Querier,
Subscribe, Subscriber, Topic,
};
use crate::participant::context::{SetupContext, ShutdownContext, StepContext};
use crate::participant::server::ServerOutcome;
use crate::participant::spec::{IsDriver, IsSimulator, IsTool, StepSchedule};
use phoxal_bus::Bus;
#[doc(hidden)]
pub mod __meta {
pub use const_format::concatcp as __concatcp;
pub const fn __bytes_of<const N: usize>(s: &str) -> [u8; N] {
let bytes = s.as_bytes();
assert!(
bytes.len() == N,
"phoxal: metadata manifest length mismatch"
);
let mut out = [0u8; N];
let mut i = 0;
while i < N {
out[i] = bytes[i];
i += 1;
}
out
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ContractRole {
Publish,
Subscribe,
Serve,
Ask,
}
#[derive(Clone, Copy, Debug)]
pub struct ApiContractUse {
pub topic: &'static str,
pub role: ContractRole,
}
pub trait ParticipantApi: Send + Sync + Clone + 'static {
const CONTRACTS: &'static [ApiContractUse];
}
impl ParticipantApi for () {
const CONTRACTS: &'static [ApiContractUse] = &[];
}
pub trait DeclaresPublish<B: ?Sized> {}
pub trait DeclaresSubscribe<B: ?Sized> {}
pub trait DeclaresAsk<Req: ?Sized, Resp: ?Sized> {}
pub trait DeclaresServe<Req: ?Sized, Resp: ?Sized> {}
pub trait ParticipantConfig: serde::de::DeserializeOwned + Send + 'static {
const SCHEMA_JSON: &'static str;
}
impl ParticipantConfig for () {
const SCHEMA_JSON: &'static str = "{}";
}
impl<T: ParticipantConfig> ParticipantConfig for Option<T> {
const SCHEMA_JSON: &'static str = T::SCHEMA_JSON;
}
pub trait Participant: Sized + Send + 'static {
const KIND: &'static str;
const PARTICIPANT_CLASS: &'static str;
const ID: &'static str;
type Config: ParticipantConfig;
type Api: ParticipantApi;
}
#[allow(async_fn_in_trait)]
pub trait ParticipantLifecycle: Participant {
const SERVER_CONTRACTS: &'static [ApiContractUse];
type Snapshot: Send + Sync + 'static;
const HAS_SNAPSHOT: bool;
fn __exclusive_server_topics() -> &'static [&'static str];
fn __snapshot_server_topics() -> &'static [&'static str];
fn __validate_server_topics() -> Result<(), String>;
fn __step_schedule() -> Option<StepSchedule>;
async fn __setup(
ctx: &mut SetupContext<Self>,
config: Self::Config,
) -> crate::Result<(Self, Self::Api)>;
async fn __step(&mut self, api: &mut Self::Api, step: StepContext) -> crate::Result<()>;
async fn __shutdown(&mut self, api: &mut Self::Api, ctx: ShutdownContext) -> crate::Result<()>;
fn __take_snapshot(&self) -> Self::Snapshot;
async fn __serve_exclusive(
&mut self,
api: &mut Self::Api,
topic: &str,
request: &[u8],
) -> ServerOutcome;
fn __serve_snapshot(
snapshot: Arc<Self::Snapshot>,
api: Arc<Self::Api>,
topic: String,
request: Vec<u8>,
) -> Pin<Box<dyn Future<Output = ServerOutcome> + Send>>;
}
pub struct Server<Req, Resp> {
_p: std::marker::PhantomData<fn(Req) -> Resp>,
}
impl<Req, Resp> Server<Req, Resp> {
#[doc(hidden)]
pub fn new() -> Self {
Server {
_p: std::marker::PhantomData,
}
}
}
impl<Req, Resp> Default for Server<Req, Resp> {
fn default() -> Self {
Self::new()
}
}
impl<Req, Resp> Clone for Server<Req, Resp> {
fn clone(&self) -> Self {
*self
}
}
impl<Req, Resp> Copy for Server<Req, Resp> {}
#[allow(async_fn_in_trait)]
pub trait SetupContextApiExt<R: Participant> {
async fn publisher<B: ContractBody>(
&self,
topic: Topic<Publish<B>>,
) -> crate::Result<Publisher<B>>
where
R::Api: DeclaresPublish<B>;
async fn latest<B: ContractBody>(&self, topic: Topic<Subscribe<B>>) -> crate::Result<Latest<B>>
where
R::Api: DeclaresSubscribe<B>;
async fn subscriber<B: ContractBody>(
&self,
topic: Topic<Subscribe<B>>,
depth: usize,
) -> crate::Result<Subscriber<B>>
where
R::Api: DeclaresSubscribe<B>;
async fn querier<Req: ContractBody, Resp: ContractBody>(
&self,
topic: Topic<AskQuery<Req, Resp>>,
) -> crate::Result<Querier<Req, Resp>>
where
R::Api: DeclaresAsk<Req, Resp>;
async fn server<Req: ContractBody, Resp: ContractBody>(
&self,
topic: Topic<AskQuery<Req, Resp>>,
) -> crate::Result<Server<Req, Resp>>
where
R::Api: DeclaresServe<Req, Resp>;
fn owner_capability(&self) -> OwnerCap;
fn robot(&self) -> crate::Result<&crate::model::v0::Robot>;
fn robot_root(&self) -> crate::Result<&std::path::Path>;
}
impl<R: Participant> SetupContextApiExt<R> for SetupContext<R> {
async fn publisher<B: ContractBody>(
&self,
topic: Topic<Publish<B>>,
) -> crate::Result<Publisher<B>>
where
R::Api: DeclaresPublish<B>,
{
Ok(Publisher::new(self.bus().clone(), &topic)?)
}
async fn latest<B: ContractBody>(&self, topic: Topic<Subscribe<B>>) -> crate::Result<Latest<B>>
where
R::Api: DeclaresSubscribe<B>,
{
Ok(Latest::new(self.bus(), &topic).await?)
}
async fn subscriber<B: ContractBody>(
&self,
topic: Topic<Subscribe<B>>,
depth: usize,
) -> crate::Result<Subscriber<B>>
where
R::Api: DeclaresSubscribe<B>,
{
Ok(Subscriber::new(self.bus(), &topic, depth).await?)
}
async fn querier<Req: ContractBody, Resp: ContractBody>(
&self,
topic: Topic<AskQuery<Req, Resp>>,
) -> crate::Result<Querier<Req, Resp>>
where
R::Api: DeclaresAsk<Req, Resp>,
{
Ok(Querier::new(
self.bus().clone(),
&topic,
DEFAULT_QUERY_TIMEOUT,
)?)
}
async fn server<Req: ContractBody, Resp: ContractBody>(
&self,
topic: Topic<AskQuery<Req, Resp>>,
) -> crate::Result<Server<Req, Resp>>
where
R::Api: DeclaresServe<Req, Resp>,
{
let _ = topic;
Ok(Server::new())
}
fn owner_capability(&self) -> OwnerCap {
self.owner_cap()
}
fn robot(&self) -> crate::Result<&crate::model::v0::Robot> {
self.robot_ref().ok_or_else(|| {
anyhow::anyhow!(
"no robot model is bound (this participant was launched without a robot root)"
)
})
}
fn robot_root(&self) -> crate::Result<&std::path::Path> {
self.robot_root_ref().ok_or_else(|| {
anyhow::anyhow!(
"no robot root is bound (this participant was launched without a robot root)"
)
})
}
}
pub trait SetupContextDriverExt {
fn component(&self) -> crate::Result<&str>;
}
impl<R: Participant + IsDriver> SetupContextDriverExt for SetupContext<R> {
fn component(&self) -> crate::Result<&str> {
self.component_instance().ok_or_else(|| {
anyhow::anyhow!("no component instance is bound (this driver was launched without one)")
})
}
}
pub trait SetupContextSimulatorExt {
fn component(&self) -> crate::Result<&str>;
}
impl<R: Participant + IsSimulator> SetupContextSimulatorExt for SetupContext<R> {
fn component(&self) -> crate::Result<&str> {
self.component_instance().ok_or_else(|| {
anyhow::anyhow!(
"no component instance is bound (this simulator was launched without one)"
)
})
}
}
pub trait SetupContextToolExt {
fn raw_bus(&self) -> Bus;
}
impl<R: Participant + IsTool> SetupContextToolExt for SetupContext<R> {
fn raw_bus(&self) -> Bus {
self.bus().clone()
}
}