autopulse_service/settings/triggers/
mod.rs

1/// Lidarr - Lidarr trigger
2///
3/// This trigger is used to process a file from Lidarr
4///
5/// # Example
6///
7/// ```yml
8/// triggers:
9///   my_lidarr:
10///     type: lidarr
11/// ```
12///
13/// or
14///
15/// ```yml
16/// triggers:
17///   my_lidarr:
18///     type: lidarr
19///     rewrite:
20///       from: "/downloads/music"
21///       to: "/music"
22///     timer:
23///       wait: 30
24///     excludes: [ "ignored_target" ]
25/// ```
26///
27/// See [`Lidarr`] for all options
28pub mod lidarr;
29/// Manual - Manual trigger
30///
31/// This trigger is used to manually process a file. Often used when implementing a custom trigger
32///
33/// Note: A default route of `/triggers/manual` is provided
34///
35/// # Example
36///
37/// ```yml
38/// triggers:
39///   my_manual:
40///     type: manual
41/// ```
42///
43/// or
44///
45/// ```yml
46/// triggers:
47///   my_manual:
48///     type: manual
49///     rewrite:
50///       from: "/downloads/stuff"
51///       to: "/stuff"
52///     timer:
53///       wait: 30
54///     excludes: [ "ignored_target" ]
55/// ```
56///
57/// See [`Manual`] for all options
58/// and
59/// [`ManualQueryParams`](manual::ManualQueryParams) for query parameters
60pub mod manual;
61/// Notify - Notify trigger
62///
63/// Cross-platform monitoring for a directory to process based on file events
64///
65/// # Example
66///
67/// ```yml
68/// triggers:
69///   my_notify:
70///     type: notify
71///     paths:
72///       - "/path/to/monitor"
73/// ```
74///
75/// or
76///
77/// ```yml
78/// triggers:
79///   my_notify:
80///     type: notify
81///     paths:
82///       - "/downloads"
83///     recursive: false
84///     rewrite:
85///       from: "/downloads"
86///       to: "/media"
87///     timer:
88///       wait: 30
89///     excludes: [ "ignored_target" ]
90/// ```
91///
92/// See [`Notify`] for all options
93pub mod notify;
94/// Radarr - Radarr trigger
95///
96/// This trigger is used to process a file from Radarr
97///
98/// # Example
99///
100/// ```yml
101/// triggers:
102///   my_radarr:
103///     type: radarr
104/// ```
105///
106/// or
107///
108/// ```yml
109/// triggers:
110///   my_radarr:
111///     type: radarr
112///     rewrite:
113///       from: "/downloads/movies"
114///       to: "/movies"
115///     timer:
116///       wait: 30
117///     excludes: [ "ignored_target" ]
118/// ```
119///
120/// See [`Radarr`] for all options
121pub mod radarr;
122/// Readarr - Readarr trigger
123///
124/// This trigger is used to process a file from Readarr
125///
126/// # Example
127///
128/// ```yml
129/// triggers:
130///   my_readarr:
131///     type: readarr
132/// ```
133///
134/// or
135///
136/// ```yml
137/// triggers:
138///   my_readarr:
139///     type: readarr
140///     rewrite:
141///       from: "/downloads/books"
142///       to: "/books"
143///     timer:
144///       wait: 30
145///     excludes: [ "ignored_target" ]
146/// ```
147///
148/// See [`Readarr`] for all options
149pub mod readarr;
150/// Sonarr - Sonarr trigger
151///
152/// This trigger is used to process a file from Sonarr
153///
154/// # Example
155///
156/// ```yml
157/// triggers:
158///   my_sonarr:
159///     type: sonarr
160/// ```
161///
162/// or
163///
164/// ```yml
165/// triggers:
166///   my_sonarr:
167///     type: sonarr
168///     rewrite:
169///       from: "/downloads/shows"
170///       to: "/shows"
171///     timer:
172///       wait: 30
173///     excludes: [ "ignored_target" ]
174/// ```
175///
176/// See [`Sonarr`] for all options
177pub mod sonarr;
178
179use crate::settings::rewrite::Rewrite;
180use crate::settings::timer::Timer;
181use serde::Deserialize;
182use {
183    lidarr::{Lidarr, LidarrRequest},
184    manual::Manual,
185    notify::Notify,
186    radarr::{Radarr, RadarrRequest},
187    readarr::{Readarr, ReadarrRequest},
188    sonarr::{Sonarr, SonarrRequest},
189};
190
191pub trait TriggerRequest {
192    fn from_json(json: serde_json::Value) -> anyhow::Result<Self>
193    where
194        Self: Sized;
195
196    // where the bool represents whether to check found status
197    fn paths(&self) -> Vec<(String, bool)>;
198}
199
200#[derive(Deserialize, Clone)]
201#[serde(tag = "type", rename_all = "lowercase")]
202pub enum Trigger {
203    Manual(Manual),
204    Radarr(Radarr),
205    Sonarr(Sonarr),
206    Lidarr(Lidarr),
207    Readarr(Readarr),
208    Notify(Notify),
209}
210
211impl Trigger {
212    pub const fn get_rewrite(&self) -> Option<&Rewrite> {
213        match &self {
214            Self::Sonarr(trigger) => trigger.rewrite.as_ref(),
215            Self::Radarr(trigger) => trigger.rewrite.as_ref(),
216            Self::Lidarr(trigger) => trigger.rewrite.as_ref(),
217            Self::Readarr(trigger) => trigger.rewrite.as_ref(),
218            Self::Manual(_) | Self::Notify(_) => None,
219        }
220    }
221
222    pub fn get_timer(&self, event_name: Option<String>) -> Timer {
223        let mut base_timer = match self.clone() {
224            Self::Sonarr(trigger) => trigger.timer,
225            Self::Radarr(trigger) => trigger.timer,
226            Self::Lidarr(trigger) => trigger.timer,
227            Self::Readarr(trigger) => trigger.timer,
228            Self::Manual(trigger) => trigger.timer,
229            Self::Notify(trigger) => trigger.timer,
230        };
231
232        let event_specific_timer = match &self {
233            Self::Sonarr(trigger) => event_name
234                .as_ref()
235                .and_then(|event| trigger.event_timers.get(event)),
236            Self::Radarr(trigger) => event_name
237                .as_ref()
238                .and_then(|event| trigger.event_timers.get(event)),
239            Self::Lidarr(trigger) => event_name
240                .as_ref()
241                .and_then(|event| trigger.event_timers.get(event)),
242            Self::Readarr(trigger) => event_name
243                .as_ref()
244                .and_then(|event| trigger.event_timers.get(event)),
245            _ => None,
246        };
247
248        if let Some(event_timer) = event_specific_timer {
249            base_timer = base_timer.chain(event_timer);
250        }
251
252        base_timer
253    }
254
255    pub fn paths(&self, body: serde_json::Value) -> anyhow::Result<(String, Vec<(String, bool)>)> {
256        let event_name = body["eventType"].as_str().unwrap_or("unknown").to_string();
257
258        let paths = match &self {
259            Self::Sonarr(_) => Ok(SonarrRequest::from_json(body)?.paths()),
260            Self::Radarr(_) => Ok(RadarrRequest::from_json(body)?.paths()),
261            Self::Lidarr(_) => Ok(LidarrRequest::from_json(body)?.paths()),
262            Self::Readarr(_) => Ok(ReadarrRequest::from_json(body)?.paths()),
263            Self::Manual(_) | Self::Notify(_) => {
264                Err(anyhow::anyhow!("Manual trigger does not have paths"))
265            }
266        }?;
267
268        Ok((event_name, paths))
269    }
270
271    pub const fn excludes(&self) -> &Vec<String> {
272        match &self {
273            Self::Manual(trigger) => &trigger.excludes,
274            Self::Radarr(trigger) => &trigger.excludes,
275            Self::Sonarr(trigger) => &trigger.excludes,
276            Self::Lidarr(trigger) => &trigger.excludes,
277            Self::Readarr(trigger) => &trigger.excludes,
278            Self::Notify(trigger) => &trigger.excludes,
279        }
280    }
281}