1#![allow(unused_assignments)]
3use anyhow::Context;
15use colored::*;
16#[allow(unused_imports)] use log::error;
18use serde::{Deserialize, Serialize};
19use std::fmt::{Display, Error, Formatter};
20use std::path::PathBuf;
21
22pub mod prelude {
24 pub use super::FatalError;
25 pub use super::LoggableError;
26 #[cfg(not(target_family = "wasm"))]
27 pub use super::ToAnyhow;
28 pub use super::ZellijError;
29 pub use anyhow::anyhow;
30 pub use anyhow::bail;
31 pub use anyhow::Context;
32 pub use anyhow::Error as anyError;
33 pub use anyhow::Result;
34}
35
36pub trait ErrorInstruction {
37 fn error(err: String) -> Self;
38}
39
40pub trait LoggableError<T>: Sized {
49 #[track_caller]
64 fn print_error<F: Fn(&str)>(self, fun: F) -> Self;
65
66 #[track_caller]
76 fn to_log(self) -> Self {
77 let caller = std::panic::Location::caller();
78 self.print_error(|msg| {
79 log::logger().log(
84 &log::Record::builder()
85 .level(log::Level::Error)
86 .args(format_args!("{}", msg))
87 .file(Some(caller.file()))
88 .line(Some(caller.line()))
89 .module_path(None)
90 .build(),
91 );
92 })
93 }
94
95 fn to_stderr(self) -> Self {
97 self.print_error(|msg| eprintln!("{}", msg))
98 }
99
100 fn to_stdout(self) -> Self {
102 self.print_error(|msg| println!("{}", msg))
103 }
104}
105
106impl<T> LoggableError<T> for anyhow::Result<T> {
107 fn print_error<F: Fn(&str)>(self, fun: F) -> Self {
108 if let Err(ref err) = self {
109 fun(&format!("{:?}", err));
110 }
111 self
112 }
113}
114
115pub trait FatalError<T> {
123 #[track_caller]
130 fn non_fatal(self);
131
132 #[track_caller]
141 fn fatal(self) -> T;
142}
143
144fn discard_result<T>(_arg: anyhow::Result<T>) {}
147
148impl<T> FatalError<T> for anyhow::Result<T> {
149 fn non_fatal(self) {
150 if self.is_err() {
151 discard_result(self.context("a non-fatal error occured").to_log());
152 }
153 }
154
155 fn fatal(self) -> T {
156 if let Ok(val) = self {
157 val
158 } else {
159 self.context("a fatal error occured")
160 .expect("Program terminates")
161 }
162 }
163}
164
165#[derive(Copy, Clone, PartialEq, Serialize, Deserialize, Debug)]
171pub enum ContextType {
172 Screen(ScreenContext),
174 Pty(PtyContext),
176 Plugin(PluginContext),
178 Client(ClientContext),
180 IPCServer(ServerContext),
182 StdinHandler,
183 AsyncTask,
184 PtyWrite(PtyWriteContext),
185 BackgroundJob(BackgroundJobContext),
186 Empty,
189}
190
191impl Display for ContextType {
192 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
193 if let Some((left, right)) = match *self {
194 ContextType::Screen(c) => Some(("screen_thread:", format!("{:?}", c))),
195 ContextType::Pty(c) => Some(("pty_thread:", format!("{:?}", c))),
196 ContextType::Plugin(c) => Some(("plugin_thread:", format!("{:?}", c))),
197 ContextType::Client(c) => Some(("main_thread:", format!("{:?}", c))),
198 ContextType::IPCServer(c) => Some(("ipc_server:", format!("{:?}", c))),
199 ContextType::StdinHandler => Some(("stdin_handler_thread:", "AcceptInput".to_string())),
200 ContextType::AsyncTask => Some(("stream_terminal_bytes:", "AsyncTask".to_string())),
201 ContextType::PtyWrite(c) => Some(("pty_writer_thread:", format!("{:?}", c))),
202 ContextType::BackgroundJob(c) => Some(("background_jobs_thread:", format!("{:?}", c))),
203 ContextType::Empty => None,
204 } {
205 write!(f, "{} {}", left.purple(), right.green())
206 } else {
207 write!(f, "")
208 }
209 }
210}
211
212#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
215pub enum ScreenContext {
216 HandlePtyBytes,
217 PluginBytes,
218 Render,
219 RenderToClients,
220 NewPane,
221 OpenInPlaceEditor,
222 ToggleFloatingPanes,
223 ShowFloatingPanes,
224 HideFloatingPanes,
225 AreFloatingPanesVisible,
226 TogglePaneEmbedOrFloating,
227 HorizontalSplit,
228 VerticalSplit,
229 WriteCharacter,
230 ResizeIncreaseAll,
231 ResizeIncreaseLeft,
232 ResizeIncreaseDown,
233 ResizeIncreaseUp,
234 ResizeIncreaseRight,
235 ResizeDecreaseAll,
236 ResizeDecreaseLeft,
237 ResizeDecreaseDown,
238 ResizeDecreaseUp,
239 ResizeDecreaseRight,
240 ResizeLeft,
241 ResizeRight,
242 ResizeDown,
243 ResizeUp,
244 ResizeIncrease,
245 ResizeDecrease,
246 SwitchFocus,
247 FocusNextPane,
248 FocusPreviousPane,
249 FocusPaneAt,
250 MoveFocusLeft,
251 MoveFocusLeftOrPreviousTab,
252 MoveFocusDown,
253 MoveFocusUp,
254 MoveFocusRight,
255 MoveFocusRightOrNextTab,
256 MovePane,
257 MovePaneBackwards,
258 MovePaneDown,
259 MovePaneUp,
260 MovePaneRight,
261 MovePaneLeft,
262 Exit,
263 ClearScreen,
264 DumpScreen,
265 DumpLayout,
266 SaveSession,
267 EditScrollback,
268 GetPaneScrollback,
269 ScrollUp,
270 ScrollUpAt,
271 ScrollDown,
272 ScrollDownAt,
273 ScrollToBottom,
274 ScrollToTop,
275 PageScrollUp,
276 PageScrollDown,
277 HalfPageScrollUp,
278 HalfPageScrollDown,
279 ClearScroll,
280 CloseFocusedPane,
281 ToggleActiveSyncTab,
282 ToggleActiveTerminalFullscreen,
283 TogglePaneFrames,
284 SetSelectable,
285 ShowPluginCursor,
286 SetInvisibleBorders,
287 SetFixedHeight,
288 SetFixedWidth,
289 ClosePane,
290 HoldPane,
291 UpdatePaneName,
292 UndoRenamePane,
293 NewTab,
294 ApplyLayout,
295 SwitchTabNext,
296 SwitchTabPrev,
297 CloseTab,
298 GoToTab,
299 GoToTabName,
300 UpdateTabName,
301 UndoRenameTab,
302 MoveTabLeft,
303 MoveTabRight,
304 GoToTabWithId,
305 CloseTabWithId,
306 RenameTabWithId,
307 BreakPanesToTabWithId,
308 TerminalResize,
309 TerminalPixelDimensions,
310 TerminalBackgroundColor,
311 TerminalForegroundColor,
312 TerminalColorRegisters,
313 ChangeMode,
314 ChangeModeForAllClients,
315 LeftClick,
316 RightClick,
317 MiddleClick,
318 LeftMouseRelease,
319 RightMouseRelease,
320 MiddleMouseRelease,
321 MouseEvent,
322 Copy,
323 ToggleTab,
324 AddClient,
325 RemoveClient,
326 UpdateSearch,
327 SearchDown,
328 SearchUp,
329 SearchToggleCaseSensitivity,
330 SearchToggleWholeWord,
331 SearchToggleWrap,
332 AddRedPaneFrameColorOverride,
333 ClearPaneFrameColorOverride,
334 SetTabBellFlash,
335 PreviousSwapLayout,
336 NextSwapLayout,
337 OverrideLayout,
338 OverrideLayoutComplete,
339 QueryTabNames,
340 NewTiledPluginPane,
341 StartOrReloadPluginPane,
342 NewFloatingPluginPane,
343 AddPlugin,
344 UpdatePluginLoadingStage,
345 ProgressPluginLoadingOffset,
346 StartPluginLoadingIndication,
347 RequestStateUpdateForPlugins,
348 LaunchOrFocusPlugin,
349 LaunchPlugin,
350 SuppressPane,
351 UnsuppressPane,
352 UnsuppressOrExpandPane,
353 FocusPaneWithId,
354 RenamePane,
355 RenameActivePane,
356 RenameTab,
357 RequestPluginPermissions,
358 BreakPane,
359 BreakPaneRight,
360 BreakPaneLeft,
361 UpdateSessionInfos,
362 UpdateAvailableLayouts,
363 ReplacePane,
364 NewInPlacePluginPane,
365 SerializeLayoutForResurrection,
366 RenameSession,
367 DumpLayoutToPlugin,
368 GetFocusedPaneInfo,
369 GetPaneInfo,
370 GetTabInfo,
371 ListClientsMetadata,
372 ListPanes,
373 ListTabs,
374 GetCurrentTabInfo,
375 Reconfigure,
376 RerunCommandPane,
377 ResizePaneWithId,
378 EditScrollbackForPaneWithId,
379 WriteToPaneId,
380 Paste,
381 SetPaneColor,
382 WriteKeyToPaneId,
383 CopyTextToClipboard,
384 MovePaneWithPaneId,
385 MovePaneWithPaneIdInDirection,
386 ClearScreenForPaneId,
387 ScrollUpInPaneId,
388 ScrollDownInPaneId,
389 ScrollToTopInPaneId,
390 ScrollToBottomInPaneId,
391 PageScrollUpInPaneId,
392 PageScrollDownInPaneId,
393 TogglePaneIdFullscreen,
394 TogglePaneEmbedOrEjectForPaneId,
395 CloseTabWithIndex,
396 BreakPanesToNewTab,
397 BreakPanesToTabWithIndex,
398 ListClientsToPlugin,
399 TogglePanePinned,
400 SetFloatingPanePinned,
401 StackPanes,
402 ChangeFloatingPanesCoordinates,
403 TogglePaneBorderless,
404 SetPaneBorderless,
405 AddHighlightPaneFrameColorOverride,
406 GroupAndUngroupPanes,
407 HighlightAndUnhighlightPanes,
408 FloatMultiplePanes,
409 EmbedMultiplePanes,
410 TogglePaneInGroup,
411 ToggleGroupMarking,
412 SessionSharingStatusChange,
413 SetMouseSelectionSupport,
414 InterceptKeyPresses,
415 ClearKeyPressesIntercepts,
416 ReplacePaneWithExistingPane,
417 AddWatcherClient,
418 RemoveWatcherClient,
419 SetFollowedClient,
420 WatcherTerminalResize,
421 ClearMouseHelpText,
422 SetPluginRegexHighlights,
423 ClearPluginHighlights,
424 DesktopNotificationResponse,
425 SubscribeToPaneRenders,
426 NotifyPaneClosedToSubscribers,
427 ScrollUpWithPaneId,
429 ScrollDownWithPaneId,
430 ScrollToTopWithPaneId,
431 ScrollToBottomWithPaneId,
432 PageScrollUpWithPaneId,
433 PageScrollDownWithPaneId,
434 HalfPageScrollUpWithPaneId,
435 HalfPageScrollDownWithPaneId,
436 ResizeWithPaneId,
437 MovePaneWithPaneIdCli,
438 MovePaneBackwardsWithPaneId,
439 ClearScreenWithPaneId,
440 EditScrollbackWithPaneId,
441 ToggleFullscreenWithPaneId,
442 TogglePaneEmbedOrFloatingWithPaneId,
443 CloseFocusWithPaneId,
444 RenamePaneWithPaneId,
445 UndoRenamePaneWithPaneId,
446 TogglePanePinnedWithPaneId,
447 UndoRenameTabWithTabId,
449 ToggleActiveSyncTabWithTabId,
450 ToggleFloatingPanesWithTabId,
451 PreviousSwapLayoutWithTabId,
452 NextSwapLayoutWithTabId,
453 MoveTabWithTabId,
454 PluginSubscribedToAnsiPaneContents,
455 UpdateBackgroundPluginSubscriptions,
456 BroadcastModeUpdate,
457}
458
459#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
461pub enum PtyContext {
462 SpawnTerminal,
463 OpenInPlaceEditor,
464 SpawnTerminalVertically,
465 SpawnTerminalHorizontally,
466 UpdateActivePane,
467 GoToTab,
468 NewTab,
469 OverrideLayout,
470 ClosePane,
471 CloseTab,
472 ReRunCommandInPane,
473 DropToShellInPane,
474 SpawnInPlaceTerminal,
475 DumpLayout,
476 LogLayoutToHd,
477 SaveSessionToDisk,
478 FillPluginCwd,
479 DumpLayoutToPlugin,
480 ListClientsMetadata,
481 Reconfigure,
482 ListClientsToPlugin,
483 ReportPluginCwd,
484 SendSigintToPaneId,
485 SendSigkillToPaneId,
486 GetPanePid,
487 GetPaneRunningCommand,
488 GetPaneCwd,
489 UpdateAndReportCwds,
490 Exit,
491}
492
493#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
495pub enum PluginContext {
496 Load,
497 LoadBackgroundPlugin,
498 Update,
499 Render,
500 Unload,
501 Reload,
502 ReloadPluginWithId,
503 Resize,
504 Exit,
505 AddClient,
506 RemoveClient,
507 NewTab,
508 OverrideLayout,
509 ApplyCachedEvents,
510 ApplyCachedWorkerMessages,
511 PostMessageToPluginWorker,
512 PostMessageToPlugin,
513 PluginSubscribedToEvents,
514 PermissionRequestResult,
515 DumpLayout,
516 LogLayoutToHd,
517 CliPipe,
518 Message,
519 CachePluginEvents,
520 MessageFromPlugin,
521 UnblockCliPipes,
522 WatchFilesystem,
523 KeybindPipe,
524 DumpLayoutToPlugin,
525 ListClientsMetadata,
526 Reconfigure,
527 FailedToWriteConfigToDisk,
528 ListClientsToPlugin,
529 ChangePluginHostDir,
530 WebServerStarted,
531 FailedToStartWebServer,
532 PaneRenderReport,
533 UserInput,
534 LayoutListUpdate,
535 RequestStateUpdateForPlugin,
536 UpdateSessionSaveTime,
537 GetLastSessionSaveTime,
538 DetectPluginConfigChanges,
539 HighlightClicked,
540}
541
542#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
544pub enum ClientContext {
545 Exit,
546 Error,
547 UnblockInputThread,
548 Render,
549 ServerError,
550 SwitchToMode,
551 Connected,
552 Log,
553 LogError,
554 OwnClientId,
555 StartedParsingStdinQuery,
556 DoneParsingStdinQuery,
557 SwitchSession,
558 SetSynchronisedOutput,
559 UnblockCliPipeInput,
560 CliPipeOutput,
561 QueryTerminalSize,
562 WriteConfigToDisk,
563 StartWebServer,
564 RenamedSession,
565 ConfigFileUpdated,
566}
567
568#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
570pub enum ServerContext {
571 NewClient,
572 Render,
573 UnblockInputThread,
574 ClientExit,
575 RemoveClient,
576 Error,
577 KillSession,
578 DetachSession,
579 AttachClient,
580 ConnStatus,
581 Log,
582 LogError,
583 SwitchSession,
584 UnblockCliPipeInput,
585 CliPipeOutput,
586 AssociatePipeWithClient,
587 DisconnectAllClientsExcept,
588 ChangeMode,
589 ChangeModeForAllClients,
590 Reconfigure,
591 ConfigWrittenToDisk,
592 FailedToWriteConfigToDisk,
593 RebindKeys,
594 StartWebServer,
595 ShareCurrentSession,
596 StopSharingCurrentSession,
597 WebServerStarted,
598 FailedToStartWebServer,
599 SendWebClientsForbidden,
600 ClearMouseHelpText,
601}
602
603#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
604pub enum PtyWriteContext {
605 Write,
606 ResizePty,
607 StartCachingResizes,
608 ApplyCachedResizes,
609 Exit,
610}
611
612#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
613pub enum BackgroundJobContext {
614 DisplayPaneError,
615 AnimatePluginLoading,
616 StopPluginLoadingAnimation,
617 ReadAllSessionInfosOnMachine,
618 ReportSessionInfo,
619 ReportLayoutInfo,
620 RunCommand,
621 WebRequest,
622 ReportPluginList,
623 ListWebSessions,
624 RenderToClients,
625 HighlightPanesWithMessage,
626 QueryZellijWebServerStatus,
627 ClearHelpText,
628 FlashPaneBell,
629 StopFlashPaneBell,
630 FlashTabBell,
631 StopFlashTabBell,
632 Exit,
633}
634
635use thiserror::Error;
636#[derive(Debug, Error)]
637pub enum ZellijError {
638 #[error("could not find command '{command}' for terminal {terminal_id}")]
639 CommandNotFound { terminal_id: u32, command: String },
640
641 #[error("could not determine default editor")]
642 NoEditorFound,
643
644 #[error("failed to allocate another terminal id")]
645 NoMoreTerminalIds,
646
647 #[error("failed to start PTY")]
648 FailedToStartPty,
649
650 #[error(
651 "This version of zellij was built to load the core plugins from
652the globally configured plugin directory. However, a plugin wasn't found:
653
654 Plugin name: '{plugin_path}'
655 Plugin directory: '{plugin_dir}'
656
657If you're a user:
658 Please report this error to the distributor of your current zellij version
659
660If you're a developer:
661 Either make sure to include the plugins with the application (See feature
662 'disable_automatic_asset_installation'), or make them available in the
663 plugin directory.
664
665Possible fix for your problem:
666 Run `zellij setup --dump-plugins`, and optionally point it to your
667 'DATA DIR', visible in e.g. the output of `zellij setup --check`. Without
668 further arguments, it will use the default 'DATA DIR'.
669"
670 )]
671 BuiltinPluginMissing {
672 plugin_path: PathBuf,
673 plugin_dir: PathBuf,
674 #[source]
675 source: anyhow::Error,
676 },
677
678 #[error(
679 "It seems you tried to load the following builtin plugin:
680
681 Plugin name: '{plugin_path}'
682
683This is not a builtin plugin known to this version of zellij. If you were using
684a custom layout, please refer to the layout documentation at:
685
686 https://zellij.dev/documentation/creating-a-layout.html#plugin
687
688If you think this is a bug and the plugin is indeed an internal plugin, please
689open an issue on GitHub:
690
691 https://github.com/zellij-org/zellij/issues
692"
693 )]
694 BuiltinPluginNonexistent {
695 plugin_path: PathBuf,
696 #[source]
697 source: anyhow::Error,
698 },
699
700 #[error("Cannot resize fixed panes")]
703 CantResizeFixedPanes { pane_ids: Vec<(u32, bool)> }, #[error("Pane size remains unchanged")]
706 PaneSizeUnchanged,
707
708 #[error("an error occured")]
709 GenericError { source: anyhow::Error },
710
711 #[error("Client {client_id} is too slow to handle incoming messages")]
712 ClientTooSlow { client_id: u16 },
713
714 #[error("The plugin does not exist")]
715 PluginDoesNotExist,
716
717 #[error("Ran out of room for spans")]
718 RanOutOfRoomForSpans,
719}
720
721#[cfg(not(target_family = "wasm"))]
722pub use not_wasm::*;
723
724#[cfg(not(target_family = "wasm"))]
725mod not_wasm {
726 use super::*;
727 use crate::channels::{SenderWithContext, ASYNCOPENCALLS, OPENCALLS};
728 use miette::{Diagnostic, GraphicalReportHandler, GraphicalTheme, Report};
729 use std::panic::PanicHookInfo;
730 use thiserror::Error as ThisError;
731
732 const MAX_THREAD_CALL_STACK: usize = 6;
735
736 #[derive(Debug, ThisError, Diagnostic)]
737 #[error("{0}{}", self.show_backtrace())]
738 #[diagnostic(help("{}", self.show_help()))]
739 struct Panic(String);
740
741 impl Panic {
742 fn show_backtrace(&self) -> String {
751 if let Ok(var) = std::env::var("RUST_BACKTRACE") {
752 if !var.is_empty() && var != "0" {
753 return format!("\n\nPanic backtrace:\n{:?}", backtrace::Backtrace::new());
754 }
755 }
756 "".into()
757 }
758
759 fn show_help(&self) -> String {
760 format!(
761 "If you are seeing this message, it means that something went wrong.
762
763-> To get additional information, check the log at: {}
764-> To see a backtrace next time, reproduce the error with: RUST_BACKTRACE=1 zellij [...]
765-> To help us fix this, please open an issue: https://github.com/zellij-org/zellij/issues
766
767",
768 crate::consts::ZELLIJ_TMP_LOG_FILE.display().to_string()
769 )
770 }
771 }
772
773 pub fn handle_panic<T>(info: &PanicHookInfo<'_>, sender: Option<&SenderWithContext<T>>)
775 where
776 T: ErrorInstruction + Clone,
777 {
778 use std::{process, thread};
779 let thread = thread::current();
780 let thread = thread.name().unwrap_or("unnamed");
781
782 let msg = match info.payload().downcast_ref::<&'static str>() {
783 Some(s) => Some(*s),
784 None => info.payload().downcast_ref::<String>().map(|s| &**s),
785 }
786 .unwrap_or("An unexpected error occurred!");
787
788 let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
789
790 let mut report: Report = Panic(format!("\u{1b}[0;31m{}\u{1b}[0;0m", msg)).into();
791
792 let mut location_string = String::new();
793 if let Some(location) = info.location() {
794 location_string = format!(
795 "At {}:{}:{}",
796 location.file(),
797 location.line(),
798 location.column()
799 );
800 report = report.wrap_err(location_string.clone());
801 }
802
803 if !err_ctx.is_empty() {
804 report = report.wrap_err(format!("{}", err_ctx));
805 }
806
807 report = report.wrap_err(format!(
808 "Thread '\u{1b}[0;31m{}\u{1b}[0;0m' panicked.",
809 thread
810 ));
811
812 error!(
813 "{}",
814 format!(
815 "Panic occured:
816 thread: {}
817 location: {}
818 message: {}",
819 thread, location_string, msg
820 )
821 );
822
823 if thread == "main" || sender.is_none() {
824 println!("\u{1b}[2J{}", fmt_report(report));
828 process::exit(1);
829 } else {
830 let _ = sender.unwrap().send(T::error(fmt_report(report)));
831 }
832 }
833
834 pub fn get_current_ctx() -> ErrorContext {
835 ASYNCOPENCALLS
836 .try_with(|ctx| *ctx.borrow())
837 .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow()))
838 }
839
840 fn fmt_report(diag: Report) -> String {
841 let mut out = String::new();
842 GraphicalReportHandler::new_themed(GraphicalTheme::unicode())
843 .render_report(&mut out, diag.as_ref())
844 .unwrap();
845 out
846 }
847
848 #[derive(Clone, Copy, Serialize, Deserialize, Debug)]
850 pub struct ErrorContext {
851 calls: [ContextType; MAX_THREAD_CALL_STACK],
852 }
853
854 impl ErrorContext {
855 pub fn new() -> Self {
858 Self {
859 calls: [ContextType::Empty; MAX_THREAD_CALL_STACK],
860 }
861 }
862
863 pub fn is_empty(&self) -> bool {
865 self.calls.iter().all(|c| c == &ContextType::Empty)
866 }
867
868 pub fn add_call(&mut self, call: ContextType) {
870 for ctx in &mut self.calls {
871 if let ContextType::Empty = ctx {
872 *ctx = call;
873 break;
874 }
875 }
876 self.update_thread_ctx()
877 }
878
879 pub fn update_thread_ctx(&self) {
881 ASYNCOPENCALLS
882 .try_with(|ctx| *ctx.borrow_mut() = *self)
883 .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self));
884 }
885 }
886
887 impl Default for ErrorContext {
888 fn default() -> Self {
889 Self::new()
890 }
891 }
892
893 impl Display for ErrorContext {
894 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
895 writeln!(f, "Originating Thread(s)")?;
896 for (index, ctx) in self.calls.iter().enumerate() {
897 if *ctx == ContextType::Empty {
898 break;
899 }
900 writeln!(f, "\t\u{1b}[0;0m{}. {}", index + 1, ctx)?;
901 }
902 Ok(())
903 }
904 }
905
906 pub trait ToAnyhow<U> {
909 fn to_anyhow(self) -> anyhow::Result<U>;
910 }
911
912 impl<T: std::fmt::Debug, U> ToAnyhow<U>
920 for Result<U, crate::channels::SendError<(T, ErrorContext)>>
921 {
922 fn to_anyhow(self) -> anyhow::Result<U> {
923 match self {
924 Ok(val) => anyhow::Ok(val),
925 Err(e) => {
926 let (msg, context) = e.into_inner();
927 if *crate::consts::DEBUG_MODE.get().unwrap_or(&true) {
928 Err(anyhow::anyhow!(
929 "failed to send message to channel: {:#?}",
930 msg
931 ))
932 .with_context(|| context.to_string())
933 } else {
934 Err(anyhow::anyhow!("failed to send message to channel"))
935 .with_context(|| context.to_string())
936 }
937 },
938 }
939 }
940 }
941
942 impl<U> ToAnyhow<U> for Result<U, std::sync::PoisonError<U>> {
943 fn to_anyhow(self) -> anyhow::Result<U> {
944 match self {
945 Ok(val) => anyhow::Ok(val),
946 Err(e) => {
947 if *crate::consts::DEBUG_MODE.get().unwrap_or(&true) {
948 Err(anyhow::anyhow!("cannot acquire poisoned lock for {e:#?}"))
949 } else {
950 Err(anyhow::anyhow!("cannot acquire poisoned lock"))
951 }
952 },
953 }
954 }
955 }
956}