Skip to main content

fileloft_core/
hooks.rs

1use std::future::Future;
2use std::pin::Pin;
3use std::sync::Arc;
4
5use tokio::sync::broadcast;
6
7use crate::error::TusError;
8use crate::info::{UploadId, UploadInfo, UploadInfoChanges};
9
10/// All lifecycle events the handler can emit.
11#[derive(Debug, Clone)]
12pub enum HookEvent {
13    /// A new upload slot was successfully created (POST).
14    UploadCreated { info: UploadInfo },
15    /// An upload reached 100% — offset == size.
16    UploadFinished { info: UploadInfo },
17    /// An upload was explicitly terminated (DELETE).
18    UploadTerminated { id: UploadId },
19    /// A chunk was written; emitted after each successful PATCH.
20    UploadProgress { info: UploadInfo },
21}
22
23/// Boxed async future returned by hook callbacks.
24pub type HookFuture<T> = Pin<Box<dyn Future<Output = T> + Send + 'static>>;
25
26/// Pre-create callback. Receives proposed `UploadInfo`; may return modified fields
27/// (e.g. override ID or metadata) or reject creation with an `Err`.
28pub type PreCreateCallback =
29    Arc<dyn Fn(UploadInfo) -> HookFuture<Result<UploadInfoChanges, TusError>> + Send + Sync>;
30
31/// Pre-finish callback. Called after all bytes are written but before 204 is sent.
32/// Return `Err` to abort and respond with an error.
33pub type PreFinishCallback =
34    Arc<dyn Fn(UploadInfo) -> HookFuture<Result<(), TusError>> + Send + Sync>;
35
36/// Pre-terminate callback. Called before DELETE is processed.
37/// Return `Err` to reject the termination.
38pub type PreTerminateCallback =
39    Arc<dyn Fn(UploadInfo) -> HookFuture<Result<(), TusError>> + Send + Sync>;
40
41/// Sender side of the lifecycle event broadcast channel.
42/// Callers call `.subscribe()` to receive a `Receiver<HookEvent>`.
43pub type HookSender = broadcast::Sender<HookEvent>;
44
45/// Hook configuration attached to `Config`.
46#[derive(Clone, Default)]
47pub struct HookConfig {
48    /// Capacity of the broadcast channel. `0` means hooks are disabled.
49    pub channel_capacity: usize,
50    pub pre_create: Option<PreCreateCallback>,
51    pub pre_finish: Option<PreFinishCallback>,
52    pub pre_terminate: Option<PreTerminateCallback>,
53}
54
55impl std::fmt::Debug for HookConfig {
56    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57        f.debug_struct("HookConfig")
58            .field("channel_capacity", &self.channel_capacity)
59            .field("pre_create", &self.pre_create.is_some())
60            .field("pre_finish", &self.pre_finish.is_some())
61            .field("pre_terminate", &self.pre_terminate.is_some())
62            .finish()
63    }
64}
65
66impl HookConfig {
67    /// Returns true if any hooks are configured.
68    pub fn has_hooks(&self) -> bool {
69        self.channel_capacity > 0
70            || self.pre_create.is_some()
71            || self.pre_finish.is_some()
72            || self.pre_terminate.is_some()
73    }
74}