use crate::debugger::address::RelocatedAddress;
use crate::debugger::r#async::AsyncError;
use crate::debugger::debugee::dwarf::r#type::TypeIdentity;
use crate::debugger::variable::value::{RustEnumValue, SpecializedValue, StructValue, Value};
use std::num::ParseIntError;
#[derive(Debug, thiserror::Error)]
pub enum ParseFutureStateError {
#[error("unexpected future structure representation")]
UnexpectedStructureRepr,
#[error("parse suspend state: {0}")]
ParseSuspendState(ParseIntError),
#[error("unexpected future state: {0}")]
UnexpectedState(String),
}
#[derive(Debug, Clone)]
pub enum AsyncFnFutureState {
Suspend(u32),
Panicked,
Returned,
Unresumed,
Ok,
}
#[derive(Debug, Clone)]
pub struct AsyncFnFuture {
pub name: String,
pub async_fn: String,
pub state: AsyncFnFutureState,
}
impl TryFrom<&RustEnumValue> for AsyncFnFuture {
type Error = AsyncError;
fn try_from(repr: &RustEnumValue) -> Result<Self, Self::Error> {
const UNRESUMED_STATE: &str = "Unresumed";
const RETURNED_STATE: &str = "Returned";
const PANICKED_STATE: &str = "Panicked";
const SUSPEND_STATE: &str = "Suspend";
const OK_STATE: &str = "Ok";
let async_fn = repr
.type_ident
.namespace()
.as_parts()
.join("::")
.to_string();
let name = repr.type_ident.name_fmt().to_string();
let Some(Value::Struct(state)) = repr.value.as_deref().map(|m| &m.value) else {
return Err(AsyncError::ParseFutureState(
ParseFutureStateError::UnexpectedStructureRepr,
));
};
let state = match state.type_ident.name_fmt() {
UNRESUMED_STATE => Ok(AsyncFnFutureState::Unresumed),
RETURNED_STATE => Ok(AsyncFnFutureState::Returned),
PANICKED_STATE => Ok(AsyncFnFutureState::Panicked),
OK_STATE => Ok(AsyncFnFutureState::Ok),
str => {
if str.starts_with(SUSPEND_STATE) {
let str = str.trim_start_matches(SUSPEND_STATE);
let num: u32 = str.parse().map_err(|e| {
AsyncError::ParseFutureState(ParseFutureStateError::ParseSuspendState(e))
})?;
Ok(AsyncFnFutureState::Suspend(num))
} else {
return Err(AsyncError::ParseFutureState(
ParseFutureStateError::UnexpectedState(str.to_string()),
));
}
}
}?;
Ok(Self {
async_fn,
name,
state,
})
}
}
#[derive(Debug, Clone)]
pub struct CustomFuture {
pub name: TypeIdentity,
}
impl From<&StructValue> for CustomFuture {
fn from(repr: &StructValue) -> Self {
let name = repr.type_ident.clone();
Self { name }
}
}
#[derive(Debug, Clone)]
pub struct TokioSleepFuture {
pub name: TypeIdentity,
pub instant: (i64, u32),
}
impl TryFrom<StructValue> for TokioSleepFuture {
type Error = AsyncError;
fn try_from(val: StructValue) -> Result<Self, Self::Error> {
let name = val.type_ident.clone();
let Some(Value::Struct(entry)) = val.field("entry") else {
return Err(AsyncError::IncorrectAssumption(
"Sleep future should contains `entry` field",
));
};
let Some(Value::Struct(deadline)) = entry.field("deadline") else {
return Err(AsyncError::IncorrectAssumption(
"Sleep future should contains `entry.deadline` field",
));
};
let Some(Value::Specialized {
value: Some(SpecializedValue::Instant(instant)),
..
}) = deadline.field("std")
else {
return Err(AsyncError::IncorrectAssumption(
"Sleep future should contains `entry.deadline.std` field",
));
};
Ok(Self { name, instant })
}
}
#[derive(Debug, Clone)]
pub struct TokioJoinHandleFuture {
pub name: TypeIdentity,
pub wait_for_task: RelocatedAddress,
}
impl TryFrom<StructValue> for TokioJoinHandleFuture {
type Error = AsyncError;
fn try_from(val: StructValue) -> Result<Self, Self::Error> {
let name = val.type_ident.clone();
let header_field = val
.field("raw")
.and_then(|raw| raw.field("ptr")?.field("pointer"));
let Some(header) = header_field else {
return Err(AsyncError::IncorrectAssumption(
"JoinHandle future should contains `raw` field",
));
};
let Value::Pointer(ref ptr) = header else {
return Err(AsyncError::IncorrectAssumption(
"JoinHandle::raw.ptr.pointer not a pointer",
));
};
let wait_for_task = ptr
.value
.map(|p| RelocatedAddress::from(p as usize))
.ok_or(AsyncError::IncorrectAssumption(
"JoinHandle::raw.ptr.pointer not a pointer",
))?;
Ok(Self {
name,
wait_for_task,
})
}
}
#[derive(Debug, Clone)]
pub enum Future {
AsyncFn(AsyncFnFuture),
TokioSleep(TokioSleepFuture),
TokioJoinHandleFuture(TokioJoinHandleFuture),
Custom(CustomFuture),
UnknownFuture,
}