tcl/lib.rs
1//! High-level bindings to Tcl 8.6
2//!
3//! The crate tcl is bindings to Tcl programming language, aiming at providing safe and easy to use API.
4//!
5//! # Quickstart
6//!
7//! ## `std::Convert` between Rust values and Tcl objects.
8//!
9//! ```rust
10//! use std::convert::TryFrom;
11//! use tcl::*;
12//!
13//! let obj = Obj::from( 0 );
14//! assert_eq!( obj.to_string(), "0" );
15//! assert_eq!( i32::try_from( obj )?, 0 );
16//!
17//! let obj = Obj::from( 1 );
18//! assert_eq!( obj.as_i32(), 1 );
19//! assert_eq!( obj.as_f64(), 1.0 );
20//! assert_eq!( obj.as_bool(), true );
21//!
22//! let obj = Obj::from(( false, 42, "answer".to_owned() ));
23//! assert_eq!( obj.to_string(), "0 42 answer" );
24//! assert_eq!( <(bool,i32,String)>::try_from( obj )?,
25//! (false, 42, "answer".to_owned() )
26//! );
27//!
28//! let v = vec![ "alpha".to_owned(), "beta".to_owned(), "gamma".to_owned() ];
29//! let obj: Obj = v.clone().into();
30//! assert_eq!( obj.to_string(), "alpha beta gamma" );
31//! assert_eq!( Vec::<String>::try_from( obj )?, v );
32//!
33//! use std::collections::HashMap;
34//!
35//! let mut map = HashMap::new();
36//! map.insert( "alpha".to_owned(), 1 );
37//! map.insert( "beta" .to_owned(), 2 );
38//! map.insert( "gamma".to_owned(), 3 );
39//!
40//! let obj: Obj = map.clone().into();
41//! assert_eq!( HashMap::<String, i32>::try_from( obj )?, map );
42//!
43//! # Ok::<(),TclError>(())
44//! ```
45//!
46//! ## User-defined types `deserialize`d / `try_from` Tcl objects.
47//!
48//! ```rust
49//! use tcl::*;
50//!
51//! #[derive( Clone, PartialEq, Debug, serde::Deserialize )]
52//! #[derive( TryFromDe )]
53//! struct Struct{ a: i32, b: bool, c: f64 }
54//!
55//! let obj = Obj::from( "a 1 b false c 3.14" );
56//! let v: Struct = from_obj( obj.clone() )?;
57//! assert_eq!( v, Struct{ a: 1, b: false, c: 3.14 });
58//!
59//! let v: Struct = obj.clone().try_into()?;
60//! assert_eq!( v, Struct{ a: 1, b: false, c: 3.14 });
61//!
62//! # Ok::<(),TclError>(())
63//! ```
64//!
65//! ## Use `Tcl<T>` to store Rust values in Tcl `Obj`s, an vice-vesa.
66//!
67//! ```rust
68//! use std::convert::TryFrom;
69//! use tcl::*;
70//!
71//! let obj = Tcl::new_obj( vec![ 1, 1, 2, 3, 5, 8 ]);
72//! let tcl_obj = Tcl::<Vec<i32>>::try_from( obj )?;
73//! assert_eq!( tcl_obj.into_inner(), vec![ 1, 1, 2, 3, 5, 8 ]);
74//!
75//! # Ok::<(),TclError>(())
76//! ```
77//!
78//! ## Run Tcl scripts
79//!
80//! ```rust
81//! use tcl::*;
82//!
83//! let interpreter = Interpreter::new()?;
84//! let a = 3;
85//! let b = 7;
86//! let c = interpreter.eval(( "expr", a, "*", b ))?;
87//! assert_eq!( a*b, c.as_i32() );
88//!
89//! # Ok::<(),TclError>(())
90//! ```
91//!
92//! ## Register Rust functions as tcl commands, the unsafe way
93//!
94//! ```rust
95//! use tcl::*;
96//!
97//! #[proc] fn mul( a: i32, b: i32 ) -> TclResult<i32> { Ok( a * b )}
98//!
99//! let interpreter = Interpreter::new()?;
100//! unsafe { // it's safe for `#[proc] fn`.
101//! interpreter.def_proc( "mul", mul );
102//! }
103//! let c = interpreter.eval( "mul 3 7" )?;
104//! assert_eq!( c.as_i32(), 21 );
105//!
106//! # Ok::<(),TclError>(())
107//! ```
108//!
109//! ## Register Rust functions as tcl commands, the safe way
110//!
111//! ```rust
112//! use tcl::*;
113//!
114//! let interpreter = Interpreter::new()?;
115//!
116//! let cmd = tclfn!( &interpreter, /*cmd: "mul", args: "",*/
117//! fn mul( a: i32, b: i32 ) -> TclResult<i32> { Ok( a * b )}
118//! );
119//!
120//! let c = interpreter.eval( "mul 3 7" )?;
121//! assert_eq!( c.as_i32(), 21 );
122//!
123//! # Ok::<(),TclError>(())
124//! ```
125//!
126//! ## Register Rust closures as tcl commands
127//!
128//! ```rust
129//! use tcl::*;
130//!
131//! let offset = 0;
132//! let interpreter = Interpreter::new()?;
133//!
134//! let cmd = tclosure!( &interpreter, /*cmd: "mul", args: "",*/
135//! move |a: i32, b: i32| -> TclResult<i32> { Ok( a * b + offset )}
136//! );
137//!
138//! let a = 3;
139//! let b = 7;
140//! let c = interpreter.eval(( "eval", cmd, a, b ))?;
141//! assert_eq!( c.as_i32(), 21 );
142//!
143//! # Ok::<(),TclError>(())
144//! ```
145
146/// The following items will be seen in `tcl_derive`'s proc macros.
147/// Let's reexport them from `crate clib`,
148/// otherwise the `crate tcl`'s users will depend on `clib` too.
149pub mod reexport_clib {
150 pub use clib::{
151 ClientData,
152 TCL_ERROR,
153 TCL_OK,
154 Tcl_Interp,
155 Tcl_InvalidateStringRep,
156 Tcl_Obj,
157 Tcl_SetObjResult,
158 Tcl_WrongNumArgs,
159 };
160}
161
162pub use tcl_derive::{
163 TryFromDe,
164 proc,
165 tclfn,
166 tclosure,
167};
168
169use std::{
170 env,
171 ffi::CString,
172 sync::Once,
173};
174
175macro_rules! tcl_panic {
176 ( $msg:expr ) => {{
177 let s = CString::new( $msg ).unwrap_or_default();
178 #[allow( unused_unsafe )]
179 unsafe{ clib::Tcl_Panic( s.as_c_str().as_ptr() ); }
180 unreachable!()
181 }};
182}
183
184/// Aborts the program instead of `panic!()` in FFI callbacks.
185pub trait UnwrapOrAbort {
186 type Inner;
187
188 /// Returns the value on success, otherwise aborts the program with `message`.
189 fn unwrap_or_abort( self, message: &str ) -> Self::Inner;
190}
191
192impl<T> UnwrapOrAbort for Option<T> {
193 type Inner = T;
194
195 #[allow( unreachable_code )]
196 fn unwrap_or_abort( self, message: &str ) -> Self::Inner {
197 self.unwrap_or_else( || tcl_panic!( format!( "unwrap_or_abort on None value: {}", message ).as_str() ))
198 }
199}
200
201impl<T,E> UnwrapOrAbort for Result<T,E>
202 where E: std::fmt::Debug
203{
204 type Inner = T;
205
206 #[allow( unreachable_code )]
207 fn unwrap_or_abort( self, message: &str ) -> Self::Inner {
208 self.unwrap_or_else( |err| tcl_panic!( format!( "unwrap_or_abort on Err: {:?}\n{}", err, message ).as_str() ))
209 }
210}
211
212mod after;
213
214pub mod interp;
215pub use interp::{CodeToResult, Interpreter, Interp, ObjCmdProc};
216
217pub mod obj;
218pub use obj::{Obj, incr_ref, decr_ref};
219
220pub mod ext;
221pub use ext::Tcl;
222
223pub mod ser;
224pub use ser::{Serializer, to_c_str, to_string};
225
226mod de;
227pub use de::from_obj;
228
229pub mod error;
230pub use error::{
231 IntoTclError,
232 TclError,
233 TclResult,
234};
235
236pub mod dict;
237pub use dict::DictIter;
238
239pub mod list;
240
241mod trace;
242
243mod update;
244
245static INIT: Once = Once::new();
246
247pub(crate) fn init() {
248 INIT.call_once( || {
249 let arg0 = CString::new( env::args().next().unwrap() ).unwrap();
250 unsafe {
251 clib::Tcl_FindExecutable( arg0.as_ptr() );
252 if clib::Tcl_GetNameOfExecutable().is_null() {
253 panic!( "failed to initialize Tcl" );
254 }
255 }
256 });
257}