1use std::time::Duration;
4use std::{panic::AssertUnwindSafe, panic::catch_unwind};
5
6use crate::{DavBackendErrorKind, DavPath, DavRequestHead};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub enum DavOperation {
11 Options,
12 Propfind,
13 Proppatch,
14 Get,
15 Head,
16 Post,
17 Put,
18 Patch,
19 Mkcol,
20 Delete,
21 Copy,
22 Move,
23 Lock,
24 Unlock,
25 Acl,
26 Report,
27 VersionControl,
28 Checkout,
29 Checkin,
30 Uncheckout,
31 Mkworkspace,
32 Update,
33 Label,
34 Merge,
35 BaselineControl,
36 Mkactivity,
37 Search,
38 Orderpatch,
39 Mkredirectref,
40 Updateredirectref,
41 Bind,
42 Unbind,
43 Rebind,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum DavEventOutcome {
49 Succeeded {
50 status: u16,
52 },
53 Failed {
54 status: u16,
56 backend_error: Option<DavBackendErrorKind>,
57 },
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq)]
62pub enum DavProtocolFailureClass {
63 Request,
64 Precondition,
65 Capability,
66 Backend,
67 Response,
68 Transport,
69}
70
71#[derive(Debug, Clone, Copy, PartialEq, Eq)]
73pub enum DavStreamOutcome {
74 Completed,
75 Cancelled { response_started: bool },
76 Failed { response_started: bool },
77}
78
79#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
84pub struct DavOperationObservations {
85 pub bytes_received: Option<u64>,
86 pub bytes_sent: Option<u64>,
87 pub requested_ranges: Option<u64>,
88 pub served_ranges: Option<u64>,
89 pub resources: Option<u64>,
90 pub backend_open_count: Option<u64>,
91 pub backend_call_count: Option<u64>,
92 pub protocol_failure: Option<DavProtocolFailureClass>,
93 pub stream: Option<DavStreamOutcome>,
94}
95
96#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
98#[error("WebDAV observation sink failed")]
99pub struct DavObservationError;
100
101impl DavEventOutcome {
102 #[must_use]
105 pub const fn from_status(status: u16, backend_error: Option<DavBackendErrorKind>) -> Self {
106 if status < 400 {
107 Self::Succeeded { status }
108 } else {
109 Self::Failed {
110 status,
111 backend_error,
112 }
113 }
114 }
115
116 #[must_use]
118 pub const fn status(self) -> u16 {
119 match self {
120 Self::Succeeded { status } | Self::Failed { status, .. } => status,
121 }
122 }
123}
124
125#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct DavEvent {
128 pub request_id: Option<String>,
129 pub operation: DavOperation,
130 pub source: DavPath,
131 pub destination: Option<DavPath>,
132 pub outcome: DavEventOutcome,
133 pub elapsed: Duration,
134 pub observations: DavOperationObservations,
135}
136
137impl DavEvent {
138 #[must_use]
143 pub fn completed(
144 request_head: &DavRequestHead,
145 status: u16,
146 elapsed: Duration,
147 backend_error: Option<DavBackendErrorKind>,
148 ) -> Self {
149 Self::completed_with_observations(
150 request_head,
151 status,
152 elapsed,
153 backend_error,
154 DavOperationObservations::default(),
155 )
156 }
157
158 #[must_use]
160 pub fn completed_with_observations(
161 request_head: &DavRequestHead,
162 status: u16,
163 elapsed: Duration,
164 backend_error: Option<DavBackendErrorKind>,
165 observations: DavOperationObservations,
166 ) -> Self {
167 Self {
168 request_id: None,
169 operation: request_head.method.operation(),
170 source: request_head.target.clone(),
171 destination: request_head
172 .destination
173 .as_ref()
174 .map(|destination| destination.path.clone()),
175 outcome: DavEventOutcome::from_status(status, backend_error),
176 elapsed,
177 observations,
178 }
179 }
180}
181
182pub trait DavEventSink: Send + Sync {
189 fn publish(&self, event: &DavEvent) -> Result<(), DavObservationError>;
194}
195
196pub fn publish_non_authoritative(sink: Option<&dyn DavEventSink>, event: &DavEvent) {
198 if let Some(sink) = sink {
199 let _ = catch_unwind(AssertUnwindSafe(|| sink.publish(event)));
200 }
201}
202
203#[derive(Debug, Clone, Copy, Default)]
205pub struct NoopDavEventSink;
206
207impl DavEventSink for NoopDavEventSink {
208 fn publish(&self, _event: &DavEvent) -> Result<(), DavObservationError> {
209 Ok(())
210 }
211}