odem-rs-core 0.1.0

Core components of the ODEM-rs simulation framework
//! Module for basic scheduling operations implemented in a space-optimized way.

use core::{
	cmp::Ordering::*,
	fmt::Display,
	future::Future,
	ops::Add,
	pin::Pin,
	task::{Context, Poll},
};

use crate::{config::Config, error::CausalityError, fsm::*};

use super::Sim;

/* *********************************************************** Try Advance To */

/// Structure that serves as a future that attempts to schedule the calling
/// continuation at a later, fixed model-time.
pub(super) struct TryAdvanceTo<'s, C: ?Sized + Config> {
	/// The simulation context. Doubles as a state since it is extracted after
	/// the first call.
	sim: Option<&'s Sim<C>>,
	/// The fixed model-time to schedule the calling continuation for.
	when: C::Time,
}

impl<'s, C: ?Sized + Config> TryAdvanceTo<'s, C> {
	/// Instantiates the structure.
	pub(super) const fn new(sim: &'s Sim<C>, when: C::Time) -> Self {
		TryAdvanceTo {
			sim: Some(sim),
			when,
		}
	}

	/// Unwraps the future, panicking and printing an error message if
	/// unsuccessful and returning the unwrapped result otherwise.
	pub(super) const fn unwrap(self) -> Unwrap<Self> {
		Unwrap(self)
	}
}

impl<C: ?Sized + Config> Future for TryAdvanceTo<'_, C> {
	type Output = Result<(), CausalityError<C::Time>>;

	#[inline]
	fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
		if let Some(sim) = self.sim.take() {
			advance_impl(sim, sim.now(), self.when)
		} else {
			Poll::Ready(Ok(()))
		}
	}
}

/* ************************************************************** Try Advance */

/// Structure that serves as a future that attempts to schedule the calling
/// continuation at a later, relative model time.
pub(super) struct TryAdvance<'s, C: ?Sized + Config> {
	/// The simulation context. Doubles as a state since it is extracted after
	/// the first call.
	sim: Option<&'s Sim<C>>,
	/// A model-time relative to the binding time to schedule the calling continuation
	/// for.
	dt: C::Time,
}

impl<'s, C: ?Sized + Config> TryAdvance<'s, C> {
	/// Instantiates the structure.
	pub(super) const fn new(sim: &'s Sim<C>, dt: C::Time) -> Self {
		TryAdvance { sim: Some(sim), dt }
	}

	/// Unwraps the future, panicking and printing an error message if
	/// unsuccessful and returning the unwrapped result otherwise.
	pub(super) const fn unwrap(self) -> Unwrap<Self> {
		Unwrap(self)
	}
}

impl<C: ?Sized + Config> Future for TryAdvance<'_, C>
where
	C::Time: Add<Output = C::Time>,
{
	type Output = Result<(), CausalityError<C::Time>>;

	#[inline]
	fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
		if let Some(sim) = self.sim.take() {
			advance_impl(sim, sim.now(), sim.now() + self.dt)
		} else {
			Poll::Ready(Ok(()))
		}
	}
}

/* ******************************************************************** Defer */

/// Structure that serves as a future that can be awaited in order to defer
/// execution for the currently active continuation.
pub(super) struct Defer<'s, C: ?Sized + Config> {
	/// A simulation context before polling or `None` after the first poll.
	sim: Option<&'s Sim<C>>,
}

impl<'s, C: ?Sized + Config> Defer<'s, C> {
	/// Initializes a new defer-future.
	pub(super) const fn new(sim: &'s Sim<C>) -> Self {
		Defer { sim: Some(sim) }
	}
}

impl<C: ?Sized + Config> Future for Defer<'_, C> {
	type Output = ();

	fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
		match self.sim.take() {
			Some(sim) => {
				if let Some(active) = sim.active.take() {
					active.into_inner().brand(|active, once| {
						let busy = active
							.token(once)
							.into_busy()
							.expect("active continuation should be in state 'Busy'");
						sim.calendar().defer(active.clone(), busy);
					});
				}
				Poll::Pending
			}
			None => Poll::Ready(()),
		}
	}
}

/* ***************************************************************** Adapters */

/// Future adapter that attempts to unwrap the inner future, panicking if it
/// resolves into an `Err`-result.
#[must_use = "Futures must be awaited in order to execute them"]
#[pin_project::pin_project]
pub(super) struct Unwrap<F: ?Sized>(#[pin] F);

impl<T, E: Display, F: ?Sized + Future<Output = Result<T, E>>> Future for Unwrap<F> {
	type Output = T;

	#[inline]
	fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
		match self.project().0.poll(cx) {
			Poll::Ready(Ok(res)) => Poll::Ready(res),
			Poll::Ready(Err(err)) => panic!("{}", err),
			Poll::Pending => Poll::Pending,
		}
	}
}

/* *************************************************** Implementation Details */

/// Implementation helper for the poll functions of `TryAdvance` and
/// `TryAdvanceTo`.
fn advance_impl<C: ?Sized + Config>(
	sim: &Sim<C>,
	now: C::Time,
	when: C::Time,
) -> Poll<Result<(), CausalityError<C::Time>>> {
	match when.partial_cmp(&now) {
		None | Some(Less) => Poll::Ready(Err(CausalityError {
			cause: now,
			effect: when,
		})),
		Some(ord) => {
			let calendar = sim.calendar();
			let task = sim
				.active
				.take()
				.expect("no active continuation")
				.into_inner();

			task.brand(|task, once| {
				let busy = task
					.token(once)
					.into_busy()
					.expect("active continuation should be in state 'Busy'");

				let idle = task.state().transition(busy, ());

				if ord == Greater {
					calendar.schedule(task.clone(), idle, when);
				} else {
					calendar.activate(task.clone(), idle);
				}
			});

			Poll::Pending
		}
	}
}