use core::{iter::FusedIterator, marker::PhantomData};
use evil_janet::JanetFiber as CJanetFiber;
use janetrs_macros::cjvg;
use super::{Janet, JanetFunction, JanetSignal, JanetTable};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
#[repr(transparent)]
pub struct JanetFiber<'data> {
pub(crate) raw: *mut CJanetFiber,
phantom: PhantomData<&'data ()>,
}
impl<'data> JanetFiber<'data> {
pub fn new(capacity: i32, f: &mut JanetFunction, args: impl AsRef<[Janet]>) -> Option<Self> {
let args = args.as_ref();
let raw = unsafe {
evil_janet::janet_fiber(
f.raw,
capacity,
args.len() as i32,
args.as_ptr() as *const _,
)
};
if raw.is_null() {
None
} else {
Some(Self {
raw,
phantom: PhantomData,
})
}
}
#[cfg_attr(feature = "inline-more", inline)]
pub fn with_env(
env: JanetTable, capacity: i32, f: &mut JanetFunction, args: impl AsRef<[Janet]>,
) -> Option<Self> {
Self::new(capacity, f, args).inspect(|f| {
unsafe { (*f.raw).env = env.raw };
})
}
#[inline]
#[must_use]
pub fn current() -> Option<Self> {
let f = unsafe { evil_janet::janet_current_fiber() };
if f.is_null() {
None
} else {
Some(Self {
raw: f,
phantom: PhantomData,
})
}
}
#[inline]
#[must_use]
pub fn root() -> Option<Self> {
let f = unsafe { evil_janet::janet_root_fiber() };
if f.is_null() {
None
} else {
Some(Self {
raw: f,
phantom: PhantomData,
})
}
}
#[inline]
pub const unsafe fn from_raw(raw: *mut CJanetFiber) -> Self {
Self {
raw,
phantom: PhantomData,
}
}
#[inline]
#[must_use]
pub fn status(&self) -> FiberStatus {
let raw_status = unsafe { evil_janet::janet_fiber_status(self.raw) };
FiberStatus::from(raw_status)
}
#[inline]
pub fn can_resume(&self) -> bool {
let status = self.status();
!matches!(
status,
FiberStatus::Dead
| FiberStatus::Error
| FiberStatus::User0
| FiberStatus::User1
| FiberStatus::User2
| FiberStatus::User3
| FiberStatus::User4
)
}
#[cjvg("1.28.0")]
#[inline]
pub fn can_resume_native(&self) -> bool {
let resume = unsafe { evil_janet::janet_fiber_can_resume(self.raw) };
resume != 0
}
#[inline]
pub fn exec<'a>(&'a mut self) -> Exec<'a, 'data> {
Exec {
fiber: self,
input: Janet::nil(),
}
}
#[inline]
pub fn exec_input<'a>(&'a mut self, input: Janet) -> Exec<'a, 'data> {
Exec { fiber: self, input }
}
#[inline]
pub fn exec_with<'a, F>(&'a mut self, f: F) -> Exec<'a, 'data>
where
F: FnOnce() -> Janet,
{
Exec {
fiber: self,
input: f(),
}
}
#[inline]
#[must_use]
pub const fn as_raw(&self) -> *const CJanetFiber {
self.raw
}
#[inline]
pub fn as_mut_raw(&mut self) -> *mut CJanetFiber {
self.raw
}
}
impl JanetFiber<'_> {
#[inline]
pub(crate) fn display_stacktrace(&mut self, err: Janet) {
unsafe { evil_janet::janet_stacktrace(self.raw, err.inner) }
}
}
#[derive(Debug)]
pub struct Exec<'a, 'data> {
fiber: &'a mut JanetFiber<'data>,
input: Janet,
}
impl Iterator for Exec<'_, '_> {
type Item = Janet;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let mut out = Janet::nil();
let sig =
unsafe { evil_janet::janet_continue(self.fiber.raw, self.input.inner, &mut out.inner) };
let sig = JanetSignal::from(sig);
if matches!(
sig,
JanetSignal::Ok | JanetSignal::Yield | JanetSignal::User9
) {
Some(out)
} else {
self.fiber.display_stacktrace(out);
None
}
}
}
impl FusedIterator for Exec<'_, '_> {}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
#[repr(u32)]
pub enum FiberStatus {
Dead = evil_janet::JanetFiberStatus_JANET_STATUS_DEAD,
Error = evil_janet::JanetFiberStatus_JANET_STATUS_ERROR,
Debug = evil_janet::JanetFiberStatus_JANET_STATUS_DEBUG,
Pending = evil_janet::JanetFiberStatus_JANET_STATUS_PENDING,
User0 = evil_janet::JanetFiberStatus_JANET_STATUS_USER0,
User1 = evil_janet::JanetFiberStatus_JANET_STATUS_USER1,
User2 = evil_janet::JanetFiberStatus_JANET_STATUS_USER2,
User3 = evil_janet::JanetFiberStatus_JANET_STATUS_USER3,
User4 = evil_janet::JanetFiberStatus_JANET_STATUS_USER4,
User5 = evil_janet::JanetFiberStatus_JANET_STATUS_USER5,
User6 = evil_janet::JanetFiberStatus_JANET_STATUS_USER6,
User7 = evil_janet::JanetFiberStatus_JANET_STATUS_USER7,
User8 = evil_janet::JanetFiberStatus_JANET_STATUS_USER8,
User9 = evil_janet::JanetFiberStatus_JANET_STATUS_USER9,
New = evil_janet::JanetFiberStatus_JANET_STATUS_NEW,
Alive = evil_janet::JanetFiberStatus_JANET_STATUS_ALIVE,
}
#[allow(non_upper_case_globals)]
impl From<u32> for FiberStatus {
#[inline]
fn from(val: u32) -> Self {
match val {
evil_janet::JanetFiberStatus_JANET_STATUS_DEAD => Self::Dead,
evil_janet::JanetFiberStatus_JANET_STATUS_ERROR => Self::Error,
evil_janet::JanetFiberStatus_JANET_STATUS_DEBUG => Self::Debug,
evil_janet::JanetFiberStatus_JANET_STATUS_PENDING => Self::Pending,
evil_janet::JanetFiberStatus_JANET_STATUS_USER0 => Self::User0,
evil_janet::JanetFiberStatus_JANET_STATUS_USER1 => Self::User1,
evil_janet::JanetFiberStatus_JANET_STATUS_USER2 => Self::User2,
evil_janet::JanetFiberStatus_JANET_STATUS_USER3 => Self::User3,
evil_janet::JanetFiberStatus_JANET_STATUS_USER4 => Self::User4,
evil_janet::JanetFiberStatus_JANET_STATUS_USER5 => Self::User5,
evil_janet::JanetFiberStatus_JANET_STATUS_USER6 => Self::User6,
evil_janet::JanetFiberStatus_JANET_STATUS_USER7 => Self::User7,
evil_janet::JanetFiberStatus_JANET_STATUS_USER8 => Self::User8,
evil_janet::JanetFiberStatus_JANET_STATUS_USER9 => Self::User9,
evil_janet::JanetFiberStatus_JANET_STATUS_NEW => Self::New,
evil_janet::JanetFiberStatus_JANET_STATUS_ALIVE => Self::Alive,
_ => unreachable!(),
}
}
}
impl From<FiberStatus> for u32 {
#[inline]
fn from(val: FiberStatus) -> Self {
val as u32
}
}
#[cfg(all(test, any(feature = "amalgation", feature = "link-system")))]
mod tests {
use super::*;
use crate::client::JanetClient;
#[test]
fn exec_iterator() -> Result<(), crate::client::Error> {
let client = JanetClient::init_with_default_env()?;
let fun = client
.run(
"(fn [x]
(yield x)
(yield (+ x 1))
(yield (+ x 2))
(yield (* x 2))
x)",
)
.unwrap();
let mut fun = JanetFunction::try_from(fun).unwrap();
let mut fiber = JanetFiber::new(64, &mut fun, [Janet::number(10.0)]).unwrap();
let mut iter = fiber.exec();
assert_eq!(Some(Janet::number(10.0)), iter.next());
assert_eq!(Some(Janet::number(11.0)), iter.next());
assert_eq!(Some(Janet::number(12.0)), iter.next());
assert_eq!(Some(Janet::number(20.0)), iter.next());
assert_eq!(Some(Janet::number(10.0)), iter.next());
assert_eq!(None, iter.next());
assert_eq!(None, iter.next());
Ok(())
}
}