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 ForwardHostQuery,
314 ForwardedReplyFromHost,
315 ResumePaneAfterForward,
316 HostTerminalThemeChanged,
317 SetDarkTheme,
318 SetLightTheme,
319 ToggleTheme,
320 ChangeMode,
321 ChangeModeForAllClients,
322 LeftClick,
323 RightClick,
324 MiddleClick,
325 LeftMouseRelease,
326 RightMouseRelease,
327 MiddleMouseRelease,
328 MouseEvent,
329 Copy,
330 ToggleTab,
331 AddClient,
332 RemoveClient,
333 UpdateSearch,
334 SearchDown,
335 SearchUp,
336 SearchToggleCaseSensitivity,
337 SearchToggleWholeWord,
338 SearchToggleWrap,
339 AddRedPaneFrameColorOverride,
340 ClearPaneFrameColorOverride,
341 SetTabBellFlash,
342 PreviousSwapLayout,
343 NextSwapLayout,
344 OverrideLayout,
345 OverrideLayoutComplete,
346 QueryTabNames,
347 NewTiledPluginPane,
348 StartOrReloadPluginPane,
349 NewFloatingPluginPane,
350 AddPlugin,
351 UpdatePluginLoadingStage,
352 ProgressPluginLoadingOffset,
353 StartPluginLoadingIndication,
354 RequestStateUpdateForPlugins,
355 LaunchOrFocusPlugin,
356 LaunchPlugin,
357 SuppressPane,
358 UnsuppressPane,
359 UnsuppressOrExpandPane,
360 FocusPaneWithId,
361 RenamePane,
362 RenameActivePane,
363 RenameTab,
364 RequestPluginPermissions,
365 BreakPane,
366 BreakPaneRight,
367 BreakPaneLeft,
368 UpdateSessionInfos,
369 UpdateAvailableLayouts,
370 ReplacePane,
371 NewInPlacePluginPane,
372 SerializeLayoutForResurrection,
373 RenameSession,
374 DumpLayoutToPlugin,
375 GetFocusedPaneInfo,
376 GetPaneInfo,
377 GetTabInfo,
378 ListClientsMetadata,
379 ListPanes,
380 ListTabs,
381 GetCurrentTabInfo,
382 Reconfigure,
383 RerunCommandPane,
384 ResizePaneWithId,
385 EditScrollbackForPaneWithId,
386 WriteToPaneId,
387 Paste,
388 SetPaneColor,
389 WriteKeyToPaneId,
390 CopyTextToClipboard,
391 MovePaneWithPaneId,
392 MovePaneWithPaneIdInDirection,
393 ClearScreenForPaneId,
394 ScrollUpInPaneId,
395 ScrollDownInPaneId,
396 ScrollToTopInPaneId,
397 ScrollToBottomInPaneId,
398 PageScrollUpInPaneId,
399 PageScrollDownInPaneId,
400 TogglePaneIdFullscreen,
401 TogglePaneEmbedOrEjectForPaneId,
402 CloseTabWithIndex,
403 BreakPanesToNewTab,
404 BreakPanesToTabWithIndex,
405 ListClientsToPlugin,
406 TogglePanePinned,
407 SetFloatingPanePinned,
408 StackPanes,
409 ChangeFloatingPanesCoordinates,
410 TogglePaneBorderless,
411 SetPaneBorderless,
412 AddHighlightPaneFrameColorOverride,
413 GroupAndUngroupPanes,
414 HighlightAndUnhighlightPanes,
415 FloatMultiplePanes,
416 EmbedMultiplePanes,
417 TogglePaneInGroup,
418 ToggleGroupMarking,
419 SessionSharingStatusChange,
420 SetMouseSelectionSupport,
421 InterceptKeyPresses,
422 ClearKeyPressesIntercepts,
423 ReplacePaneWithExistingPane,
424 AddWatcherClient,
425 RemoveWatcherClient,
426 SetFollowedClient,
427 WatcherTerminalResize,
428 ClearMouseHelpText,
429 SetPluginRegexHighlights,
430 ClearPluginHighlights,
431 DesktopNotificationResponse,
432 SubscribeToPaneRenders,
433 NotifyPaneClosedToSubscribers,
434 ScrollUpWithPaneId,
436 ScrollDownWithPaneId,
437 ScrollToTopWithPaneId,
438 ScrollToBottomWithPaneId,
439 PageScrollUpWithPaneId,
440 PageScrollDownWithPaneId,
441 HalfPageScrollUpWithPaneId,
442 HalfPageScrollDownWithPaneId,
443 ResizeWithPaneId,
444 MovePaneWithPaneIdCli,
445 MovePaneBackwardsWithPaneId,
446 ClearScreenWithPaneId,
447 EditScrollbackWithPaneId,
448 ToggleFullscreenWithPaneId,
449 TogglePaneEmbedOrFloatingWithPaneId,
450 CloseFocusWithPaneId,
451 RenamePaneWithPaneId,
452 UndoRenamePaneWithPaneId,
453 TogglePanePinnedWithPaneId,
454 UndoRenameTabWithTabId,
456 ToggleActiveSyncTabWithTabId,
457 ToggleFloatingPanesWithTabId,
458 PreviousSwapLayoutWithTabId,
459 NextSwapLayoutWithTabId,
460 MoveTabWithTabId,
461 PluginSubscribedToAnsiPaneContents,
462 UpdateBackgroundPluginSubscriptions,
463 BroadcastModeUpdate,
464}
465
466#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
468pub enum PtyContext {
469 SpawnTerminal,
470 OpenInPlaceEditor,
471 SpawnTerminalVertically,
472 SpawnTerminalHorizontally,
473 UpdateActivePane,
474 GoToTab,
475 NewTab,
476 OverrideLayout,
477 ClosePane,
478 CloseTab,
479 ReRunCommandInPane,
480 DropToShellInPane,
481 SpawnInPlaceTerminal,
482 DumpLayout,
483 LogLayoutToHd,
484 SaveSessionToDisk,
485 FillPluginCwd,
486 DumpLayoutToPlugin,
487 ListClientsMetadata,
488 Reconfigure,
489 ListClientsToPlugin,
490 ReportPluginCwd,
491 SendSigintToPaneId,
492 SendSigkillToPaneId,
493 GetPanePid,
494 GetPaneRunningCommand,
495 GetPaneCwd,
496 UpdateAndReportCwds,
497 NotifyCwdFromOsc7,
498 Exit,
499}
500
501#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
503pub enum PluginContext {
504 Load,
505 LoadBackgroundPlugin,
506 Update,
507 Render,
508 Unload,
509 Reload,
510 ReloadPluginWithId,
511 Resize,
512 Exit,
513 AddClient,
514 RemoveClient,
515 NewTab,
516 OverrideLayout,
517 ApplyCachedEvents,
518 ApplyCachedWorkerMessages,
519 PostMessageToPluginWorker,
520 PostMessageToPlugin,
521 PluginSubscribedToEvents,
522 PermissionRequestResult,
523 DumpLayout,
524 LogLayoutToHd,
525 CliPipe,
526 Message,
527 CachePluginEvents,
528 MessageFromPlugin,
529 UnblockCliPipes,
530 WatchFilesystem,
531 KeybindPipe,
532 DumpLayoutToPlugin,
533 ListClientsMetadata,
534 Reconfigure,
535 FailedToWriteConfigToDisk,
536 ListClientsToPlugin,
537 ChangePluginHostDir,
538 WebServerStarted,
539 FailedToStartWebServer,
540 PaneRenderReport,
541 UserInput,
542 LayoutListUpdate,
543 RequestStateUpdateForPlugin,
544 UpdateSessionSaveTime,
545 GetLastSessionSaveTime,
546 DetectPluginConfigChanges,
547 HighlightClicked,
548}
549
550#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
552pub enum ClientContext {
553 Exit,
554 Error,
555 UnblockInputThread,
556 Render,
557 ServerError,
558 SwitchToMode,
559 Connected,
560 Log,
561 LogError,
562 OwnClientId,
563 SwitchSession,
564 SetSynchronisedOutput,
565 UnblockCliPipeInput,
566 CliPipeOutput,
567 QueryTerminalSize,
568 WriteConfigToDisk,
569 StartWebServer,
570 RenamedSession,
571 ConfigFileUpdated,
572 ForwardQueryToHost,
573}
574
575#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
577pub enum ServerContext {
578 NewClient,
579 Render,
580 UnblockInputThread,
581 ClientExit,
582 RemoveClient,
583 Error,
584 KillSession,
585 DetachSession,
586 AttachClient,
587 ConnStatus,
588 Log,
589 LogError,
590 SwitchSession,
591 UnblockCliPipeInput,
592 CliPipeOutput,
593 AssociatePipeWithClient,
594 DisconnectAllClientsExcept,
595 ChangeMode,
596 ChangeModeForAllClients,
597 Reconfigure,
598 ConfigWrittenToDisk,
599 FailedToWriteConfigToDisk,
600 RebindKeys,
601 StartWebServer,
602 ShareCurrentSession,
603 StopSharingCurrentSession,
604 WebServerStarted,
605 FailedToStartWebServer,
606 SendWebClientsForbidden,
607 ClearMouseHelpText,
608 ForwardQueryToHost,
609}
610
611#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
612pub enum PtyWriteContext {
613 Write,
614 ResizePty,
615 StartCachingResizes,
616 ApplyCachedResizes,
617 Exit,
618}
619
620#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
621pub enum BackgroundJobContext {
622 DisplayPaneError,
623 AnimatePluginLoading,
624 StopPluginLoadingAnimation,
625 ReportSessionInfo,
626 ReportLayoutInfo,
627 RunCommand,
628 WebRequest,
629 ReportPluginList,
630 ListWebSessions,
631 RenderToClients,
632 HighlightPanesWithMessage,
633 QueryZellijWebServerStatus,
634 ClearHelpText,
635 FlashPaneBell,
636 StopFlashPaneBell,
637 FlashTabBell,
638 StopFlashTabBell,
639 Exit,
640}
641
642use thiserror::Error;
643#[derive(Debug, Error)]
644pub enum ZellijError {
645 #[error("could not find command '{command}' for terminal {terminal_id}")]
646 CommandNotFound { terminal_id: u32, command: String },
647
648 #[error("could not determine default editor")]
649 NoEditorFound,
650
651 #[error("failed to allocate another terminal id")]
652 NoMoreTerminalIds,
653
654 #[error("failed to start PTY")]
655 FailedToStartPty,
656
657 #[error(
658 "This version of zellij was built to load the core plugins from
659the globally configured plugin directory. However, a plugin wasn't found:
660
661 Plugin name: '{plugin_path}'
662 Plugin directory: '{plugin_dir}'
663
664If you're a user:
665 Please report this error to the distributor of your current zellij version
666
667If you're a developer:
668 Either make sure to include the plugins with the application (See feature
669 'disable_automatic_asset_installation'), or make them available in the
670 plugin directory.
671
672Possible fix for your problem:
673 Run `zellij setup --dump-plugins`, and optionally point it to your
674 'DATA DIR', visible in e.g. the output of `zellij setup --check`. Without
675 further arguments, it will use the default 'DATA DIR'.
676"
677 )]
678 BuiltinPluginMissing {
679 plugin_path: PathBuf,
680 plugin_dir: PathBuf,
681 #[source]
682 source: anyhow::Error,
683 },
684
685 #[error(
686 "It seems you tried to load the following builtin plugin:
687
688 Plugin name: '{plugin_path}'
689
690This is not a builtin plugin known to this version of zellij. If you were using
691a custom layout, please refer to the layout documentation at:
692
693 https://zellij.dev/documentation/creating-a-layout.html#plugin
694
695If you think this is a bug and the plugin is indeed an internal plugin, please
696open an issue on GitHub:
697
698 https://github.com/zellij-org/zellij/issues
699"
700 )]
701 BuiltinPluginNonexistent {
702 plugin_path: PathBuf,
703 #[source]
704 source: anyhow::Error,
705 },
706
707 #[error("Cannot resize fixed panes")]
710 CantResizeFixedPanes { pane_ids: Vec<(u32, bool)> }, #[error("Pane size remains unchanged")]
713 PaneSizeUnchanged,
714
715 #[error("an error occured")]
716 GenericError { source: anyhow::Error },
717
718 #[error("Client {client_id} is too slow to handle incoming messages")]
719 ClientTooSlow { client_id: u16 },
720
721 #[error("The plugin does not exist")]
722 PluginDoesNotExist,
723
724 #[error("Ran out of room for spans")]
725 RanOutOfRoomForSpans,
726}
727
728#[cfg(not(target_family = "wasm"))]
729pub use not_wasm::*;
730
731#[cfg(not(target_family = "wasm"))]
732mod not_wasm {
733 use super::*;
734 use crate::channels::{SenderWithContext, ASYNCOPENCALLS, OPENCALLS};
735 use miette::{Diagnostic, GraphicalReportHandler, GraphicalTheme, Report};
736 use std::panic::PanicHookInfo;
737 use thiserror::Error as ThisError;
738
739 const MAX_THREAD_CALL_STACK: usize = 6;
742
743 #[derive(Debug, ThisError, Diagnostic)]
744 #[error("{0}{}", self.show_backtrace())]
745 #[diagnostic(help("{}", self.show_help()))]
746 struct Panic(String);
747
748 impl Panic {
749 fn show_backtrace(&self) -> String {
758 if let Ok(var) = std::env::var("RUST_BACKTRACE") {
759 if !var.is_empty() && var != "0" {
760 return format!("\n\nPanic backtrace:\n{:?}", backtrace::Backtrace::new());
761 }
762 }
763 "".into()
764 }
765
766 fn show_help(&self) -> String {
767 format!(
768 "If you are seeing this message, it means that something went wrong.
769
770-> To get additional information, check the log at: {}
771-> To see a backtrace next time, reproduce the error with: RUST_BACKTRACE=1 zellij [...]
772-> To help us fix this, please open an issue: https://github.com/zellij-org/zellij/issues
773
774",
775 crate::consts::ZELLIJ_TMP_LOG_FILE.display().to_string()
776 )
777 }
778 }
779
780 pub fn handle_panic<T>(info: &PanicHookInfo<'_>, sender: Option<&SenderWithContext<T>>)
782 where
783 T: ErrorInstruction + Clone,
784 {
785 use std::{process, thread};
786 let thread = thread::current();
787 let thread = thread.name().unwrap_or("unnamed");
788
789 let msg = match info.payload().downcast_ref::<&'static str>() {
790 Some(s) => Some(*s),
791 None => info.payload().downcast_ref::<String>().map(|s| &**s),
792 }
793 .unwrap_or("An unexpected error occurred!");
794
795 let err_ctx = OPENCALLS.with(|ctx| *ctx.borrow());
796
797 let mut report: Report = Panic(format!("\u{1b}[0;31m{}\u{1b}[0;0m", msg)).into();
798
799 let mut location_string = String::new();
800 if let Some(location) = info.location() {
801 location_string = format!(
802 "At {}:{}:{}",
803 location.file(),
804 location.line(),
805 location.column()
806 );
807 report = report.wrap_err(location_string.clone());
808 }
809
810 if !err_ctx.is_empty() {
811 report = report.wrap_err(format!("{}", err_ctx));
812 }
813
814 report = report.wrap_err(format!(
815 "Thread '\u{1b}[0;31m{}\u{1b}[0;0m' panicked.",
816 thread
817 ));
818
819 error!(
820 "{}",
821 format!(
822 "Panic occured:
823 thread: {}
824 location: {}
825 message: {}",
826 thread, location_string, msg
827 )
828 );
829
830 if thread == "main" || sender.is_none() {
831 println!("\u{1b}[2J{}", fmt_report(report));
835 process::exit(1);
836 } else {
837 let _ = sender.unwrap().send(T::error(fmt_report(report)));
838 }
839 }
840
841 pub fn get_current_ctx() -> ErrorContext {
842 ASYNCOPENCALLS
843 .try_with(|ctx| *ctx.borrow())
844 .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow()))
845 }
846
847 fn fmt_report(diag: Report) -> String {
848 let mut out = String::new();
849 GraphicalReportHandler::new_themed(GraphicalTheme::unicode())
850 .render_report(&mut out, diag.as_ref())
851 .unwrap();
852 out
853 }
854
855 #[derive(Clone, Copy, Serialize, Deserialize, Debug)]
857 pub struct ErrorContext {
858 calls: [ContextType; MAX_THREAD_CALL_STACK],
859 }
860
861 impl ErrorContext {
862 pub fn new() -> Self {
865 Self {
866 calls: [ContextType::Empty; MAX_THREAD_CALL_STACK],
867 }
868 }
869
870 pub fn is_empty(&self) -> bool {
872 self.calls.iter().all(|c| c == &ContextType::Empty)
873 }
874
875 pub fn add_call(&mut self, call: ContextType) {
877 for ctx in &mut self.calls {
878 if let ContextType::Empty = ctx {
879 *ctx = call;
880 break;
881 }
882 }
883 self.update_thread_ctx()
884 }
885
886 pub fn update_thread_ctx(&self) {
888 ASYNCOPENCALLS
889 .try_with(|ctx| *ctx.borrow_mut() = *self)
890 .unwrap_or_else(|_| OPENCALLS.with(|ctx| *ctx.borrow_mut() = *self));
891 }
892 }
893
894 impl Default for ErrorContext {
895 fn default() -> Self {
896 Self::new()
897 }
898 }
899
900 impl Display for ErrorContext {
901 fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
902 writeln!(f, "Originating Thread(s)")?;
903 for (index, ctx) in self.calls.iter().enumerate() {
904 if *ctx == ContextType::Empty {
905 break;
906 }
907 writeln!(f, "\t\u{1b}[0;0m{}. {}", index + 1, ctx)?;
908 }
909 Ok(())
910 }
911 }
912
913 pub trait ToAnyhow<U> {
916 fn to_anyhow(self) -> anyhow::Result<U>;
917 }
918
919 impl<T: std::fmt::Debug, U> ToAnyhow<U>
927 for Result<U, crate::channels::SendError<(T, ErrorContext)>>
928 {
929 fn to_anyhow(self) -> anyhow::Result<U> {
930 match self {
931 Ok(val) => anyhow::Ok(val),
932 Err(e) => {
933 let (msg, context) = e.into_inner();
934 if *crate::consts::DEBUG_MODE.get().unwrap_or(&true) {
935 Err(anyhow::anyhow!(
936 "failed to send message to channel: {:#?}",
937 msg
938 ))
939 .with_context(|| context.to_string())
940 } else {
941 Err(anyhow::anyhow!("failed to send message to channel"))
942 .with_context(|| context.to_string())
943 }
944 },
945 }
946 }
947 }
948
949 impl<U> ToAnyhow<U> for Result<U, std::sync::PoisonError<U>> {
950 fn to_anyhow(self) -> anyhow::Result<U> {
951 match self {
952 Ok(val) => anyhow::Ok(val),
953 Err(e) => {
954 if *crate::consts::DEBUG_MODE.get().unwrap_or(&true) {
955 Err(anyhow::anyhow!("cannot acquire poisoned lock for {e:#?}"))
956 } else {
957 Err(anyhow::anyhow!("cannot acquire poisoned lock"))
958 }
959 },
960 }
961 }
962 }
963}