1mod 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
34pub 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#[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 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
168pub trait BufferInputCallback: 'static {
175 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 fn callback(&mut self, weechat: &Weechat, buffer: &Buffer, input: Cow<str>) -> Result<(), ()> {
199 self(weechat, buffer, input)
200 }
201}
202
203pub trait BufferCloseCallback {
208 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)]
227pub trait BufferInputCallbackAsync: 'static {
234 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)))]
259pub 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
266pub 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 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 pub fn input_callback(mut self, callback: impl BufferInputCallbackAsync) -> Self {
331 self.input_callback = Some(Box::new(callback));
332 self
333 }
334
335 pub fn close_callback(mut self, callback: impl BufferCloseCallback + 'static) -> Self {
342 self.close_callback = Some(Box::new(callback));
343 self
344 }
345
346 pub fn build(self) -> Result<BufferHandle, ()> {
348 Weechat::buffer_new_with_async(self)
349 }
350}
351
352impl BufferBuilder {
353 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 pub fn input_callback(mut self, callback: impl BufferInputCallback + 'static) -> Self {
407 self.input_callback = Some(Box::new(callback));
408 self
409 }
410
411 pub fn close_callback(mut self, callback: impl BufferCloseCallback + 'static) -> Self {
417 self.close_callback = Some(Box::new(callback));
418 self
419 }
420
421 pub fn build(self) -> Result<BufferHandle, ()> {
423 Weechat::buffer_new(self)
424 }
425}
426
427impl Weechat {
428 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 pub fn get_localvar(&self, property: &str) -> Option<Cow<str>> {
1077 self.get_string(&format!("localvar_{}", property))
1078 }
1079
1080 pub fn set_localvar(&self, property: &str, value: &str) {
1088 self.set(&format!("localvar_set_{}", property), value)
1089 }
1090
1091 pub fn full_name(&self) -> Cow<str> {
1093 self.get_string("full_name").unwrap()
1094 }
1095
1096 pub fn set_full_name(&self, name: &str) {
1102 self.set("full_name", name);
1103 }
1104
1105 pub fn name(&self) -> Cow<str> {
1107 self.get_string("name").unwrap()
1108 }
1109
1110 pub fn set_name(&self, name: &str) {
1116 self.set("name", name);
1117 }
1118
1119 pub fn short_name(&self) -> Cow<str> {
1121 self.get_string("short_name").unwrap()
1122 }
1123
1124 pub fn set_short_name(&self, name: &str) {
1130 self.set("short_name", name);
1131 }
1132
1133 pub fn plugin_name(&self) -> Cow<str> {
1135 self.get_string("plugin").unwrap()
1136 }
1137
1138 pub fn disable_time_for_each_line(&self) {
1140 self.set("time_for_each_line", "0");
1141 }
1142
1143 pub fn disable_nicklist(&self) {
1145 self.set("nicklist", "0")
1146 }
1147
1148 pub fn enable_nicklist(&self) {
1150 self.set("nicklist", "1")
1151 }
1152
1153 pub fn title(&self) {
1155 self.get_string("title");
1156 }
1157
1158 pub fn set_title(&self, title: &str) {
1164 self.set("title", title);
1165 }
1166
1167 pub fn disable_log(&self) {
1169 self.set("localvar_set_no_log", "1");
1170 }
1171
1172 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 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 pub fn input(&self) -> Cow<str> {
1198 self.get_string("input").unwrap()
1199 }
1200
1201 pub fn set_input(&self, input: &str) {
1203 self.set("input", input)
1204 }
1205
1206 pub fn input_position(&self) -> i32 {
1208 self.get_integer("input_pos")
1209 }
1210
1211 pub fn set_input_position(&self, position: i32) {
1217 self.set("input_pos", &position.to_string())
1218 }
1219
1220 pub fn number(&self) -> i32 {
1222 self.get_integer("number")
1223 }
1224
1225 pub fn switch_to(&self) {
1227 self.set("display", "1");
1228 }
1229
1230 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 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 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}