Skip to main content

weechat/buffer/
mod.rs

1//! Weechat Buffer module containing Buffer and Nick types.
2
3mod lines;
4mod nick;
5mod nickgroup;
6
7use std::{
8    borrow::Cow,
9    cmp::{Ord, Ordering},
10    ffi::{c_void, CStr},
11    marker::PhantomData,
12    ptr,
13};
14
15use std::{cell::Cell, rc::Rc};
16
17#[cfg(feature = "async")]
18use async_trait::async_trait;
19#[cfg(feature = "async")]
20use futures::future::LocalBoxFuture;
21
22use crate::{LossyCString, Weechat};
23use libc::{c_char, c_int};
24use weechat_sys::{
25    t_gui_buffer, t_gui_nick, t_hdata, t_weechat_plugin, WEECHAT_RC_ERROR, WEECHAT_RC_OK,
26};
27
28pub use crate::buffer::{
29    lines::{BufferLine, BufferLines, LineData},
30    nick::{Nick, NickSettings},
31    nickgroup::NickGroup,
32};
33
34/// A Weechat buffer.
35///
36/// A buffer contains the data displayed on the screen.
37pub struct Buffer<'a> {
38    pub(crate) inner: InnerBuffers<'a>,
39}
40
41impl<'a> std::fmt::Debug for Buffer<'a> {
42    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43        f.debug_struct("Buffer")
44            .field("full_name", &self.full_name())
45            .finish()
46    }
47}
48
49pub(crate) enum InnerBuffers<'a> {
50    BorrowedBuffer(InnerBuffer<'a>),
51    OwnedBuffer(InnerOwnedBuffer<'a>),
52}
53
54impl<'a> InnerBuffers<'a> {
55    fn is_closing(&self) -> bool {
56        match self {
57            InnerBuffers::BorrowedBuffer(b) => b.closing.get(),
58            InnerBuffers::OwnedBuffer(b) => b.closing.get(),
59        }
60    }
61
62    fn mark_as_closing(&self) {
63        match self {
64            InnerBuffers::BorrowedBuffer(b) => b.closing.as_ref().replace(true),
65            InnerBuffers::OwnedBuffer(b) => b.closing.as_ref().replace(true),
66        };
67    }
68}
69
70impl<'a> InnerBuffers<'a> {
71    pub(crate) fn weechat_ptr(&self) -> *mut t_weechat_plugin {
72        match self {
73            InnerBuffers::BorrowedBuffer(b) => b.weechat,
74            InnerBuffers::OwnedBuffer(b) => b.weechat,
75        }
76    }
77}
78
79pub(crate) struct InnerOwnedBuffer<'a> {
80    pub(crate) weechat: *mut t_weechat_plugin,
81    pub(crate) buffer_handle: &'a BufferHandle,
82    closing: Rc<Cell<bool>>,
83}
84
85pub(crate) struct InnerBuffer<'a> {
86    pub(crate) weechat: *mut t_weechat_plugin,
87    pub(crate) ptr: *mut t_gui_buffer,
88    pub(crate) weechat_phantom: PhantomData<&'a Weechat>,
89    pub(crate) closing: Rc<Cell<bool>>,
90}
91
92impl PartialEq for Buffer<'_> {
93    fn eq(&self, other: &Buffer) -> bool {
94        self.ptr() == other.ptr()
95    }
96}
97
98impl PartialOrd for Buffer<'_> {
99    fn partial_cmp(&self, other: &Buffer) -> Option<Ordering> {
100        self.number().partial_cmp(&other.number())
101    }
102}
103
104impl Eq for Buffer<'_> {}
105
106impl Ord for Buffer<'_> {
107    fn cmp(&self, other: &Self) -> Ordering {
108        self.number().cmp(&other.number())
109    }
110}
111
112/// A handle to a buffer that was created in the current plugin.
113///
114/// This means that the plugin owns this buffer. Nevertheless Weechat can
115/// invalidate the buffer between callbacks at any point in time.
116///
117/// The buffer handle can be upgraded to a buffer which can then manipulate the
118/// buffer state using the `upgrade()` method.
119///
120/// The buffer won't be closed if this handle gets dropped, to close the buffer
121/// call the close method on the upgraded buffer object.
122#[derive(Clone)]
123pub struct BufferHandle {
124    buffer_name: Rc<String>,
125    weechat: *mut t_weechat_plugin,
126    buffer_ptr: Rc<Cell<*mut t_gui_buffer>>,
127    closing: Rc<Cell<bool>>,
128}
129
130impl BufferHandle {
131    /// Upgrade the buffer handle into a `Buffer`.
132    ///
133    /// This is necessary to do because the handle can be invalidated by Weechat
134    /// between callbacks.
135    pub fn upgrade(&self) -> Result<Buffer<'_>, ()> {
136        let ptr = self.buffer_ptr.get();
137
138        if ptr.is_null() {
139            Err(())
140        } else {
141            let buffer = Buffer {
142                inner: InnerBuffers::OwnedBuffer(InnerOwnedBuffer {
143                    weechat: self.weechat,
144                    buffer_handle: self,
145                    closing: self.closing.clone(),
146                }),
147            };
148            Ok(buffer)
149        }
150    }
151}
152
153#[cfg(feature = "async")]
154pub(crate) struct BufferPointersAsync {
155    pub(crate) weechat: *mut t_weechat_plugin,
156    pub(crate) input_cb: Option<Box<dyn BufferInputCallbackAsync>>,
157    pub(crate) close_cb: Option<Box<dyn BufferCloseCallback>>,
158    pub(crate) buffer_cell: Option<Rc<Cell<*mut t_gui_buffer>>>,
159}
160
161pub(crate) struct BufferPointers {
162    pub(crate) weechat: *mut t_weechat_plugin,
163    pub(crate) input_cb: Option<Box<dyn BufferInputCallback>>,
164    pub(crate) close_cb: Option<Box<dyn BufferCloseCallback>>,
165    pub(crate) buffer_cell: Option<Rc<Cell<*mut t_gui_buffer>>>,
166}
167
168/// Trait for the buffer input callback
169///
170/// This is the sync version of the callback.
171///
172/// A blanket implementation for pure `FnMut` functions exists, if data needs to
173/// be passed to the callback implement this over your struct.
174pub trait BufferInputCallback: 'static {
175    /// Callback that will be called when the buffer receives some input.
176    ///
177    /// # Arguments
178    ///
179    /// * `weechat` - A Weechat context.
180    ///
181    /// * `buffer` - The buffer that received the input
182    ///
183    /// * `input` - The input that was received.
184    fn callback(&mut self, weechat: &Weechat, buffer: &Buffer, input: Cow<str>) -> Result<(), ()>;
185}
186
187impl<T: FnMut(&Weechat, &Buffer, Cow<str>) -> Result<(), ()> + 'static> BufferInputCallback for T {
188    /// Callback that will be called if the user inputs something into the buffer
189    /// input field.
190    ///
191    /// # Arguments
192    ///
193    /// * `weechat` - A Weechat context.
194    ///
195    /// * `buffer` - The buffer that the user inputed some text into.
196    ///
197    /// * `input` - The input that was posted by the user.
198    fn callback(&mut self, weechat: &Weechat, buffer: &Buffer, input: Cow<str>) -> Result<(), ()> {
199        self(weechat, buffer, input)
200    }
201}
202
203/// Trait for the buffer close callback
204///
205/// A blanket implementation for pure `FnMut` functions exists, if data needs to
206/// be passed to the callback implement this over your struct.
207pub trait BufferCloseCallback {
208    /// Callback that will be called before the buffer is closed.
209    ///
210    /// # Arguments
211    ///
212    /// * `weechat` - A Weechat context.
213    ///
214    /// * `buffer` - The buffer that will be closed.
215    fn callback(&mut self, weechat: &Weechat, buffer: &Buffer) -> Result<(), ()>;
216}
217
218impl<T: FnMut(&Weechat, &Buffer) -> Result<(), ()> + 'static> BufferCloseCallback for T {
219    fn callback(&mut self, weechat: &Weechat, buffer: &Buffer) -> Result<(), ()> {
220        self(weechat, buffer)
221    }
222}
223
224#[cfg(feature = "async")]
225#[cfg_attr(feature = "docs", doc(cfg(r#async)))]
226#[async_trait(?Send)]
227/// Trait for the buffer input callback.
228///
229/// This is the async version of the callback.
230///
231/// A blanket implementation for pure `FnMut` functions exists, if data needs to
232/// be passed to the callback implement this over your struct.
233pub trait BufferInputCallbackAsync: 'static {
234    /// Callback that will be called if the user inputs something into the buffer
235    /// input field.
236    ///
237    /// # Arguments
238    ///
239    /// * `weechat` - A Weechat context.
240    ///
241    /// * `buffer` - The buffer that the user inputed some text into.
242    ///
243    /// * `input` - The input that was posted by the user.
244    async fn callback(&mut self, buffer: BufferHandle, input: String);
245}
246
247#[cfg(feature = "async")]
248#[async_trait(?Send)]
249impl<T: FnMut(BufferHandle, String) -> LocalBoxFuture<'static, ()> + 'static>
250    BufferInputCallbackAsync for T
251{
252    async fn callback(&mut self, buffer: BufferHandle, input: String) {
253        self(buffer, input).await
254    }
255}
256
257#[cfg(feature = "async")]
258#[cfg_attr(feature = "docs", doc(cfg(r#async)))]
259/// Builder for the creation of a buffer.
260pub struct BufferBuilderAsync {
261    pub(crate) name: String,
262    pub(crate) input_callback: Option<Box<dyn BufferInputCallbackAsync>>,
263    pub(crate) close_callback: Option<Box<dyn BufferCloseCallback>>,
264}
265
266/// Builder for the creation of a buffer.
267pub struct BufferBuilder {
268    pub(crate) name: String,
269    pub(crate) input_callback: Option<Box<dyn BufferInputCallback>>,
270    pub(crate) close_callback: Option<Box<dyn BufferCloseCallback>>,
271}
272
273#[cfg(feature = "async")]
274impl BufferBuilderAsync {
275    /// Create a new buffer builder that will create a buffer with an async
276    /// input callback.
277    ///
278    /// # Arguments
279    ///
280    /// * `name` - The name of the new buffer. Needs to be unique across a
281    /// plugin, otherwise the buffer creation will fail.
282    ///
283    /// Returns a Buffer if one has been created, otherwise an empty Error.
284    ///
285    /// # Panics
286    ///
287    /// Panics if the method is not called from the main Weechat thread.
288    ///
289    /// # Example
290    /// ```no_run
291    /// # use futures::future::{FutureExt, LocalBoxFuture};
292    /// # use weechat::Weechat;
293    /// # use weechat::buffer::{BufferHandle, Buffer, BufferBuilderAsync};
294    /// fn input_cb(buffer: BufferHandle, input: String) -> LocalBoxFuture<'static, ()> {
295    ///     async move {
296    ///         let buffer = buffer.upgrade().unwrap();
297    ///         buffer.print(&input);
298    ///     }.boxed_local()
299    /// }
300    ///
301    /// let buffer_handle = BufferBuilderAsync::new("test_buffer")
302    ///     .input_callback(input_cb)
303    ///     .close_callback(|weechat: &Weechat, buffer: &Buffer| {
304    ///         Ok(())
305    /// })
306    ///     .build()
307    ///     .expect("Can't create new buffer");
308    ///
309    /// let buffer = buffer_handle
310    ///     .upgrade()
311    ///     .expect("Can't upgrade newly created buffer");
312    ///
313    /// buffer.enable_nicklist();
314    /// buffer.print("Hello world");
315    /// ```
316    pub fn new(name: &str) -> Self {
317        BufferBuilderAsync {
318            name: name.to_owned(),
319            input_callback: None,
320            close_callback: None,
321        }
322    }
323
324    /// Set the buffer input callback.
325    ///
326    /// # Arguments
327    ///
328    /// * `callback` - An async function that will be called once a user inputs
329    ///     data into the buffer input line.
330    pub fn input_callback(mut self, callback: impl BufferInputCallbackAsync) -> Self {
331        self.input_callback = Some(Box::new(callback));
332        self
333    }
334
335    /// Set the close callback.
336    ///
337    /// # Arguments
338    ///
339    /// * `callback` - The callback that should be called before a buffer is
340    ///     closed.
341    pub fn close_callback(mut self, callback: impl BufferCloseCallback + 'static) -> Self {
342        self.close_callback = Some(Box::new(callback));
343        self
344    }
345
346    /// Build the configured buffer.
347    pub fn build(self) -> Result<BufferHandle, ()> {
348        Weechat::buffer_new_with_async(self)
349    }
350}
351
352impl BufferBuilder {
353    /// Create a new buffer builder that will create a buffer with an sync input
354    /// callback.
355    ///
356    /// # Arguments
357    ///
358    /// * `name` - The name of the new buffer. Needs to be unique across a
359    /// plugin, otherwise the buffer creation will fail.
360    ///
361    /// # Panics
362    ///
363    /// Panics if the method is not called from the main Weechat thread.
364    ///
365    /// Returns a Buffer if one has been created, otherwise an empty Error.
366    ///
367    /// # Example
368    /// ```no_run
369    /// # use std::borrow::Cow;
370    /// # use weechat::Weechat;
371    /// # use weechat::buffer::{Buffer, BufferHandle, BufferBuilder};
372    /// fn input_cb(weechat: &Weechat, buffer: &Buffer, input: Cow<str>) -> Result<(), ()> {
373    ///     buffer.print(&input);
374    ///     Ok(())
375    /// }
376    ///
377    /// let buffer_handle = BufferBuilder::new("test_buffer")
378    ///     .input_callback(input_cb)
379    ///     .close_callback(|weechat: &Weechat, buffer: &Buffer| {
380    ///         Ok(())
381    /// })
382    ///     .build()
383    ///     .expect("Can't create new buffer");
384    ///
385    /// let buffer = buffer_handle
386    ///     .upgrade()
387    ///     .expect("Can't upgrade newly created buffer");
388    ///
389    /// buffer.enable_nicklist();
390    /// buffer.print("Hello world");
391    /// ```
392    pub fn new(name: &str) -> Self {
393        BufferBuilder {
394            name: name.to_owned(),
395            input_callback: None,
396            close_callback: None,
397        }
398    }
399
400    /// Set the buffer input callback.
401    ///
402    /// # Arguments
403    ///
404    /// * `callback` - A function or a struct that implements the
405    /// BufferCloseCallback trait.
406    pub fn input_callback(mut self, callback: impl BufferInputCallback + 'static) -> Self {
407        self.input_callback = Some(Box::new(callback));
408        self
409    }
410
411    /// Set the close callback.
412    ///
413    /// # Arguments
414    ///
415    /// * `callback` - The callback that should be called before a buffer is
416    pub fn close_callback(mut self, callback: impl BufferCloseCallback + 'static) -> Self {
417        self.close_callback = Some(Box::new(callback));
418        self
419    }
420
421    /// Build the configured buffer.
422    pub fn build(self) -> Result<BufferHandle, ()> {
423        Weechat::buffer_new(self)
424    }
425}
426
427impl Weechat {
428    /// Search a buffer by plugin and/or name.
429    ///
430    /// Returns a Buffer if one is found, otherwise None.
431    ///
432    /// # Arguments
433    ///
434    /// * `plugin_name` - name of a plugin, the following special value is
435    ///     allowed: "==", the buffer name used is the buffers full name.
436    ///
437    /// * `buffer_name` - name of a buffer, if this is an empty string,
438    ///     the current buffer is returned (buffer displayed by current
439    ///     window); if the name starts with (?i), the search is case
440    ///     insensitive.
441    pub fn buffer_search(&self, plugin_name: &str, buffer_name: &str) -> Option<Buffer> {
442        let buffer_search = self.get().buffer_search.unwrap();
443
444        let plugin_name = LossyCString::new(plugin_name);
445        let buffer_name = LossyCString::new(buffer_name);
446
447        let buf_ptr = unsafe { buffer_search(plugin_name.as_ptr(), buffer_name.as_ptr()) };
448
449        if buf_ptr.is_null() {
450            None
451        } else {
452            Some(self.buffer_from_ptr(buf_ptr))
453        }
454    }
455
456    pub(crate) fn buffer_from_ptr(&self, buffer_ptr: *mut t_gui_buffer) -> Buffer {
457        Buffer {
458            inner: InnerBuffers::BorrowedBuffer(InnerBuffer {
459                weechat: self.ptr,
460                ptr: buffer_ptr,
461                weechat_phantom: PhantomData,
462                closing: Rc::new(Cell::new(false)),
463            }),
464        }
465    }
466
467    /// Get the currently open buffer
468    pub fn current_buffer(&self) -> Buffer {
469        let buffer_search = self.get().buffer_search.unwrap();
470
471        let buf_ptr = unsafe { buffer_search(ptr::null(), ptr::null()) };
472        if buf_ptr.is_null() {
473            panic!("No open buffer found");
474        } else {
475            self.buffer_from_ptr(buf_ptr)
476        }
477    }
478
479    #[cfg(feature = "async")]
480    #[cfg_attr(feature = "docs", doc(cfg(r#async)))]
481    fn buffer_new_with_async(builder: BufferBuilderAsync) -> Result<BufferHandle, ()> {
482        unsafe extern "C" fn c_input_cb(
483            pointer: *const c_void,
484            _data: *mut c_void,
485            buffer: *mut t_gui_buffer,
486            input_data: *const c_char,
487        ) -> c_int {
488            let input_data = CStr::from_ptr(input_data).to_string_lossy();
489
490            let pointers: &mut BufferPointersAsync =
491                { &mut *(pointer as *mut BufferPointersAsync) };
492
493            let weechat = Weechat::from_ptr(pointers.weechat);
494            let buffer = weechat.buffer_from_ptr(buffer);
495            let buffer_cell = pointers
496                .buffer_cell
497                .as_ref()
498                .expect("Buffer cell wasn't initialized properly")
499                .clone();
500
501            let buffer_handle = BufferHandle {
502                buffer_name: Rc::new(buffer.full_name().to_string()),
503                weechat: pointers.weechat,
504                buffer_ptr: buffer_cell,
505                closing: Rc::new(Cell::new(false)),
506            };
507            if let Some(cb) = pointers.input_cb.as_mut() {
508                let future = cb.callback(buffer_handle, input_data.to_string());
509                Weechat::spawn_buffer_cb(buffer.full_name().to_string(), future).detach();
510            }
511
512            WEECHAT_RC_OK
513        }
514
515        unsafe extern "C" fn c_close_cb(
516            pointer: *const c_void,
517            _data: *mut c_void,
518            buffer: *mut t_gui_buffer,
519        ) -> c_int {
520            // We use from_raw() here so that the box gets deallocated at the
521            // end of this scope.
522            let pointers = Box::from_raw(pointer as *mut BufferPointersAsync);
523            let weechat = Weechat::from_ptr(pointers.weechat);
524            let buffer = weechat.buffer_from_ptr(buffer);
525            buffer.mark_as_closing();
526
527            let ret = if let Some(mut cb) = pointers.close_cb {
528                cb.callback(&weechat, &buffer).is_ok()
529            } else {
530                true
531            };
532
533            // Invalidate the buffer pointer now.
534            pointers
535                .buffer_cell
536                .as_ref()
537                .expect("Buffer cell wasn't initialized properly")
538                .replace(ptr::null_mut());
539
540            if ret {
541                WEECHAT_RC_OK
542            } else {
543                WEECHAT_RC_ERROR
544            }
545        }
546
547        Weechat::check_thread();
548        let weechat = unsafe { Weechat::weechat() };
549
550        let c_input_cb: Option<WeechatInputCbT> = match builder.input_callback {
551            Some(_) => Some(c_input_cb),
552            None => None,
553        };
554
555        // We create a box and use leak to stop rust from freeing our data,
556        // we are giving Weechat ownership over the data and will free it in
557        // the buffer close callback.
558        let buffer_pointers = Box::new(BufferPointersAsync {
559            weechat: weechat.ptr,
560            input_cb: builder.input_callback,
561            close_cb: builder.close_callback,
562            buffer_cell: None,
563        });
564
565        let buffer_pointers_ref = Box::leak(buffer_pointers);
566
567        let buf_new = weechat.get().buffer_new.unwrap();
568        let c_name = LossyCString::new(builder.name);
569
570        let buf_ptr = unsafe {
571            buf_new(
572                weechat.ptr,
573                c_name.as_ptr(),
574                c_input_cb,
575                buffer_pointers_ref as *const _ as *const c_void,
576                ptr::null_mut(),
577                Some(c_close_cb),
578                buffer_pointers_ref as *const _ as *const c_void,
579                ptr::null_mut(),
580            )
581        };
582
583        if buf_ptr.is_null() {
584            unsafe { Box::from_raw(buffer_pointers_ref) };
585            return Err(());
586        }
587
588        let pointers: &mut BufferPointersAsync =
589            unsafe { &mut *(buffer_pointers_ref as *mut BufferPointersAsync) };
590
591        let buffer = weechat.buffer_from_ptr(buf_ptr);
592        let buffer_cell = Rc::new(Cell::new(buf_ptr));
593
594        pointers.buffer_cell = Some(buffer_cell.clone());
595
596        Ok(BufferHandle {
597            buffer_name: Rc::new(buffer.full_name().to_string()),
598            weechat: weechat.ptr,
599            buffer_ptr: buffer_cell,
600            closing: Rc::new(Cell::new(false)),
601        })
602    }
603
604    fn buffer_new(builder: BufferBuilder) -> Result<BufferHandle, ()> {
605        unsafe extern "C" fn c_input_cb(
606            pointer: *const c_void,
607            _data: *mut c_void,
608            buffer: *mut t_gui_buffer,
609            input_data: *const c_char,
610        ) -> c_int {
611            let input_data = CStr::from_ptr(input_data).to_string_lossy();
612
613            let pointers: &mut BufferPointers = { &mut *(pointer as *mut BufferPointers) };
614
615            let weechat = Weechat::from_ptr(pointers.weechat);
616            let buffer = weechat.buffer_from_ptr(buffer);
617
618            let ret = if let Some(ref mut cb) = pointers.input_cb.as_mut() {
619                cb.callback(&weechat, &buffer, input_data).is_ok()
620            } else {
621                true
622            };
623
624            if ret {
625                WEECHAT_RC_OK
626            } else {
627                WEECHAT_RC_ERROR
628            }
629        }
630
631        unsafe extern "C" fn c_close_cb(
632            pointer: *const c_void,
633            _data: *mut c_void,
634            buffer: *mut t_gui_buffer,
635        ) -> c_int {
636            // We use from_raw() here so that the box gets freed at the end
637            // of this scope.
638            let pointers = Box::from_raw(pointer as *mut BufferPointers);
639            let weechat = Weechat::from_ptr(pointers.weechat);
640            let buffer = weechat.buffer_from_ptr(buffer);
641            buffer.mark_as_closing();
642
643            let ret = if let Some(mut cb) = pointers.close_cb {
644                cb.callback(&weechat, &buffer).is_ok()
645            } else {
646                true
647            };
648
649            // Invalidate the buffer pointer now.
650            pointers
651                .buffer_cell
652                .as_ref()
653                .expect("Buffer cell wasn't initialized properly")
654                .replace(ptr::null_mut());
655
656            if ret {
657                WEECHAT_RC_OK
658            } else {
659                WEECHAT_RC_ERROR
660            }
661        }
662
663        Weechat::check_thread();
664        let weechat = unsafe { Weechat::weechat() };
665
666        let c_input_cb: Option<WeechatInputCbT> = match builder.input_callback {
667            Some(_) => Some(c_input_cb),
668            None => None,
669        };
670
671        // We create a box and use leak to stop rust from freeing our data,
672        // we are giving weechat ownership over the data and will free it in
673        // the buffer close callback.
674        let buffer_pointers = Box::new(BufferPointers {
675            weechat: weechat.ptr,
676            input_cb: builder.input_callback,
677            close_cb: builder.close_callback,
678            buffer_cell: None,
679        });
680        let buffer_pointers_ref = Box::leak(buffer_pointers);
681
682        let buf_new = weechat.get().buffer_new.unwrap();
683        let c_name = LossyCString::new(builder.name);
684
685        let buf_ptr = unsafe {
686            buf_new(
687                weechat.ptr,
688                c_name.as_ptr(),
689                c_input_cb,
690                buffer_pointers_ref as *const _ as *const c_void,
691                ptr::null_mut(),
692                Some(c_close_cb),
693                buffer_pointers_ref as *const _ as *const c_void,
694                ptr::null_mut(),
695            )
696        };
697
698        if buf_ptr.is_null() {
699            unsafe { Box::from_raw(buffer_pointers_ref) };
700            return Err(());
701        }
702
703        let pointers: &mut BufferPointers =
704            unsafe { &mut *(buffer_pointers_ref as *mut BufferPointers) };
705
706        let buffer = weechat.buffer_from_ptr(buf_ptr);
707        let buffer_cell = Rc::new(Cell::new(buf_ptr));
708
709        pointers.buffer_cell = Some(buffer_cell.clone());
710
711        Ok(BufferHandle {
712            buffer_name: Rc::new(buffer.full_name().to_string()),
713            weechat: weechat.ptr,
714            buffer_ptr: buffer_cell,
715            closing: Rc::new(Cell::new(false)),
716        })
717    }
718}
719
720pub(crate) type WeechatInputCbT = unsafe extern "C" fn(
721    pointer: *const c_void,
722    data: *mut c_void,
723    buffer: *mut t_gui_buffer,
724    input_data: *const c_char,
725) -> c_int;
726
727impl Buffer<'_> {
728    fn weechat(&self) -> Weechat {
729        let ptr = match &self.inner {
730            InnerBuffers::BorrowedBuffer(b) => b.weechat,
731            InnerBuffers::OwnedBuffer(b) => b.weechat,
732        };
733
734        Weechat::from_ptr(ptr)
735    }
736
737    pub(crate) fn ptr(&self) -> *mut t_gui_buffer {
738        match &self.inner {
739            InnerBuffers::BorrowedBuffer(b) => b.ptr,
740            InnerBuffers::OwnedBuffer(b) => {
741                let ptr = b.buffer_handle.buffer_ptr.get();
742
743                if ptr.is_null() {
744                    panic!("Buffer {} has been closed.", b.buffer_handle.buffer_name)
745                } else {
746                    ptr
747                }
748            }
749        }
750    }
751
752    fn is_closing(&self) -> bool {
753        self.inner.is_closing()
754    }
755
756    fn mark_as_closing(&self) {
757        self.inner.mark_as_closing()
758    }
759
760    /// Display a message on the buffer.
761    pub fn print(&self, message: &str) {
762        let weechat = self.weechat();
763        let printf_date_tags = weechat.get().printf_date_tags.unwrap();
764
765        let fmt_str = LossyCString::new("%s");
766        let c_message = LossyCString::new(message);
767
768        unsafe {
769            printf_date_tags(
770                self.ptr(),
771                0,
772                ptr::null(),
773                fmt_str.as_ptr(),
774                c_message.as_ptr(),
775            )
776        }
777    }
778
779    /// Display a message on the buffer with attached date and tags
780    ///
781    /// # Arguments
782    ///
783    /// * `date` - A unix time-stamp representing the date of the message, 0
784    ///     means now.
785    ///
786    /// * `tags` - A list of tags that will be applied to the printed line.
787    ///
788    /// * `message` - The message that will be displayed.
789    pub fn print_date_tags(&self, date: i64, tags: &[&str], message: &str) {
790        let weechat = self.weechat();
791        let printf_date_tags = weechat.get().printf_date_tags.unwrap();
792
793        let fmt_str = LossyCString::new("%s");
794        let tags = tags.join(",");
795        let tags = LossyCString::new(tags);
796        let message = LossyCString::new(message);
797
798        unsafe {
799            printf_date_tags(
800                self.ptr(),
801                date,
802                tags.as_ptr(),
803                fmt_str.as_ptr(),
804                message.as_ptr(),
805            )
806        }
807    }
808
809    /// Search for a nicklist group by name
810    ///
811    /// # Arguments
812    ///
813    /// * `name` - The name of the nicklist that should be searched for.
814    ///
815    /// Returns a NickGroup if one is found, None otherwise.
816    pub fn search_nicklist_group(&self, name: &str) -> Option<NickGroup> {
817        let weechat = self.weechat();
818
819        let nicklist_search_group = weechat.get().nicklist_search_group.unwrap();
820
821        let name = LossyCString::new(name);
822
823        let group = unsafe { nicklist_search_group(self.ptr(), ptr::null_mut(), name.as_ptr()) };
824
825        if group.is_null() {
826            None
827        } else {
828            Some(NickGroup {
829                ptr: group,
830                buf_ptr: self.ptr(),
831                weechat_ptr: self.weechat().ptr,
832                buffer: PhantomData,
833            })
834        }
835    }
836
837    /// Search for a nick in the whole nicklist.
838    ///
839    /// # Arguments
840    ///
841    /// * `nick` - The name of the nick that should be found.
842    ///
843    /// Returns a `Nick` if one is found, None otherwise.
844    pub fn search_nick(&self, nick: &str) -> Option<Nick> {
845        let weechat = self.weechat();
846        let nick = Buffer::search_nick_helper(&weechat, self.ptr(), nick, None);
847
848        if nick.is_null() {
849            None
850        } else {
851            Some(Nick {
852                ptr: nick,
853                buf_ptr: self.ptr(),
854                weechat_ptr: weechat.ptr,
855                buffer: PhantomData,
856            })
857        }
858    }
859
860    fn search_nick_helper(
861        weechat: &Weechat,
862        buffer_ptr: *mut t_gui_buffer,
863        nick: &str,
864        group: Option<&NickGroup>,
865    ) -> *mut t_gui_nick {
866        let nicklist_search_nick = weechat.get().nicklist_search_nick.unwrap();
867
868        let nick = LossyCString::new(nick);
869        let group_ptr = group.map(|g| g.ptr).unwrap_or(ptr::null_mut());
870
871        unsafe { nicklist_search_nick(buffer_ptr, group_ptr, nick.as_ptr()) }
872    }
873
874    /// Create and add a new nick to the buffer nicklist.
875    ///
876    /// This will add the nick to the root nick group.
877    ///
878    /// # Arguments
879    ///
880    /// * `nick_settings` - Nick arguments struct for the nick that should be
881    ///     added.
882    ///
883    /// Returns the newly created nick if one is created successfully, an empty
884    /// error otherwise.
885    pub fn add_nick(&self, nick_settings: NickSettings) -> Result<Nick, ()> {
886        let weechat = self.weechat();
887        let nick_ptr = Buffer::add_nick_helper(&weechat, self.ptr(), nick_settings, None);
888
889        if nick_ptr.is_null() {
890            return Err(());
891        }
892
893        Ok(Nick {
894            ptr: nick_ptr,
895            buf_ptr: self.ptr(),
896            weechat_ptr: self.weechat().ptr,
897            buffer: PhantomData,
898        })
899    }
900
901    /// Removes a group from the nicklist.
902    ///
903    /// # Arguments
904    ///
905    /// * `group_name` - The name of the group that should be removed.
906    ///
907    /// Returns `true` if a group was found and removed, `false` otherwise.
908    pub fn remove_nicklist_group(&self, group_name: &str) -> bool {
909        let weechat = self.weechat();
910
911        let group = self.search_nicklist_group(group_name);
912
913        match group {
914            Some(group) => {
915                let nicklist_remove_group = weechat.get().nicklist_remove_group.unwrap();
916
917                unsafe {
918                    nicklist_remove_group(self.ptr(), group.ptr);
919                }
920                true
921            }
922            None => false,
923        }
924    }
925
926    /// Removes a nick from the nicklist.
927    ///
928    /// # Arguments
929    ///
930    /// * `nick` - The name of the nick that should be removed.
931    ///
932    /// Returns `true` if a nick was found and removed, `false` otherwise.
933    pub fn remove_nick(&self, nick: &str) -> bool {
934        let weechat = self.weechat();
935
936        let nick = self.search_nick(nick);
937
938        match nick {
939            Some(nick) => {
940                let nicklist_remove_nick = weechat.get().nicklist_remove_nick.unwrap();
941
942                unsafe {
943                    nicklist_remove_nick(self.ptr(), nick.ptr);
944                }
945                true
946            }
947            None => false,
948        }
949    }
950
951    fn add_nick_helper(
952        weechat: &Weechat,
953        buffer_ptr: *mut t_gui_buffer,
954        nick_settings: NickSettings,
955        group: Option<&NickGroup>,
956    ) -> *mut t_gui_nick {
957        let c_nick = LossyCString::new(nick_settings.name);
958        let color = LossyCString::new(nick_settings.color);
959        let prefix = LossyCString::new(nick_settings.prefix);
960        let prefix_color = LossyCString::new(nick_settings.prefix_color);
961
962        let add_nick = weechat.get().nicklist_add_nick.unwrap();
963
964        let group_ptr = match group {
965            Some(g) => g.ptr,
966            None => ptr::null_mut(),
967        };
968
969        unsafe {
970            add_nick(
971                buffer_ptr,
972                group_ptr,
973                c_nick.as_ptr(),
974                color.as_ptr(),
975                prefix.as_ptr(),
976                prefix_color.as_ptr(),
977                nick_settings.visible as i32,
978            )
979        }
980    }
981
982    /// Create and add a new nicklist group to the buffers nicklist.
983    ///
984    /// * `name` - Name of the new group.
985    ///
986    /// * `color` - Color of the new group.
987    ///
988    /// * `visible` - Should the group be visible in the nicklist.
989    ///
990    /// * `parent_group` - Parent group that the group should be added to.
991    ///     If no group is provided the group is added to the root group.
992    ///
993    /// Returns the new nicklist group. The group is not removed if the object
994    /// is dropped.
995    pub fn add_nicklist_group(
996        &self,
997        name: &str,
998        color: &str,
999        visible: bool,
1000        parent_group: Option<&NickGroup>,
1001    ) -> Result<NickGroup, ()> {
1002        let weechat = self.weechat();
1003        let add_group = weechat.get().nicklist_add_group.unwrap();
1004
1005        let c_name = LossyCString::new(name);
1006        let c_color = LossyCString::new(color);
1007
1008        let group_ptr = match parent_group {
1009            Some(g) => g.ptr,
1010            None => ptr::null_mut(),
1011        };
1012
1013        let group_ptr = unsafe {
1014            add_group(
1015                self.ptr(),
1016                group_ptr,
1017                c_name.as_ptr(),
1018                c_color.as_ptr(),
1019                visible as i32,
1020            )
1021        };
1022
1023        if group_ptr.is_null() {
1024            return Err(());
1025        }
1026
1027        Ok(NickGroup {
1028            ptr: group_ptr,
1029            buf_ptr: self.ptr(),
1030            weechat_ptr: self.weechat().ptr,
1031            buffer: PhantomData,
1032        })
1033    }
1034
1035    fn set(&self, property: &str, value: &str) {
1036        let weechat = self.weechat();
1037
1038        let buffer_set = weechat.get().buffer_set.unwrap();
1039        let option = LossyCString::new(property);
1040        let value = LossyCString::new(value);
1041
1042        unsafe { buffer_set(self.ptr(), option.as_ptr(), value.as_ptr()) };
1043    }
1044
1045    fn get_string(&self, property: &str) -> Option<Cow<str>> {
1046        let weechat = self.weechat();
1047
1048        let buffer_get = weechat.get().buffer_get_string.unwrap();
1049        let property = LossyCString::new(property);
1050
1051        unsafe {
1052            let value = buffer_get(self.ptr(), property.as_ptr());
1053            if value.is_null() {
1054                None
1055            } else {
1056                Some(CStr::from_ptr(value).to_string_lossy())
1057            }
1058        }
1059    }
1060
1061    fn get_integer(&self, property: &str) -> i32 {
1062        let weechat = self.weechat();
1063
1064        let buffer_get = weechat.get().buffer_get_integer.unwrap();
1065        let property = LossyCString::new(property);
1066
1067        unsafe { buffer_get(self.ptr(), property.as_ptr()) }
1068    }
1069
1070    /// Get the value of a buffer localvar
1071    ///
1072    /// # Arguments
1073    ///
1074    /// * `property` - The name of the property for which the value should be
1075    ///     fetched.
1076    pub fn get_localvar(&self, property: &str) -> Option<Cow<str>> {
1077        self.get_string(&format!("localvar_{}", property))
1078    }
1079
1080    /// Set the value of a buffer localvar
1081    ///
1082    /// # Arguments
1083    ///
1084    /// * `property` - The property that should be set.
1085    ///
1086    /// * `value` - The value that the property should get.
1087    pub fn set_localvar(&self, property: &str, value: &str) {
1088        self.set(&format!("localvar_set_{}", property), value)
1089    }
1090
1091    /// Get the full name of the buffer.
1092    pub fn full_name(&self) -> Cow<str> {
1093        self.get_string("full_name").unwrap()
1094    }
1095
1096    /// Set the full name of the buffer
1097    ///
1098    /// # Arguments
1099    ///
1100    /// * `name` - The new full name that should be set.
1101    pub fn set_full_name(&self, name: &str) {
1102        self.set("full_name", name);
1103    }
1104
1105    /// Get the name of the buffer.
1106    pub fn name(&self) -> Cow<str> {
1107        self.get_string("name").unwrap()
1108    }
1109
1110    /// Set the name of the buffer.
1111    ///
1112    /// # Arguments
1113    ///
1114    /// * `name` - The new name that should be set.
1115    pub fn set_name(&self, name: &str) {
1116        self.set("name", name);
1117    }
1118
1119    /// Get the short_name of the buffer.
1120    pub fn short_name(&self) -> Cow<str> {
1121        self.get_string("short_name").unwrap()
1122    }
1123
1124    /// Set the short_name of the buffer.
1125    ///
1126    /// # Arguments
1127    ///
1128    /// * `name` - The new short name that should be set.
1129    pub fn set_short_name(&self, name: &str) {
1130        self.set("short_name", name);
1131    }
1132
1133    /// Get the plugin name of the plugin that owns this buffer.
1134    pub fn plugin_name(&self) -> Cow<str> {
1135        self.get_string("plugin").unwrap()
1136    }
1137
1138    /// Hide time for all lines in the buffer.
1139    pub fn disable_time_for_each_line(&self) {
1140        self.set("time_for_each_line", "0");
1141    }
1142
1143    /// Disable the nicklist for this buffer.
1144    pub fn disable_nicklist(&self) {
1145        self.set("nicklist", "0")
1146    }
1147
1148    /// Enable the nicklist for this buffer.
1149    pub fn enable_nicklist(&self) {
1150        self.set("nicklist", "1")
1151    }
1152
1153    /// Get the title of the buffer
1154    pub fn title(&self) {
1155        self.get_string("title");
1156    }
1157
1158    /// Set the title of the buffer.
1159    ///
1160    /// # Arguments
1161    ///
1162    /// * `title` - The new title that will be set.
1163    pub fn set_title(&self, title: &str) {
1164        self.set("title", title);
1165    }
1166
1167    /// Disable logging for this buffer.
1168    pub fn disable_log(&self) {
1169        self.set("localvar_set_no_log", "1");
1170    }
1171
1172    /// Clear buffer contents
1173    pub fn clear(&self) {
1174        let weechat = self.weechat();
1175
1176        let buffer_clear = weechat.get().buffer_clear.unwrap();
1177        unsafe { buffer_clear(self.ptr()) }
1178    }
1179
1180    /// Close the buffer.
1181    ///
1182    /// Note that this will only queue up the buffer to be closed. The close
1183    /// callback will first be run, only then will the buffer be closed.
1184    ///
1185    /// Multiple calls to this method will result in a nop.
1186    pub fn close(&self) {
1187        if !self.is_closing() {
1188            let weechat = self.weechat();
1189
1190            let buffer_close = weechat.get().buffer_close.unwrap();
1191            unsafe { buffer_close(self.ptr()) };
1192            self.mark_as_closing();
1193        }
1194    }
1195
1196    /// Get the contents of the input
1197    pub fn input(&self) -> Cow<str> {
1198        self.get_string("input").unwrap()
1199    }
1200
1201    /// Set the content of the buffer input.
1202    pub fn set_input(&self, input: &str) {
1203        self.set("input", input)
1204    }
1205
1206    /// Get the position of the cursor in the buffer input.
1207    pub fn input_position(&self) -> i32 {
1208        self.get_integer("input_pos")
1209    }
1210
1211    /// Set the position of the input buffer.
1212    ///
1213    /// # Arguments
1214    ///
1215    /// * `position` - The new position of the input.
1216    pub fn set_input_position(&self, position: i32) {
1217        self.set("input_pos", &position.to_string())
1218    }
1219
1220    /// Get the number of the buffer.
1221    pub fn number(&self) -> i32 {
1222        self.get_integer("number")
1223    }
1224
1225    /// Switch to the buffer
1226    pub fn switch_to(&self) {
1227        self.set("display", "1");
1228    }
1229
1230    /// Run the given command in the buffer.
1231    ///
1232    /// # Arguments
1233    ///
1234    /// * `command` - The command that should run.
1235    ///
1236    /// # Example
1237    ///
1238    /// ```no_run
1239    /// # use weechat::Weechat;
1240    /// # use weechat::buffer::BufferBuilder;
1241    /// # let buffer_handle = BufferBuilder::new("test")
1242    /// #    .build()
1243    /// #    .unwrap();
1244    /// # let buffer = buffer_handle.upgrade().unwrap();
1245    ///
1246    /// // Switch to the core buffer using a command.
1247    /// buffer.run_command("/buffer core");
1248    /// ```
1249    pub fn run_command(&self, command: &str) -> Result<(), ()> {
1250        let command = LossyCString::new(command);
1251        let weechat = self.weechat();
1252        let run_command = weechat.get().command.unwrap();
1253
1254        let ret = unsafe { run_command(weechat.ptr, self.ptr(), command.as_ptr()) };
1255
1256        match ret {
1257            WEECHAT_RC_OK => Ok(()),
1258            WEECHAT_RC_ERROR => Err(()),
1259            _ => unreachable!(),
1260        }
1261    }
1262
1263    fn hdata_pointer(&self) -> *mut t_hdata {
1264        let weechat = self.weechat();
1265
1266        unsafe { weechat.hdata_get("buffer") }
1267    }
1268
1269    fn own_lines(&self) -> *mut c_void {
1270        let weechat = self.weechat();
1271
1272        let hdata = self.hdata_pointer();
1273
1274        unsafe { weechat.hdata_pointer(hdata, self.ptr() as *mut c_void, "own_lines") }
1275    }
1276
1277    /// Get the number of lines that the buffer has printed out.
1278    pub fn num_lines(&self) -> i32 {
1279        let weechat = self.weechat();
1280        let own_lines = self.own_lines();
1281
1282        unsafe {
1283            let lines = weechat.hdata_get("lines");
1284            weechat.hdata_integer(lines, own_lines, "lines_count")
1285        }
1286    }
1287
1288    /// Get the lines of the buffer.
1289    ///
1290    /// This returns an iterator over all the buffer lines, the iterator can be
1291    /// traversed forwards (from the first line of the buffer, to the last) as
1292    /// well as backwards (from the last line of the buffer to the first).
1293    ///
1294    /// # Example
1295    /// ```no_run
1296    /// # use weechat::Weechat;
1297    /// # use weechat::buffer::BufferBuilder;
1298    /// # let buffer_handle = BufferBuilder::new("test")
1299    /// #    .build()
1300    /// #    .unwrap();
1301    /// # let buffer = buffer_handle.upgrade().unwrap();
1302    ///
1303    /// let lines = buffer.lines();
1304    ///
1305    /// for line in lines {
1306    ///     Weechat::print(&format!("{:?}", line.tags()));
1307    /// }
1308    /// ```
1309    pub fn lines(&self) -> BufferLines {
1310        let weechat = self.weechat();
1311
1312        let own_lines = self.own_lines();
1313
1314        let (first_line, last_line) = unsafe {
1315            let lines = weechat.hdata_get("lines");
1316
1317            (
1318                weechat.hdata_pointer(lines, own_lines, "first_line"),
1319                weechat.hdata_pointer(lines, own_lines, "last_line"),
1320            )
1321        };
1322
1323        BufferLines {
1324            weechat_ptr: self.weechat().ptr,
1325            first_line,
1326            last_line,
1327            buffer: PhantomData,
1328            done: false,
1329        }
1330    }
1331}