use crate::runtime::heap::{Gc, GcHeader, Marker};
use crate::runtime::table::Table;
#[repr(C)]
pub struct Userdata {
pub(crate) hdr: GcHeader,
metatable: Option<Gc<Table>>,
pub(crate) payload: UserdataPayload,
pub(crate) peeked: Option<u8>,
pub(crate) write_buf: Vec<u8>,
pub(crate) writable: bool,
pub(crate) buf_mode: u8,
pub(crate) popen_child: Option<std::process::Child>,
}
pub enum UserdataPayload {
File(FileHandle),
Empty,
Host {
type_id: std::any::TypeId,
data: Box<dyn std::any::Any + 'static>,
},
}
pub enum FileHandle {
Stdin,
Stdout,
Stderr,
File(
std::fs::File,
),
Closed,
}
impl FileHandle {
pub fn is_closed(&self) -> bool {
matches!(self, FileHandle::Closed)
}
pub fn is_std(&self) -> bool {
matches!(self, FileHandle::Stdin | FileHandle::Stdout | FileHandle::Stderr)
}
}
impl Userdata {
pub(crate) fn new(hdr: GcHeader, payload: UserdataPayload, writable: bool) -> Userdata {
Userdata {
hdr,
metatable: None,
payload,
peeked: None,
write_buf: Vec::new(),
writable,
buf_mode: 0,
popen_child: None,
}
}
pub fn metatable(&self) -> Option<Gc<Table>> {
self.metatable
}
pub fn set_metatable(&mut self, mt: Option<Gc<Table>>) {
self.metatable = mt;
}
pub(crate) fn trace(&self, m: &mut Marker) {
if let Some(mt) = self.metatable {
m.header(mt.as_ptr() as *mut GcHeader);
}
}
pub fn file(&self) -> &FileHandle {
match &self.payload {
UserdataPayload::File(fh) => fh,
UserdataPayload::Empty => panic!("file() on a newproxy userdata"),
UserdataPayload::Host { .. } => panic!("file() on a host userdata"),
}
}
pub fn file_mut(&mut self) -> &mut FileHandle {
match &mut self.payload {
UserdataPayload::File(fh) => fh,
UserdataPayload::Empty => panic!("file_mut() on a newproxy userdata"),
UserdataPayload::Host { .. } => panic!("file_mut() on a host userdata"),
}
}
pub fn is_proxy(&self) -> bool {
matches!(self.payload, UserdataPayload::Empty)
}
pub fn is_host(&self) -> bool {
matches!(self.payload, UserdataPayload::Host { .. })
}
pub fn downcast<T: std::any::Any + 'static>(&self) -> Option<&T> {
match &self.payload {
UserdataPayload::Host { type_id, data } => {
if *type_id == std::any::TypeId::of::<T>() {
data.downcast_ref::<T>()
} else {
None
}
}
_ => None,
}
}
pub fn downcast_mut<T: std::any::Any + 'static>(&mut self) -> Option<&mut T> {
match &mut self.payload {
UserdataPayload::Host { type_id, data } => {
if *type_id == std::any::TypeId::of::<T>() {
data.downcast_mut::<T>()
} else {
None
}
}
_ => None,
}
}
}