use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use schemars::JsonSchema;
use serde::Serialize;
use crate::bus::ApiVersion;
use crate::participant::context::{SetupContext, ShutdownContext, StepContext};
use crate::participant::server::ServerOutcome;
#[derive(Clone, Copy, Debug, Serialize)]
pub struct ContractUse {
pub api_version: &'static str,
pub schema_id: &'static str,
pub family: &'static str,
}
pub trait ParticipantSpec: Sized + Send + 'static {
const KIND: &'static str;
const PARTICIPANT_CLASS: &'static str;
const ID: &'static str;
type Api: ApiVersion;
const API_VERSION: &'static str;
type Config: serde::de::DeserializeOwned + JsonSchema + Send + 'static;
const FIELD_CONTRACTS: &'static [ContractUse];
}
#[diagnostic::on_unimplemented(
message = "`{Self}` is a `Tool`, which is a thin raw-bus runner and has no typed-graph surface",
label = "`#[step]` / `#[server]` is not allowed here; use the raw bus (`phoxal::raw`) instead"
)]
pub trait TypedGraphSurface {}
#[doc(hidden)]
pub trait IsDriver {}
#[doc(hidden)]
pub trait IsTool {}
#[doc(hidden)]
pub trait IsSimulator {}
#[allow(async_fn_in_trait)]
pub trait ParticipantBehavior: ParticipantSpec {
const SERVER_CONTRACTS: &'static [ContractUse];
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() -> std::result::Result<(), String>;
fn __step_schedule() -> Option<StepSchedule>;
async fn __setup(ctx: &mut SetupContext<Self>, config: Self::Config) -> crate::Result<Self>;
async fn __step(&mut self, step: StepContext) -> crate::Result<()>;
async fn __shutdown(&mut self, ctx: ShutdownContext) -> crate::Result<()>;
fn __take_snapshot(&self) -> Self::Snapshot;
async fn __serve_exclusive(
&mut self,
topic: &str,
api_version: &str,
schema_id: &str,
family: &str,
request: &[u8],
) -> ServerOutcome;
fn __serve_snapshot(
snapshot: Arc<Self::Snapshot>,
topic: String,
api_version: String,
schema_id: String,
family: String,
request: Vec<u8>,
) -> Pin<Box<dyn Future<Output = ServerOutcome> + Send>>;
}
pub trait DeclaresPublish<B: ?Sized> {}
pub trait DeclaresSubscribe<B: ?Sized> {}
pub trait DeclaresQuery<Req: ?Sized, Resp: ?Sized> {}
#[derive(Clone, Copy, Debug)]
pub struct StepSchedule {
pub hz: f64,
pub missed_tick: MissedTick,
}
impl StepSchedule {
pub const fn hz(hz: f64) -> Self {
StepSchedule {
hz,
missed_tick: MissedTick::Collapse,
}
}
pub fn period(&self) -> Duration {
std::cmp::max(
Duration::from_secs_f64(1.0 / self.hz),
Duration::from_nanos(1),
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MissedTick {
Collapse,
CatchUp,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn step_schedule_period_never_rounds_to_zero() {
assert_eq!(StepSchedule::hz(f64::MAX).period(), Duration::from_nanos(1));
}
}