use std::ffi::CStr;
use std::ffi::CString;
use std::ffi::c_void;
use std::fmt::Display;
use std::os::raw::c_char;
use crate::Interpreter;
use crate::TclStatus;
use crate::tcl::*;
#[repr(C)]
#[derive(Debug)]
pub struct RawObject {
pub(crate) ref_count: usize,
pub bytes: *mut c_char,
pub length: usize,
pub obj_type: *const ObjectType,
pub ptr1: *mut c_void,
pub ptr2: *mut c_void,
}
impl RawObject {
pub fn wrap(obj: *mut RawObject) -> Object {
unsafe { INCR_REF_COUNT.expect("module must have been initialized")(obj) };
Object { obj }
}
}
#[derive(Debug)]
pub struct Object {
pub obj: *mut RawObject,
}
impl Default for Object {
fn default() -> Self {
Object::new()
}
}
impl Object {
pub fn new() -> Object {
unsafe { RawObject::wrap(NEW_OBJ.expect("module must have been initialized")()) }
}
pub fn new_string(s: &str) -> Object {
let cstr = CString::new(s).expect("Unexpected NulError!");
unsafe {
RawObject::wrap(NEW_STRING_OBJ.expect("module must have been initialized")(
cstr.as_ptr(),
cstr.as_bytes().len(),
))
}
}
pub fn set_string(&self, s: &str) {
let cstr = CString::new(s).expect("unexpected NulError!");
unsafe {
SET_STRING_OBJ.expect("module must have been initialized")(
self.obj,
cstr.as_ptr(),
cstr.as_bytes().len(),
);
}
}
pub fn get_string(&self) -> &str {
unsafe {
CStr::from_ptr(GET_STRING.expect("module must have been initialized")(
self.obj,
))
.to_str()
.expect("TCL guarantees strings are valid UTF-8")
}
}
pub fn get_type_name(&self) -> &str {
let raw_obj = unsafe { &*self.obj };
if raw_obj.obj_type.is_null() {
"string"
} else {
unsafe {
let obj_type = &*raw_obj.obj_type;
CStr::from_ptr(obj_type.name as *const i8)
.to_str()
.expect("TCL guarantees string are valid UTF-8")
}
}
}
}
impl Drop for Object {
fn drop(&mut self) {
unsafe { DECR_REF_COUNT.expect("module must have been initialized")(self.obj) }
}
}
impl Display for Object {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
unsafe {
let o_type = (*self.obj).obj_type;
if (*self.obj).bytes.is_null()
&& !o_type.is_null()
&& o_type.as_ref().unwrap().update_string_proc.is_some()
{
let update_fn = o_type.as_ref().unwrap().update_string_proc.unwrap();
update_fn(self.obj);
} else if (*self.obj).bytes.is_null() {
panic!("invalid string representation and no update function");
}
write!(
f,
"{}",
CStr::from_ptr((*self.obj).bytes)
.to_str()
.expect("TCL guarantees strings are valid UTF-8")
)
}
}
}
#[repr(C)]
#[derive(Debug)]
pub struct ObjectType {
pub name: *const u8,
pub free_internal_rep_proc: Option<extern "C" fn(*mut RawObject)>,
pub dup_internal_rep_proc: extern "C" fn(*const RawObject, *mut RawObject),
pub update_string_proc: Option<extern "C" fn(*mut RawObject)>,
pub set_from_any_proc: Option<extern "C" fn(*const Interpreter, *mut RawObject) -> TclStatus>,
}
unsafe impl Sync for ObjectType {}
pub trait TclObjectType: Clone + Display {
fn from_object(obj: &Object) -> Option<&Self>;
fn into_object(self) -> Object;
fn type_name() -> &'static str;
fn tcl_type() -> &'static ObjectType;
fn convert(obj: Object) -> Result<Object, Object> {
Err(obj)
}
}