autopulse_service/settings/
mod.rs

1use app::App;
2use auth::Auth;
3use config::Config;
4use opts::Opts;
5use serde::{Deserialize, Serialize};
6use std::{collections::HashMap, path::PathBuf};
7use targets::Target;
8use triggers::manual::Manual;
9use triggers::Trigger;
10use webhooks::Webhook;
11
12/// App-specific settings
13///
14/// Example:
15///
16/// ```yml
17/// app:
18///   hostname: 0.0.0.0
19///   port: 1234
20///   database_url: sqlite://autopulse.db
21///   log_level: debug
22/// ```
23pub mod app;
24
25/// Authentication settings
26///
27/// Example:
28///
29/// ```yml
30/// auth:
31///   username: terry
32///   password: yogurt
33/// ```
34pub mod auth;
35
36/// Global settings
37///
38/// Example:
39///
40/// ```yml
41/// opts:
42///   check_path: true
43///   max_retries: 10
44///   default_timer_wait: 300
45///   cleanup_days: 7
46/// ```
47pub mod opts;
48
49/// Rewrite structure for triggers
50///
51/// Example:
52///
53/// ```yml
54/// triggers:
55///   sonarr:
56///     type: sonarr
57///     rewrite:
58///       from: /tv
59///       to: /media/tv
60pub use autopulse_utils::rewrite;
61
62/// Timer structure for triggers
63///
64/// Example:
65///
66/// ```yml
67/// triggers:
68///  sonarr:
69///   type: sonarr
70///   timer:
71///    wait: 300 # wait 5 minutes before processing
72/// ```
73pub mod timer;
74
75/// Trigger structure
76///
77/// [Triggers](triggers) for all triggers
78pub mod triggers;
79
80/// Target structure
81///
82/// [Targets](targets) for all targets
83pub mod targets;
84
85/// Webhook structure
86///
87/// [Webhooks](webhooks) for all webhooks
88pub mod webhooks;
89
90#[doc(hidden)]
91pub fn default_triggers() -> HashMap<String, Trigger> {
92    let mut triggers = HashMap::new();
93
94    triggers.insert(
95        "manual".to_string(),
96        Trigger::Manual(Manual {
97            rewrite: None,
98            timer: None,
99            excludes: vec![],
100        }),
101    );
102
103    triggers
104}
105
106#[derive(Serialize, Deserialize, Clone)]
107pub struct Settings {
108    #[serde(default)]
109    pub app: App,
110
111    #[serde(default)]
112    pub auth: Auth,
113
114    #[serde(default)]
115    pub opts: Opts,
116
117    #[serde(default = "default_triggers")]
118    pub triggers: HashMap<String, Trigger>,
119    #[serde(default)]
120    pub targets: HashMap<String, Target>,
121
122    #[serde(default)]
123    pub webhooks: HashMap<String, Webhook>,
124
125    /// List of paths to anchor the service to
126    ///
127    /// This is useful to prevent the service notifying a target when the drive is not mounted or visible
128    /// The contents of the file/directory are not tampered with, only the presence of the file/directory is checked
129    ///
130    /// Example:
131    /// ```yml
132    /// anchors:
133    ///  - /mnt/media/tv # Directory
134    ///  - /mnt/media/anchor # File
135    /// ```
136    #[serde(default)]
137    pub anchors: Vec<PathBuf>,
138}
139
140impl Default for Settings {
141    fn default() -> Self {
142        Self {
143            app: App::default(),
144            auth: Auth::default(),
145            opts: Opts::default(),
146            triggers: default_triggers(),
147            targets: HashMap::new(),
148            webhooks: HashMap::new(),
149            anchors: vec![],
150        }
151    }
152}
153
154impl Settings {
155    pub fn get_settings(optional_config_file: Option<String>) -> anyhow::Result<Self> {
156        let mut settings = Config::builder()
157            .add_source(config::File::with_name("config").required(false))
158            .add_source(config::Environment::with_prefix("AUTOPULSE").separator("__"));
159
160        if let Some(file_loc) = optional_config_file {
161            settings = settings.add_source(config::File::with_name(&file_loc));
162        }
163
164        let settings = settings.build()?;
165
166        let mut settings = settings
167            .try_deserialize::<Self>()
168            .map_err(|e| anyhow::anyhow!(e))?;
169
170        settings.add_default_manual_trigger()?;
171
172        Ok(settings)
173    }
174
175    pub fn add_default_manual_trigger(&mut self) -> anyhow::Result<()> {
176        if !self.triggers.contains_key("manual") {
177            self.triggers.insert(
178                "manual".to_string(),
179                Trigger::Manual(Manual {
180                    rewrite: None,
181                    timer: None,
182                    excludes: vec![],
183                }),
184            );
185        }
186
187        Ok(())
188    }
189}