1use std::ffi::CStr;
2use std::ffi::CString;
3use std::ffi::c_void;
4use std::fmt::Display;
5use std::os::raw::c_char;
6
7use crate::Interpreter;
8use crate::TclStatus;
9use crate::tcl::*;
10
11#[repr(C)]
15#[derive(Debug)]
16pub struct RawObject {
17 pub(crate) ref_count: usize,
18 pub bytes: *mut c_char,
19 pub length: usize,
20 pub obj_type: *const ObjectType,
21 pub ptr1: *mut c_void,
22 pub ptr2: *mut c_void,
23}
24
25impl RawObject {
26 pub fn wrap(obj: *mut RawObject) -> Object {
27 unsafe {
28 INCR_REF_COUNT
29 .expect("module must have been initialized")
30 (obj)
31 };
32 Object { obj: obj }
33 }
34}
35
36#[derive(Debug)]
37pub struct Object {
38 pub obj: *mut RawObject,
39}
40
41impl Object {
42 pub fn new() -> Object {
43 unsafe { RawObject::wrap(NEW_OBJ.expect("module must have been initialized")()) }
44 }
45
46 pub fn new_string(s: &str) -> Object {
47 let cstr = CString::new(s).expect("Unexpected NulError!");
48 unsafe {
49 RawObject::wrap(NEW_STRING_OBJ.expect("module must have been initialized")(
50 cstr.as_ptr() as *const i8,
51 cstr.as_bytes().len() as usize,
52 ))
53 }
54 }
55
56 pub fn set_string(&self, s: &str) {
57 let cstr = CString::new(s).expect("unexpected NulError!");
58 unsafe {
59 SET_STRING_OBJ.expect("module must have been initialized")(
60 self.obj,
61 cstr.as_ptr() as *const i8,
62 cstr.as_bytes().len() as usize,
63 );
64 }
65 }
66
67 pub fn get_string(&self) -> &str {
69 unsafe {
70 CStr::from_ptr(GET_STRING.expect("module must have been initialized")(
71 self.obj,
72 ))
73 .to_str()
74 .expect("TCL guarantees strings are valid UTF-8")
75 }
76 }
77
78 pub fn get_type_name(&self) -> &str {
80 let raw_obj = unsafe { &*self.obj };
81 if raw_obj.obj_type.is_null() {
82 "string"
83 } else {
84 unsafe {
85 let obj_type = &*raw_obj.obj_type;
86 CStr::from_ptr(obj_type.name as *const i8)
87 .to_str()
88 .expect("TCL guarantees string are valid UTF-8")
89 }
90 }
91 }
92}
93
94impl Drop for Object {
95 fn drop(&mut self) {
96 unsafe { DECR_REF_COUNT.expect("module must have been initialized")(self.obj) }
97 }
98}
99
100impl Display for Object {
101 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
102 unsafe {
103 let o_type = (*self.obj).obj_type;
104 if (*self.obj).bytes.is_null()
105 && !o_type.is_null()
106 && o_type.as_ref().unwrap().update_string_proc.is_some()
107 {
108 let update_fn = o_type.as_ref().unwrap().update_string_proc.unwrap();
109 update_fn(self.obj);
110 } else if (*self.obj).bytes.is_null() {
111 panic!("invalid string representation and no update function");
112 }
113
114 write!(f, "{}", CStr::from_ptr((*self.obj).bytes).to_str().unwrap())
115 }
116 }
117}
118
119#[repr(C)]
123#[derive(Debug)]
124pub struct ObjectType {
125 pub name: *const u8,
126 pub free_internal_rep_proc: Option<extern "C" fn(*mut RawObject)>,
127 pub dup_internal_rep_proc: extern "C" fn(*const RawObject, *mut RawObject),
128 pub update_string_proc: Option<extern "C" fn(*mut RawObject)>,
129 pub set_from_any_proc: Option<extern "C" fn(*const Interpreter, *mut RawObject) -> TclStatus>,
130}
131
132unsafe impl Sync for ObjectType {}
133
134pub trait TclObjectType: Clone + Display {
135 fn from_object(obj: &Object) -> Option<&Self>;
136
137 fn into_object(self) -> Object;
138
139 fn type_name() -> &'static str;
140
141 fn tcl_type() -> &'static ObjectType;
142
143 fn as_string(&self) -> String {
144 format!("{}", self)
145 }
146
147 fn convert(obj: Object) -> Result<Object, Object> {
148 Err(obj)
149 }
150}