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