1use schemars::JsonSchema;
11use serde::{Deserialize, Serialize};
12use systemprompt_identifiers::{
13 ContentHash, DocumentCommentId, DocumentEditSessionId, DocumentId, DocumentNodeId,
14 DocumentProposalId, DocumentRevisionId, DriveDeviceId, UserId,
15};
16
17use super::access::{ActorKind, ProposalStatus, RevisionOriginKind};
18use super::awareness::DocumentAwarenessEvent;
19pub use super::collab_events::{DocumentCollabDegradedEvent, DocumentCollabUpdateEvent};
20use super::comment_events::{
21 DocumentCommentDeletedEvent, DocumentCommentMentionedEvent, DocumentCommentUpdatedEvent,
22};
23use super::drive::{PresenceSource, PresenceState};
24
25pub use super::sharing_events::{
26 DocumentAccessRequestedEvent, DocumentOwnershipTransferredEvent, DocumentSharedEvent,
27 SHARED_EVENT_VERSION,
28};
29
30#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]
31pub enum DocumentEventName {
32 #[serde(rename = "document.revision.created")]
33 RevisionCreated,
34 #[serde(rename = "document.presence")]
35 Presence,
36 #[serde(rename = "document.lock")]
37 Lock,
38 #[serde(rename = "document.conflict")]
39 Conflict,
40 #[serde(rename = "document.collab.update")]
41 CollabUpdate,
42 #[serde(rename = "document.comment.created")]
43 CommentCreated,
44 #[serde(rename = "document.comment.resolved")]
45 CommentResolved,
46 #[serde(rename = "document.comment.updated")]
47 CommentUpdated,
48 #[serde(rename = "document.comment.deleted")]
49 CommentDeleted,
50 #[serde(rename = "document.comment.mentioned")]
51 CommentMentioned,
52 #[serde(rename = "document.shared")]
53 Shared,
54 #[serde(rename = "document.ownership_transferred")]
55 OwnershipTransferred,
56 #[serde(rename = "document.proposal.opened")]
57 ProposalOpened,
58 #[serde(rename = "document.proposal.accepted")]
59 ProposalAccepted,
60 #[serde(rename = "document.proposal.rejected")]
61 ProposalRejected,
62 #[serde(rename = "document.proposal.superseded")]
63 ProposalSuperseded,
64 #[serde(rename = "document.awareness")]
65 Awareness,
66 #[serde(rename = "document.access_requested")]
67 AccessRequested,
68 #[serde(rename = "document.collab.degraded")]
69 CollabDegraded,
70}
71
72impl DocumentEventName {
73 pub const ALL: [Self; 19] = [
74 Self::RevisionCreated,
75 Self::Presence,
76 Self::Lock,
77 Self::Conflict,
78 Self::CollabUpdate,
79 Self::CommentCreated,
80 Self::CommentResolved,
81 Self::CommentUpdated,
82 Self::CommentDeleted,
83 Self::CommentMentioned,
84 Self::Shared,
85 Self::OwnershipTransferred,
86 Self::ProposalOpened,
87 Self::ProposalAccepted,
88 Self::ProposalRejected,
89 Self::ProposalSuperseded,
90 Self::Awareness,
91 Self::AccessRequested,
92 Self::CollabDegraded,
93 ];
94
95 #[must_use]
96 pub const fn as_str(self) -> &'static str {
97 match self {
98 Self::RevisionCreated => DocumentRevisionCreatedEvent::NAME,
99 Self::Presence => DocumentPresenceEvent::NAME,
100 Self::Lock => DocumentLockEvent::NAME,
101 Self::Conflict => DocumentConflictEvent::NAME,
102 Self::CollabUpdate => DocumentCollabUpdateEvent::NAME,
103 Self::CommentCreated => DocumentCommentCreatedEvent::NAME,
104 Self::CommentResolved => DocumentCommentResolvedEvent::NAME,
105 Self::CommentUpdated => DocumentCommentUpdatedEvent::NAME,
106 Self::CommentDeleted => DocumentCommentDeletedEvent::NAME,
107 Self::CommentMentioned => DocumentCommentMentionedEvent::NAME,
108 Self::Shared => DocumentSharedEvent::NAME,
109 Self::OwnershipTransferred => DocumentOwnershipTransferredEvent::NAME,
110 Self::ProposalOpened => DocumentProposalEvent::OPENED,
111 Self::ProposalAccepted => DocumentProposalEvent::ACCEPTED,
112 Self::ProposalRejected => DocumentProposalEvent::REJECTED,
113 Self::ProposalSuperseded => DocumentProposalEvent::SUPERSEDED,
114 Self::Awareness => DocumentAwarenessEvent::NAME,
115 Self::AccessRequested => DocumentAccessRequestedEvent::NAME,
116 Self::CollabDegraded => DocumentCollabDegradedEvent::NAME,
117 }
118 }
119
120 #[must_use]
121 pub fn from_name(name: &str) -> Option<Self> {
122 Self::ALL.into_iter().find(|event| event.as_str() == name)
123 }
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
127pub struct DocumentRevisionCreatedEvent {
128 pub document_id: DocumentId,
129 pub revision_id: DocumentRevisionId,
130 #[serde(default, skip_serializing_if = "Option::is_none")]
131 pub parent_revision_id: Option<DocumentRevisionId>,
132 pub content_sha256: ContentHash,
133 pub author_id: UserId,
134 pub actor_kind: ActorKind,
135 pub origin_kind: RevisionOriginKind,
136}
137
138impl DocumentRevisionCreatedEvent {
139 pub const NAME: &'static str = "document.revision.created";
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
143pub struct DocumentPresenceEvent {
144 pub document_id: DocumentId,
145 pub user_id: UserId,
146 pub state: PresenceState,
147 pub source_kind: PresenceSource,
148 pub source_ref: String,
149 #[serde(default, skip_serializing_if = "Option::is_none")]
151 pub cursor: Option<serde_json::Value>,
152}
153
154impl DocumentPresenceEvent {
155 pub const NAME: &'static str = "document.presence";
156}
157
158#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
159pub struct DocumentLockEvent {
160 pub document_id: DocumentId,
161 pub locked: bool,
162 #[serde(default, skip_serializing_if = "Option::is_none")]
163 pub holder_id: Option<UserId>,
164 #[serde(default, skip_serializing_if = "Option::is_none")]
165 pub edit_session_id: Option<DocumentEditSessionId>,
166}
167
168impl DocumentLockEvent {
169 pub const NAME: &'static str = "document.lock";
170}
171
172#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
173pub struct DocumentConflictEvent {
174 pub document_id: DocumentId,
175 pub path: String,
176 pub head_revision_id: DocumentRevisionId,
177 #[serde(default, skip_serializing_if = "Option::is_none")]
178 pub base_revision_id: Option<DocumentRevisionId>,
179 #[serde(default, skip_serializing_if = "Option::is_none")]
180 pub device_id: Option<DriveDeviceId>,
181}
182
183impl DocumentConflictEvent {
184 pub const NAME: &'static str = "document.conflict";
185}
186
187#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
188pub struct DocumentCommentCreatedEvent {
189 pub document_id: DocumentId,
190 pub comment_id: DocumentCommentId,
191 pub node_id: DocumentNodeId,
192 #[serde(default, skip_serializing_if = "Option::is_none")]
193 pub parent_id: Option<DocumentCommentId>,
194 pub author_id: UserId,
195 #[serde(default, skip_serializing_if = "Vec::is_empty")]
196 pub mentions: Vec<UserId>,
197}
198
199impl DocumentCommentCreatedEvent {
200 pub const NAME: &'static str = "document.comment.created";
201}
202
203#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
204pub struct DocumentCommentResolvedEvent {
205 pub document_id: DocumentId,
206 pub comment_id: DocumentCommentId,
207 pub resolved_by: UserId,
208}
209
210impl DocumentCommentResolvedEvent {
211 pub const NAME: &'static str = "document.comment.resolved";
212}
213
214#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
217pub struct DocumentProposalEvent {
218 pub document_id: DocumentId,
219 pub proposal_id: DocumentProposalId,
220 pub status: ProposalStatus,
221 pub proposer_id: UserId,
222 pub base_revision_id: DocumentRevisionId,
223 pub proposed_revision_id: DocumentRevisionId,
224 #[serde(default, skip_serializing_if = "Option::is_none")]
225 pub reviewer_id: Option<UserId>,
226 #[serde(default, skip_serializing_if = "Option::is_none")]
227 pub landed_revision_id: Option<DocumentRevisionId>,
228 #[serde(default, skip_serializing_if = "Option::is_none")]
229 pub message: Option<String>,
230}
231
232impl DocumentProposalEvent {
233 pub const OPENED: &'static str = "document.proposal.opened";
234 pub const ACCEPTED: &'static str = "document.proposal.accepted";
235 pub const REJECTED: &'static str = "document.proposal.rejected";
236 pub const SUPERSEDED: &'static str = "document.proposal.superseded";
237
238 #[must_use]
239 pub const fn name_of(status: ProposalStatus) -> &'static str {
240 match status {
241 ProposalStatus::Open => Self::OPENED,
242 ProposalStatus::Accepted => Self::ACCEPTED,
243 ProposalStatus::Rejected => Self::REJECTED,
244 ProposalStatus::Superseded => Self::SUPERSEDED,
245 }
246 }
247}