use alloc::boxed::Box;
use core::{
cell::UnsafeCell,
future::{Future, IntoFuture},
marker::{PhantomData, PhantomPinned},
ops::ControlFlow,
pin::Pin,
task::{self, Poll},
};
use bitflags::bitflags;
use futures_core::Stream;
use pin_project::pin_project;
use vex_sdk::vexCompetitionStatus;
bitflags! {
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct CompetitionStatus: u32 {
const DISABLED = 1 << 0;
const AUTONOMOUS = 1 << 1;
const CONNECTED = 1 << 2;
const SYSTEM = 1 << 3;
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompetitionMode {
Disabled,
Autonomous,
Driver,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompetitionSystem {
FieldControl,
CompetitionSwitch,
}
impl CompetitionStatus {
#[must_use]
pub const fn is_connected(&self) -> bool {
self.contains(CompetitionStatus::CONNECTED)
}
#[must_use]
pub const fn mode(&self) -> CompetitionMode {
if self.contains(Self::DISABLED) {
CompetitionMode::Disabled
} else if self.contains(Self::AUTONOMOUS) {
CompetitionMode::Autonomous
} else {
CompetitionMode::Driver
}
}
#[must_use]
pub const fn system(&self) -> Option<CompetitionSystem> {
if self.contains(CompetitionStatus::CONNECTED) {
if self.contains(Self::SYSTEM) {
Some(CompetitionSystem::FieldControl)
} else {
Some(CompetitionSystem::CompetitionSwitch)
}
} else {
None
}
}
}
#[must_use]
pub fn status() -> CompetitionStatus {
CompetitionStatus::from_bits_retain(unsafe { vexCompetitionStatus() })
}
#[must_use]
pub fn is_connected() -> bool {
status().is_connected()
}
#[must_use]
pub fn system() -> Option<CompetitionSystem> {
status().system()
}
#[must_use]
pub fn mode() -> CompetitionMode {
status().mode()
}
pub struct CompetitionUpdates {
last_status: Option<CompetitionStatus>,
}
impl Stream for CompetitionUpdates {
type Item = CompetitionStatus;
fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
let current = status();
cx.waker().wake_by_ref();
if self.last_status == Some(current) {
Poll::Pending
} else {
self.get_mut().last_status = Some(current);
Poll::Ready(Some(current))
}
}
}
impl CompetitionUpdates {
pub fn last(&self) -> CompetitionStatus {
self.last_status.unwrap_or_else(status)
}
}
#[must_use]
pub const fn updates() -> CompetitionUpdates {
CompetitionUpdates { last_status: None }
}
#[pin_project]
pub struct CompetitionRuntime<
Shared: 'static,
Return,
MkConnected,
MkDisconnected,
MkDisabled,
MkAutonomous,
MkDriver,
> where
MkConnected:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDisconnected:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDisabled:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkAutonomous:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDriver:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
{
mk_connected: MkConnected,
mk_disconnected: MkDisconnected,
mk_disabled: MkDisabled,
mk_autonomous: MkAutonomous,
mk_driver: MkDriver,
#[pin]
updates: CompetitionUpdates,
status: CompetitionStatus,
phase: CompetitionRuntimePhase,
#[allow(clippy::type_complexity)]
task: Option<Pin<Box<dyn Future<Output = ControlFlow<Return>> + 'static>>>,
shared: UnsafeCell<Shared>,
_pin: PhantomPinned,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum CompetitionRuntimePhase {
Initial,
Disconnected,
Connected,
Mode(CompetitionMode),
}
impl<Shared, Return, MkConnected, MkDisconnected, MkDisabled, MkAutonomous, MkDriver> Future
for CompetitionRuntime<
Shared,
Return,
MkConnected,
MkDisconnected,
MkDisabled,
MkAutonomous,
MkDriver,
>
where
MkConnected:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDisconnected:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDisabled:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkAutonomous:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDriver:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
{
type Output = Return;
fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
let mut this = self.as_mut().project();
let old_phase = *this.phase;
match this.updates.as_mut().poll_next(cx) {
Poll::Ready(Some(new_status)) => {
let old_status = *this.status;
if *this.phase != CompetitionRuntimePhase::Connected
&& *this.phase != CompetitionRuntimePhase::Disconnected
{
*this.phase = if !old_status.is_connected() && new_status.is_connected() {
CompetitionRuntimePhase::Connected
} else if old_status.is_connected() && !new_status.is_connected() {
CompetitionRuntimePhase::Disconnected
} else {
CompetitionRuntimePhase::Mode(new_status.mode())
};
}
*this.status = new_status;
}
Poll::Ready(None) => unreachable!(),
_ => {}
}
if let Some(Poll::Ready(res)) = this.task.as_mut().map(|task| task.as_mut().poll(cx)) {
if let ControlFlow::Break(val) = res {
return Poll::Ready(val);
}
*this.task = None;
match *this.phase {
CompetitionRuntimePhase::Connected | CompetitionRuntimePhase::Disconnected => {
*this.phase = CompetitionRuntimePhase::Mode(this.updates.last().mode());
}
_ => {}
}
}
if old_phase != *this.phase {
drop(this.task.take());
let shared = unsafe { &mut *this.shared.get() };
*this.task = match *this.phase {
CompetitionRuntimePhase::Initial => None,
CompetitionRuntimePhase::Disconnected => Some((this.mk_disconnected)(shared)),
CompetitionRuntimePhase::Connected => Some((this.mk_connected)(shared)),
CompetitionRuntimePhase::Mode(CompetitionMode::Disabled) => {
Some((this.mk_disabled)(shared))
}
CompetitionRuntimePhase::Mode(CompetitionMode::Autonomous) => {
Some((this.mk_autonomous)(shared))
}
CompetitionRuntimePhase::Mode(CompetitionMode::Driver) => {
Some((this.mk_driver)(shared))
}
};
}
Poll::Pending
}
}
impl<Shared, Return>
CompetitionRuntime<
Shared,
Return,
DefaultMk<Shared, Return>,
DefaultMk<Shared, Return>,
DefaultMk<Shared, Return>,
DefaultMk<Shared, Return>,
DefaultMk<Shared, Return>,
>
{
pub const fn builder(shared: Shared) -> CompetitionBuilder<Shared, Return> {
fn default_mk<Shared, Return>(
_: &mut Shared,
) -> Pin<Box<dyn Future<Output = ControlFlow<Return>>>> {
Box::pin(async { ControlFlow::Continue(()) })
}
CompetitionBuilder {
shared,
mk_connected: default_mk,
mk_disconnected: default_mk,
mk_disabled: default_mk,
mk_autonomous: default_mk,
mk_driver: default_mk,
_return: PhantomData,
}
}
}
type DefaultMk<Shared, Return> =
for<'t> fn(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>;
pub struct CompetitionBuilder<
Shared,
Return,
MkConnected = DefaultMk<Shared, Return>,
MkDisconnected = DefaultMk<Shared, Return>,
MkDisabled = DefaultMk<Shared, Return>,
MkAutonomous = DefaultMk<Shared, Return>,
MkDriver = DefaultMk<Shared, Return>,
> {
shared: Shared,
mk_connected: MkConnected,
mk_disconnected: MkDisconnected,
mk_disabled: MkDisabled,
mk_autonomous: MkAutonomous,
mk_driver: MkDriver,
_return: PhantomData<fn(Return) -> Return>,
}
impl<Shared, Return, MkConnected, MkDisconnected, MkDisabled, MkAutonomous, MkDriver>
CompetitionBuilder<
Shared,
Return,
MkConnected,
MkDisconnected,
MkDisabled,
MkAutonomous,
MkDriver,
>
where
MkConnected:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDisconnected:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDisabled:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkAutonomous:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDriver:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
{
pub fn finish(
self,
) -> CompetitionRuntime<
Shared,
Return,
MkConnected,
MkDisconnected,
MkDisabled,
MkAutonomous,
MkDriver,
> {
CompetitionRuntime {
mk_connected: self.mk_connected,
mk_disconnected: self.mk_disconnected,
mk_disabled: self.mk_disabled,
mk_autonomous: self.mk_autonomous,
mk_driver: self.mk_driver,
status: status(),
updates: updates(),
phase: CompetitionRuntimePhase::Initial,
task: None,
shared: UnsafeCell::new(self.shared),
_pin: PhantomPinned,
}
}
}
impl<Shared: 'static, Return, MkConnected, MkDisconnected, MkDisabled, MkAutonomous, MkDriver>
IntoFuture
for CompetitionBuilder<
Shared,
Return,
MkConnected,
MkDisconnected,
MkDisabled,
MkAutonomous,
MkDriver,
>
where
MkConnected:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDisconnected:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDisabled:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkAutonomous:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
MkDriver:
for<'t> FnMut(&'t mut Shared) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 't>>,
{
type Output = Return;
type IntoFuture = CompetitionRuntime<
Shared,
Return,
MkConnected,
MkDisconnected,
MkDisabled,
MkAutonomous,
MkDriver,
>;
fn into_future(self) -> Self::IntoFuture {
self.finish()
}
}
impl<Shared, Return, MkDisconnected, MkDisabled, MkAutonomous, MkDriver>
CompetitionBuilder<
Shared,
Return,
DefaultMk<Shared, Return>,
MkDisconnected,
MkDisabled,
MkAutonomous,
MkDriver,
>
{
pub fn on_connect<Mk>(
self,
mk_connected: Mk,
) -> CompetitionBuilder<Shared, Return, Mk, MkDisconnected, MkDisabled, MkAutonomous, MkDriver>
where
Mk: for<'s> FnMut(
&'s mut Shared,
) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 's>>,
{
CompetitionBuilder {
shared: self.shared,
mk_connected,
mk_disconnected: self.mk_disconnected,
mk_disabled: self.mk_disabled,
mk_autonomous: self.mk_autonomous,
mk_driver: self.mk_driver,
_return: self._return,
}
}
}
impl<Shared, Return, MkConnected, MkDisabled, MkAutonomous, MkDriver>
CompetitionBuilder<
Shared,
Return,
MkConnected,
DefaultMk<Shared, Return>,
MkDisabled,
MkAutonomous,
MkDriver,
>
{
pub fn on_disconnect<Mk>(
self,
mk_disconnected: Mk,
) -> CompetitionBuilder<Shared, Return, MkConnected, Mk, MkDisabled, MkAutonomous, MkDriver>
where
Mk: for<'s> FnMut(
&'s mut Shared,
) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 's>>,
{
CompetitionBuilder {
shared: self.shared,
mk_connected: self.mk_connected,
mk_disconnected,
mk_disabled: self.mk_disabled,
mk_autonomous: self.mk_autonomous,
mk_driver: self.mk_driver,
_return: self._return,
}
}
}
impl<Shared, Return, MkConnected, MkDisconnected, MkAutonomous, MkDriver>
CompetitionBuilder<
Shared,
Return,
MkConnected,
MkDisconnected,
DefaultMk<Shared, Return>,
MkAutonomous,
MkDriver,
>
{
pub fn while_disabled<Mk>(
self,
mk_disabled: Mk,
) -> CompetitionBuilder<Shared, Return, MkConnected, MkDisconnected, Mk, MkAutonomous, MkDriver>
where
Mk: for<'s> FnMut(
&'s mut Shared,
) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 's>>,
{
CompetitionBuilder {
shared: self.shared,
mk_connected: self.mk_connected,
mk_disconnected: self.mk_disconnected,
mk_disabled,
mk_autonomous: self.mk_autonomous,
mk_driver: self.mk_driver,
_return: self._return,
}
}
}
impl<Shared, Return, MkConnected, MkDisconnected, MkDisabled, MkDriver>
CompetitionBuilder<
Shared,
Return,
MkConnected,
MkDisconnected,
MkDisabled,
DefaultMk<Shared, Return>,
MkDriver,
>
{
pub fn while_autonomous<Mk>(
self,
mk_autonomous: Mk,
) -> CompetitionBuilder<Shared, Return, MkConnected, MkDisconnected, MkDisabled, Mk, MkDriver>
where
Mk: for<'s> FnMut(
&'s mut Shared,
) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 's>>,
{
CompetitionBuilder {
shared: self.shared,
mk_connected: self.mk_connected,
mk_disconnected: self.mk_disconnected,
mk_disabled: self.mk_disabled,
mk_autonomous,
mk_driver: self.mk_driver,
_return: self._return,
}
}
}
impl<Shared, Return, MkConnected, MkDisconnected, MkDisabled, MkAutonomous>
CompetitionBuilder<
Shared,
Return,
MkConnected,
MkDisconnected,
MkDisabled,
MkAutonomous,
DefaultMk<Shared, Return>,
>
{
pub fn while_driving<Mk>(
self,
mk_driver: Mk,
) -> CompetitionBuilder<Shared, Return, MkConnected, MkDisconnected, MkDisabled, MkAutonomous, Mk>
where
Mk: for<'s> FnMut(
&'s mut Shared,
) -> Pin<Box<dyn Future<Output = ControlFlow<Return>> + 's>>,
{
CompetitionBuilder {
shared: self.shared,
mk_connected: self.mk_connected,
mk_disconnected: self.mk_disconnected,
mk_disabled: self.mk_disabled,
mk_autonomous: self.mk_autonomous,
mk_driver,
_return: self._return,
}
}
}
#[allow(async_fn_in_trait, clippy::unused_async)]
pub trait Compete: Sized {
async fn connected(&mut self) {}
async fn disconnected(&mut self) {}
async fn disabled(&mut self) {}
async fn autonomous(&mut self) {}
async fn driver(&mut self) {}
}
#[allow(clippy::type_complexity)]
pub trait CompeteExt: Compete {
fn compete(
self,
) -> CompetitionRuntime<
Self,
!,
impl for<'s> FnMut(&'s mut Self) -> Pin<Box<dyn Future<Output = ControlFlow<!>> + 's>>,
impl for<'s> FnMut(&'s mut Self) -> Pin<Box<dyn Future<Output = ControlFlow<!>> + 's>>,
impl for<'s> FnMut(&'s mut Self) -> Pin<Box<dyn Future<Output = ControlFlow<!>> + 's>>,
impl for<'s> FnMut(&'s mut Self) -> Pin<Box<dyn Future<Output = ControlFlow<!>> + 's>>,
impl for<'s> FnMut(&'s mut Self) -> Pin<Box<dyn Future<Output = ControlFlow<!>> + 's>>,
> {
#[allow(clippy::unit_arg)]
CompetitionRuntime::builder(self)
.on_connect(|s| Box::pin(async { ControlFlow::Continue(s.connected().await) }))
.on_disconnect(|s| Box::pin(async { ControlFlow::Continue(s.disconnected().await) }))
.while_disabled(|s| Box::pin(async { ControlFlow::Continue(s.disabled().await) }))
.while_autonomous(|s| Box::pin(async { ControlFlow::Continue(s.autonomous().await) }))
.while_driving(|s| Box::pin(async { ControlFlow::Continue(s.driver().await) }))
.finish()
}
}
impl<R: Compete> CompeteExt for R {}