hexchat_api/
utils.rs

1use libc::c_char;
2use std::ffi::{CString, CStr};
3
4use crate::PHEXCHAT;
5
6/// ```&str -> CString``` (provides a C compatible character buffer)
7///
8/// Wrapper function that creates a `CString` from a `&str`. This is a
9/// convenient format for struct fields where a persistent character buffer
10/// is needed.
11#[inline]
12pub (crate)
13fn str2cstring(s: &str) -> CString {
14    CString::new(s).unwrap()
15}
16
17/// Reduces the syntax required to output formatted text to the current
18/// hexchat window. Internally it invokes
19/// `hexchat.print(&format!("<format-string>", arg1, arg2, ...)`.
20/// Using the macro, this becomes
21/// `hc_print!("<format_string>", arg1, arg2, ...)`. To print from another
22/// thread `hc_print_th!()` can be used.
23/// ``` no_test
24/// use hexchat_api::hc_print;
25/// hc_print!(fmt, argv, ..);
26/// hc_print!(arg);
27/// ```
28///
29#[macro_export]
30macro_rules! hc_print {
31    ( $( $arg:tt )* ) => {
32        hexchat_api::print_inner(&format!( $( $arg )* ))
33    };
34}
35
36/// Used by `hc_print!()` to print to the active Hexchat window. This function
37/// is not intended to be used directly.
38///
39#[doc(hidden)]
40pub fn print_inner(msg: &str) {
41    let hc = unsafe { &*PHEXCHAT };
42    hc.print(msg);
43}
44
45/// Similar to `hc_print!()`, that can be used from spawned threads to print to
46/// the active Hexchat window. Use `hc_print()` if printing from the main 
47/// thread.
48/// ``` no_test
49/// use hexchat_api::hc_print_th;
50/// hc_print_th!(fmt, argv, ..);
51/// hc_print_th!(arg);
52/// ```c
53/// # Arguments
54/// * `ctx=(network, channel)` - Sets the context to print in.
55/// * `fmt`     - The format string.
56/// * `argv`    - The varibale length formatted arguments.
57///
58#[cfg(feature = "threadsafe")]
59#[macro_export]
60macro_rules! hc_print_th {
61    ( $( $arg:tt )* ) => {
62        let rc_msg = format!( $( $arg )* );
63        hexchat_api::main_thread(move |_| hexchat_api::print_inner(&rc_msg));
64    };
65}
66
67/// Executes a command in the active Hexchat window. Provided for convenience
68/// to support formatted string commands.
69///
70#[macro_export]
71macro_rules! hc_command {
72    ( $( $arg:tt )* ) => {
73        hexchat_api::command_inner(&format!( $( $arg )* ));
74    };
75}
76
77/// Executes a command on the main thread. This is useful for executing
78/// commands from spawned threads.
79///
80#[cfg(feature = "threadsafe")]
81#[macro_export]
82macro_rules! hc_command_th {
83    ( $( $arg:tt )* ) => {
84        let rc_cmd = format!( $( $arg )* );
85        hexchat_api::main_thread(move |_| hexchat_api::command_inner(&rc_cmd));
86    };
87}
88
89/// Executes a command in the active Hexchat window. This function is not
90/// intended to be used directly.
91///
92#[doc(hidden)]
93pub fn command_inner(cmd: &str) {
94    let hc = unsafe { &*PHEXCHAT };
95    hc.command(cmd);
96}
97
98
99/// ```*const c_char -> CString```
100///
101/// Creates an owned `CString` from the character buffer. Useful for saving
102/// a string returned by a C call.
103#[inline]
104pub (crate)
105fn pchar2cstring(p_char: *const c_char) -> CString {
106    unsafe { CStr::from_ptr(p_char).to_owned() }
107}
108
109/// ```*const c_char -> String```
110///
111/// Creates a new `String` from a character array - typically coming from
112/// Hexchat. The conversion is lossy - which means any invalid utf8 chars
113/// in the string from Hexchat will be replaced with a default character.
114pub (crate)
115fn pchar2string(p_char: *const c_char) -> String {
116    if p_char.is_null() {
117        String::new()
118    } else {
119        unsafe {
120            CStr::from_ptr(p_char).to_string_lossy().into_owned()
121        }
122    }
123}
124
125/// ```*const *const c_char -> Vec<String>```
126///
127/// Converts a C style character pointer vector (argv) into a Rust String Vec.
128/// This function is used to marshal the string parameters Hexchat passes to
129/// registered command callbacks.
130///
131/// NOTE: This function discards the first item in the vector. This is due to
132///       how the Hexchat callbacks are passed string data. This function is
133///       probably only useful for these specific callbacks.
134///
135/// # Arguments
136/// * `pchar`   - The C array of character pointers.
137/// * `start`   - Which offset in the pchar array to start at.
138///
139/// # Returns
140/// A String Vec mirroring the data passed to it via `pchar`.
141///
142pub (crate)
143fn argv2svec(pchar: *const *const c_char, start: usize) -> Vec<String>
144{
145    // From the Hexchat document site at
146    // (https://hexchat.readthedocs.io/en/latest/plugins.html), the first string
147    // of word and word_eol shouldn't be read:
148    //     These arrays are simply provided for your convenience. You are not
149    //     allowed to alter them. Both arrays are limited to 32 elements
150    //     (index 31). word[0] and word_eol[0] are reserved and should not be
151    //     read.
152    unsafe {
153        let mut svec = vec![];
154        let mut i    = start;
155        let mut pval = *pchar.add(i);
156        loop {
157            // to_string_lossy() protects against invalid chars.
158            let s = CStr::from_ptr(pval)
159                            .to_string_lossy()
160                            .into_owned();
161            if (*pchar.add(i)).is_null() || s.is_empty() {
162                break;
163            }
164            i += 1;
165            pval = *pchar.add(i);
166            svec.push(s);
167        }
168        svec
169    }
170}
171
172/// ```&CString -> String``` creates a new String from a CString.
173/// Some strings coming from Hexchat my contain invalid characters. This
174/// function guards against them offecting the system by replacing those
175/// characters with a default character.
176pub (crate)
177fn cstring2string(cstring: &CString) -> String {
178    cstring.to_string_lossy().into_owned()
179}