email/watch/config.rs
1use std::{fmt, future::Future, ops::Deref, pin::Pin, sync::Arc};
2
3use process::Command;
4
5use crate::envelope::Envelope;
6
7/// Watch hook configuration.
8///
9/// Each variant represent the action that should be done when a
10/// change occurs.
11#[derive(Clone, Debug)]
12#[cfg_attr(
13 feature = "derive",
14 derive(serde::Serialize, serde::Deserialize),
15 serde(rename_all = "kebab-case")
16)]
17pub struct WatchHook {
18 /// Execute the shell command.
19 ///
20 /// For now, command is executed without any parameter nor
21 /// input. This may change in the future.
22 pub cmd: Option<Command>,
23
24 /// Send a system notification using the given
25 /// [`notify_rust::Notification`]-like configuration.
26 pub notify: Option<WatchNotifyConfig>,
27
28 /// Execute the given watch function.
29 ///
30 /// The watch function cannot be de/serialized. The function
31 /// should take a reference to an envelope and return a [`Result`]
32 /// of unit.
33 #[cfg_attr(feature = "derive", serde(skip))]
34 pub callback: Option<WatchFn>,
35}
36
37impl Eq for WatchHook {
38 //
39}
40
41impl PartialEq for WatchHook {
42 fn eq(&self, other: &Self) -> bool {
43 self.cmd == other.cmd && self.notify == other.notify
44 }
45}
46
47/// Watch function.
48///
49/// This is just a wrapper around a function that takes a reference to
50/// an envelope.
51#[derive(Clone)]
52pub struct WatchFn(
53 // This function is essentially an asyc Fn with an empty anyhow result type.
54 // So it should not create too much type complexity.
55 // Added to that there is a new function that can take the complexity of
56 // pinning and arcing away.
57 #[allow(clippy::type_complexity)]
58 Arc<
59 dyn Fn(&Envelope) -> Pin<Box<dyn Future<Output = crate::Result<()>> + Send>> + Send + Sync,
60 >,
61);
62
63impl WatchFn {
64 /// Create a new watch function.
65 pub fn new<F: Future<Output = crate::Result<()>> + Send + 'static>(
66 f: impl Fn(&Envelope) -> F + Send + Sync + 'static,
67 ) -> Self {
68 Self(Arc::new(move |envelope| Box::pin(f(envelope))))
69 }
70}
71
72impl Default for WatchFn {
73 fn default() -> Self {
74 Self(Arc::new(|_| Box::pin(async { Ok(()) })))
75 }
76}
77
78impl Deref for WatchFn {
79 type Target = Arc<
80 dyn Fn(&Envelope) -> Pin<Box<dyn Future<Output = crate::Result<()>> + Send>> + Send + Sync,
81 >;
82
83 fn deref(&self) -> &Self::Target {
84 &self.0
85 }
86}
87
88impl fmt::Debug for WatchFn {
89 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
90 write!(f, "WatchFn()")
91 }
92}
93
94/// The watch configuration of the notify hook variant.
95///
96/// The structure tries to match the [`notify_rust::Notification`] API
97/// and may evolve in the future.
98#[derive(Clone, Debug, Default, Eq, PartialEq)]
99#[cfg_attr(
100 feature = "derive",
101 derive(serde::Serialize, serde::Deserialize),
102 serde(rename_all = "kebab-case")
103)]
104pub struct WatchNotifyConfig {
105 /// The summary (or the title) of the notification.
106 ///
107 /// Accepted placeholders:
108 /// - "{id}": the id of the envelope
109 /// - "{subject}": the subject of the envelope
110 /// - "{sender}" either the sender name or the address
111 /// - "{sender.name}" the sender name or "unknown"
112 /// - "{sender.address}" the sender address
113 /// - "{recipient}" either the recipient name or the address
114 /// - "{recipient.name}" the recipient name or "unknown"
115 /// - "{recipient.address}" the recipient address
116 pub summary: String,
117
118 /// The body of the notification.
119 ///
120 /// Accepted placeholders:
121 /// - "{id}": the id of the envelope
122 /// - "{subject}": the subject of the envelope
123 /// - "{sender}" either the sender name or the address
124 /// - "{sender.name}" the sender name or "unknown"
125 /// - "{sender.address}" the sender address
126 /// - "{recipient}" either the recipient name or the address
127 /// - "{recipient.name}" the recipient name or "unknown"
128 /// - "{recipient.address}" the recipient address
129 pub body: String,
130}