use super::{ProgramProjection, ProgramShape, ProgramSourceError};
use crate::global::program::Projectable;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum ProjectionProblem {
RouteControllerMismatch,
ReceiveLaneCausality,
ParallelSelector,
ReentrySelector,
UnprojectableRoute,
MissingBranchKnowledge,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DiagnosticRange {
pub start: usize,
pub end: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct DiagnosticEvent {
pub index: usize,
pub from: u8,
pub to: u8,
pub label: u8,
pub lane: u8,
}
impl DiagnosticEvent {
pub(crate) const fn from_atom(index: usize, atom: crate::eff::EffAtom) -> Self {
Self {
index,
from: atom.from,
to: atom.to,
label: atom.label,
lane: atom.lane,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ProjectionDiagnostic {
pub problem: ProjectionProblem,
pub role: Option<u8>,
pub scope: Option<u16>,
pub controller: Option<u8>,
pub arms: Option<[DiagnosticRange; 2]>,
pub first: Option<DiagnosticEvent>,
pub second: Option<DiagnosticEvent>,
}
impl ProjectionDiagnostic {
pub(crate) const fn from_error(error: ProgramSourceError) -> Self {
let problem = match error {
ProgramSourceError::RouteControllerMismatch => {
ProjectionProblem::RouteControllerMismatch
}
ProgramSourceError::ReceiveLaneCausalityConflict => {
ProjectionProblem::ReceiveLaneCausality
}
ProgramSourceError::ParallelAmbiguousEndpointSelector => {
ProjectionProblem::ParallelSelector
}
ProgramSourceError::ReentryAmbiguousEndpointSelector => {
ProjectionProblem::ReentrySelector
}
ProgramSourceError::ProjectionRouteUnprojectable => {
ProjectionProblem::UnprojectableRoute
}
};
Self {
problem,
role: None,
scope: None,
controller: None,
arms: None,
first: None,
second: None,
}
}
pub const fn help(&self) -> &'static str {
match self.problem {
ProjectionProblem::RouteControllerMismatch => {
"give both route arms the same first-visible controller"
}
ProjectionProblem::ReceiveLaneCausality => {
"add a causal handoff from the earlier receiver to the later sender; on reentry, close that handoff inside the roll"
}
ProjectionProblem::ParallelSelector => "make parallel endpoint selectors distinct",
ProjectionProblem::ReentrySelector => {
"make rolled continuation and reentry endpoint selectors distinct"
}
ProjectionProblem::MissingBranchKnowledge => {
"notify the affected role of the branch, or make its local paths mergeable; keep terminal acknowledgments within the selected arm"
}
ProjectionProblem::UnprojectableRoute => {
"inspect route selectors and passive-child scope structure"
}
}
}
}
impl core::fmt::Display for ProjectionDiagnostic {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", self.problem)?;
if let Some(role) = self.role {
write!(f, " role={role}")?;
}
if let Some(scope) = self.scope {
write!(f, " scope={scope}")?;
}
if let Some(controller) = self.controller {
write!(f, " controller={controller}")?;
}
if let Some(arms) = self.arms {
write!(
f,
" arms={}..{}|{}..{}",
arms[0].start, arms[0].end, arms[1].start, arms[1].end
)?;
}
for event in [self.first, self.second].into_iter().flatten() {
write!(
f,
" event#{}({}->{} label={} lane={})",
event.index, event.from, event.to, event.label, event.lane
)?;
}
write!(f, "; {}", self.help())
}
}
pub fn diagnose<P: Projectable>(program: &P) -> Option<ProjectionDiagnostic> {
let _ = program;
const {
let rows = P::Steps::SOURCE_ROW_COUNT;
if rows <= 8 {
diagnostic_for::<P::Steps, 8>()
} else if rows <= 32 {
diagnostic_for::<P::Steps, 32>()
} else if rows <= 128 {
diagnostic_for::<P::Steps, 128>()
} else if rows <= 512 {
diagnostic_for::<P::Steps, 512>()
} else if rows <= 2048 {
diagnostic_for::<P::Steps, 2048>()
} else if rows <= 8192 {
diagnostic_for::<P::Steps, 8192>()
} else if rows <= 32768 {
diagnostic_for::<P::Steps, 32768>()
} else if rows <= 65535 {
diagnostic_for::<P::Steps, 65535>()
} else {
panic!("choreography source exceeds compact descriptor domain")
}
}
}
const fn diagnostic_for<Steps: ProgramShape, const CAPACITY: usize>() -> Option<ProjectionDiagnostic>
{
ProgramProjection::<Steps, CAPACITY>::DIAGNOSTIC
}