use core::fmt;
use std::{collections::HashMap, sync::Arc};
use crate::probe::{CommandResult, DebugProbeError, JtagCommand};
#[derive(Debug, thiserror::Error)]
pub enum BatchError<E> {
#[error(transparent)]
Specific(E),
#[error(transparent)]
Probe(DebugProbeError),
}
#[derive(thiserror::Error, Debug)]
pub struct BatchExecutionError<E = Box<dyn std::error::Error + Send + Sync>> {
#[source]
pub error: BatchError<E>,
pub results: DeferredResultSet<CommandResult>,
}
impl BatchExecutionError {
pub(crate) fn new_specific(
error: Box<dyn std::error::Error + Send + Sync>,
results: DeferredResultSet<CommandResult>,
) -> Self {
BatchExecutionError {
error: match error.downcast::<DebugProbeError>() {
Ok(error) => BatchError::Probe(*error),
Err(error) => BatchError::Specific(error),
},
results,
}
}
pub(crate) fn new_from_debug_probe(
error: DebugProbeError,
results: DeferredResultSet<CommandResult>,
) -> Self {
BatchExecutionError {
error: BatchError::Probe(error),
results,
}
}
pub fn downcast_specific<T>(self) -> BatchExecutionError<T>
where
T: std::error::Error + Send + Sync + 'static,
{
BatchExecutionError {
error: match self.error {
BatchError::Specific(boxed) => BatchError::Specific(
*boxed
.downcast::<T>()
.expect("error type mismatch in downcast_specific"),
),
BatchError::Probe(e) => BatchError::Probe(e),
},
results: self.results,
}
}
}
impl<E: std::fmt::Display> std::fmt::Display for BatchExecutionError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"Error cause was {}. Successful command count {}",
self.error,
self.results.len()
)
}
}
#[derive(Debug)]
pub struct Queue<E> {
queue: ErasedQueue,
_marker: std::marker::PhantomData<E>,
}
impl<E: std::error::Error + Send + Sync + 'static> Default for Queue<E> {
fn default() -> Self {
Self::new()
}
}
impl<E: std::error::Error + Send + Sync + 'static> Queue<E> {
pub fn new() -> Self {
Self {
queue: ErasedQueue::new(),
_marker: std::marker::PhantomData,
}
}
pub fn schedule(&mut self, cmd: impl Into<JtagCommand>) -> DeferredResultIndex {
self.queue.schedule(cmd)
}
}
impl<E> Queue<E> {
pub fn is_empty(&self) -> bool {
self.queue.is_empty()
}
pub fn len(&self) -> usize {
self.queue.len()
}
pub fn consume(&mut self, len: usize) {
self.queue.consume(len)
}
pub fn rewind(&mut self, by: usize) -> bool {
self.queue.rewind(by)
}
}
impl<E: std::error::Error + Send + Sync + 'static> Queue<E> {
pub fn execute<F>(
&self,
command: F,
) -> Result<DeferredResultSet<CommandResult>, BatchExecutionError<E>>
where
F: FnOnce(&ErasedQueue) -> Result<DeferredResultSet<CommandResult>, BatchExecutionError>,
{
command(&self.queue).map_err(|e| e.downcast_specific::<E>())
}
}
pub struct DeferredResultSet<T>(HashMap<DeferredResultIndex, T>);
impl<T: std::fmt::Debug> std::fmt::Debug for DeferredResultSet<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("DeferredResultSet").field(&self.0).finish()
}
}
impl<T> Default for DeferredResultSet<T> {
fn default() -> Self {
Self(HashMap::default())
}
}
impl<T> DeferredResultSet<T> {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(capacity: usize) -> Self {
Self(HashMap::with_capacity(capacity))
}
pub(crate) fn push(&mut self, idx: &DeferredResultIndex, result: T) {
self.0.insert(idx.clone(), result);
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub(crate) fn merge_from(&mut self, other: DeferredResultSet<T>) {
self.0.extend(other.0);
self.0.retain(|k, _| k.should_capture());
}
pub fn take(&mut self, index: DeferredResultIndex) -> Result<T, DeferredResultIndex> {
self.0.remove(&index).ok_or(index)
}
}
#[derive(Eq)]
pub struct DeferredResultIndex(Arc<()>);
impl PartialEq for DeferredResultIndex {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
impl fmt::Debug for DeferredResultIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_tuple("DeferredResultIndex")
.field(&self.id())
.finish()
}
}
impl DeferredResultIndex {
pub(crate) fn new() -> Self {
Self(Arc::new(()))
}
fn id(&self) -> usize {
Arc::as_ptr(&self.0) as usize
}
pub(crate) fn should_capture(&self) -> bool {
Arc::strong_count(&self.0) > 1
}
}
impl Clone for DeferredResultIndex {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl std::hash::Hash for DeferredResultIndex {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.id().hash(state)
}
}
#[derive(Debug, Default)]
pub struct ErasedQueue {
commands: Vec<(DeferredResultIndex, JtagCommand)>,
cursor: usize,
}
impl ErasedQueue {
fn new() -> Self {
Self::default()
}
fn schedule(&mut self, command: impl Into<JtagCommand>) -> DeferredResultIndex {
let index = DeferredResultIndex::new();
self.commands.push((index.clone(), command.into()));
index
}
pub fn len(&self) -> usize {
self.commands[self.cursor..].len()
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub(crate) fn iter(&self) -> impl Iterator<Item = &(DeferredResultIndex, JtagCommand)> {
self.commands[self.cursor..].iter()
}
pub(crate) fn rewind(&mut self, by: usize) -> bool {
if self.cursor >= by {
self.cursor -= by;
true
} else {
false
}
}
pub(crate) fn consume(&mut self, len: usize) {
debug_assert!(self.len() >= len);
self.cursor += len;
}
}