1package domain
2
3import "time"
4
5// NoticeSeverity ranks a user-facing notification.
6//
7// Its numeric values persist in JSON notice records and are a protocol/domain
8// contract. Add values only at the end; never reorder existing values.
9type NoticeSeverity uint8
10
11const (
12 NoticeInfo NoticeSeverity = iota
13 NoticeWarn
14 NoticeError
15)
16
17// NoticeCode identifies a failure meaning, not a call site.
18//
19// Its numeric values persist in JSON notice records and are a protocol/domain
20// contract. Add values only at the end; never reorder existing values.
21type NoticeCode uint16
22
23const (
24 NoticeInternal NoticeCode = iota
25 NoticePaneSpawn
26 NoticeTabSpawn
27 NoticeFloatingSpawn
28 NoticeSessionSpawn
29 NoticeLayoutTooSmall
30 NoticePaneNotFound
31 NoticeSessionUnavailable
32 NoticePersistDisabled
33 NoticeSnapshotWrite
34 NoticeSnapshotRestore
35 NoticeSnapshotSaturated
36 NoticePersistDelete
37 NoticeConfigReload
38 NoticeInputDropped
39 NoticeResizeFailed
40 NoticeClipboard
41 NoticeClipboardTooLarge
42 NoticeAutoResume
43 NoticeConnection
44 NoticeUser
45 NoticeNavigationInventory
46 NoticeRemoteObservation
47 NoticeSessionKill
48
49 // noticeCodeLimit is an append-only declaration sentinel, not a persisted
50 // valid notice code. Keep it last when adding a NoticeCode.
51 noticeCodeLimit
52)
53
54var noticeSlugs = map[NoticeCode]string{
55 NoticeInternal: "internal",
56 NoticePaneSpawn: "pane-spawn",
57 NoticeTabSpawn: "tab-spawn",
58 NoticeFloatingSpawn: "floating-spawn",
59 NoticeSessionSpawn: "session-spawn",
60 NoticeLayoutTooSmall: "layout-too-small",
61 NoticePaneNotFound: "pane-not-found",
62 NoticeSessionUnavailable: "session-unavailable",
63 NoticePersistDisabled: "persist-disabled",
64 NoticeSnapshotWrite: "snapshot-write",
65 NoticeSnapshotRestore: "snapshot-restore",
66 NoticeSnapshotSaturated: "snapshot-saturated",
67 NoticePersistDelete: "persist-delete",
68 NoticeConfigReload: "config-reload",
69 NoticeInputDropped: "input-dropped",
70 NoticeResizeFailed: "resize-failed",
71 NoticeClipboard: "clipboard",
72 NoticeClipboardTooLarge: "clipboard-too-large",
73 NoticeAutoResume: "auto-resume",
74 NoticeConnection: "connection",
75 NoticeUser: "user",
76 NoticeNavigationInventory: "navigation-inventory",
77 NoticeRemoteObservation: "remote-observation",
78 NoticeSessionKill: "session-kill",
79}
80
81func (c NoticeCode) String() string {
82 if s, ok := noticeSlugs[c]; ok {
83 return s
84 }
85 return "unknown"
86}
87
88// UserError is an error whose message is written for the attached user.
89// The wrapped cause is shown only in yank details, never in the toast.
90type UserError struct {
91 Code NoticeCode
92 Severity NoticeSeverity
93 Msg string
94 Err error
95}
96
97func (e *UserError) Error() string {
98 if e.Err == nil {
99 return e.Msg
100 }
101 return e.Msg + ": " + e.Err.Error()
102}
103
104func (e *UserError) Unwrap() error { return e.Err }
105
106// UserErr wraps err as an error-severity user-facing failure.
107func UserErr(code NoticeCode, msg string, err error) *UserError {
108 return &UserError{Code: code, Severity: NoticeError, Msg: msg, Err: err}
109}
110
111// UserWarn wraps err as a warn-severity user-facing failure.
112func UserWarn(code NoticeCode, msg string, err error) *UserError {
113 return &UserError{Code: code, Severity: NoticeWarn, Msg: msg, Err: err}
114}
115
116// Notification is one entry in the daemon's user-facing notice history.
117type Notification struct {
118 Code NoticeCode
119 Severity NoticeSeverity
120 Message string
121 Details string // formatted error chain for yank
122 Time time.Time
123 Count int // >1 when coalesced
124 SessionID SessionID // "" = daemon-global
125 // Scope narrows the subject below the session, such as one remote host.
126 // Notices with the same code, session, and scope replace each other.
127 Scope string `json:",omitempty"`
128}
129
130// Subject is the stable identity of what a notice is about. Client toasts
131// use it so a newer notice for the same subject replaces the visible one;
132// the daemon's notice coalescing keeps its own rule (daemon toastNoticeID).
133func (n Notification) Subject() string {
134 return n.Code.String() + "/" + string(n.SessionID) + "/" + n.Scope
135}