aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/config.rs194
-rw-r--r--src/config/helpers.rs193
2 files changed, 199 insertions, 188 deletions
diff --git a/src/config.rs b/src/config.rs
index b04b72c..d3fc75b 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,21 +1,22 @@
// SPDX-FileCopyrightText: 2025 Tomasz Kramkowski <tomasz@kramkow.ski>
// SPDX-License-Identifier: GPL-3.0-or-later
+mod helpers;
+
use std::{
- collections::HashMap, fmt, fs::File, io::Read, os::unix::fs::PermissionsExt, path::Path,
+ collections::HashMap, fs::File, io::Read, os::unix::fs::PermissionsExt, path::Path,
time::Duration,
};
use anyhow::bail;
use log::LevelFilter;
use rumqttc::{AsyncClient, EventLoop, MqttOptions, QoS};
-use serde::{
- de::{self, Visitor},
- Deserialize, Deserializer,
-};
+use serde::Deserialize;
use crate::PROGRAM;
+use helpers::*;
+
#[derive(Deserialize, Debug, PartialEq)]
pub struct Logging {
#[serde(default = "default_level_filter")]
@@ -63,27 +64,6 @@ fn default_level_filter() -> LevelFilter {
LevelFilter::Info
}
-#[allow(clippy::enum_variant_names)]
-#[derive(Deserialize, Debug)]
-#[serde(remote = "QoS", rename_all = "kebab-case")]
-#[repr(u8)]
-pub enum QoSDef {
- AtMostOnce = 0,
- AtLeastOnce = 1,
- ExactlyOnce = 2,
-}
-
-pub fn deserialize_qos_opt<'de, D>(deserializer: D) -> Result<Option<QoS>, D::Error>
-where
- D: Deserializer<'de>,
-{
- #[derive(Deserialize)]
- struct Helper(#[serde(with = "QoSDef")] QoS);
-
- let helper = Option::deserialize(deserializer)?;
- Ok(helper.map(|Helper(external)| external))
-}
-
#[derive(Debug, PartialEq, Clone)]
pub struct Program {
// TODO: Figure out a way to allow arbitrary unix paths (arbitrary
@@ -92,174 +72,12 @@ pub struct Program {
pub timeout: Option<Duration>,
}
-impl<'de> Deserialize<'de> for Program {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- struct VecOrProgram;
-
- impl<'de> Visitor<'de> for VecOrProgram {
- type Value = Program;
-
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("map or seq")
- }
-
- fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
- where
- A: serde::de::SeqAccess<'de>,
- {
- let vec: Box<[String]> =
- Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
- Ok(Program {
- command: vec,
- timeout: None,
- })
- }
-
- fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
- where
- A: serde::de::MapAccess<'de>,
- {
- #[derive(Deserialize)]
- struct Helper {
- command: Box<[String]>,
- #[serde(default, deserialize_with = "deserialize_timeout_opt")]
- timeout: Option<Duration>,
- }
-
- let helper: Helper =
- Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
- Ok(Program {
- command: helper.command,
- timeout: helper.timeout,
- })
- }
- }
-
- deserializer.deserialize_any(VecOrProgram)
- }
-}
-
#[derive(Debug)]
pub struct Route {
pub programs: Box<[Program]>,
pub qos: Option<QoS>,
}
-impl<'de> Deserialize<'de> for Route {
- fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
- where
- D: Deserializer<'de>,
- {
- struct VecOrRoute;
-
- impl<'de> Visitor<'de> for VecOrRoute {
- type Value = Route;
-
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("map or seq")
- }
-
- fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
- where
- A: serde::de::SeqAccess<'de>,
- {
- let vec: Box<[Program]> =
- Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
- Ok(Route {
- programs: vec,
- qos: None,
- })
- }
-
- fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
- where
- A: serde::de::MapAccess<'de>,
- {
- #[derive(Deserialize)]
- struct Helper {
- programs: Box<[Program]>,
- #[serde(default, deserialize_with = "deserialize_qos_opt")]
- qos: Option<QoS>,
- }
-
- let helper: Helper =
- Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
- Ok(Route {
- programs: helper.programs,
- qos: helper.qos,
- })
- }
- }
-
- deserializer.deserialize_any(VecOrRoute)
- }
-}
-
-pub fn deserialize_timeout<'de, D>(deserializer: D) -> Result<Duration, D::Error>
-where
- D: Deserializer<'de>,
-{
- struct DurationVisitor;
-
- impl<'de> de::Visitor<'de> for DurationVisitor {
- type Value = Duration;
-
- fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
- formatter.write_str("a positive number")
- }
-
- fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- if v < 0 {
- return Err(de::Error::invalid_value(
- de::Unexpected::Signed(v),
- &"a non-negative number",
- ));
- }
- if v == 0 {
- Ok(Duration::MAX)
- } else {
- Ok(Duration::from_secs(v as u64))
- }
- }
-
- fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
- where
- E: de::Error,
- {
- if v < 0.0 {
- return Err(de::Error::invalid_value(
- de::Unexpected::Float(v),
- &"a non-negative number",
- ));
- }
- if v == 0.0 {
- Ok(Duration::MAX)
- } else {
- Ok(Duration::from_secs_f64(v))
- }
- }
- }
-
- deserializer.deserialize_any(DurationVisitor)
-}
-
-pub fn deserialize_timeout_opt<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
-where
- D: Deserializer<'de>,
-{
- #[derive(Deserialize)]
- struct Helper(#[serde(deserialize_with = "deserialize_timeout")] Duration);
-
- let helper = Option::deserialize(deserializer)?;
- Ok(helper.map(|Helper(external)| external))
-}
-
#[derive(Deserialize, Debug)]
pub struct Config {
#[serde(default = "default_host")]
diff --git a/src/config/helpers.rs b/src/config/helpers.rs
new file mode 100644
index 0000000..c03e6fd
--- /dev/null
+++ b/src/config/helpers.rs
@@ -0,0 +1,193 @@
+// SPDX-FileCopyrightText: 2025 Tomasz Kramkowski <tomasz@kramkow.ski>
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+use std::{fmt, time::Duration};
+
+use rumqttc::QoS;
+use serde::{
+ de::{self, Visitor},
+ Deserialize, Deserializer,
+};
+
+#[allow(clippy::enum_variant_names)]
+#[derive(Deserialize, Debug)]
+#[serde(remote = "QoS", rename_all = "kebab-case")]
+#[repr(u8)]
+pub enum QoSDef {
+ AtMostOnce = 0,
+ AtLeastOnce = 1,
+ ExactlyOnce = 2,
+}
+
+pub fn deserialize_qos_opt<'de, D>(deserializer: D) -> Result<Option<QoS>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize)]
+ struct Helper(#[serde(with = "QoSDef")] QoS);
+
+ let helper = Option::deserialize(deserializer)?;
+ Ok(helper.map(|Helper(external)| external))
+}
+
+pub fn deserialize_timeout<'de, D>(deserializer: D) -> Result<Duration, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ struct DurationVisitor;
+
+ impl<'de> de::Visitor<'de> for DurationVisitor {
+ type Value = Duration;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("a positive number")
+ }
+
+ fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ if v < 0 {
+ return Err(de::Error::invalid_value(
+ de::Unexpected::Signed(v),
+ &"a non-negative number",
+ ));
+ }
+ if v == 0 {
+ Ok(Duration::MAX)
+ } else {
+ Ok(Duration::from_secs(v as u64))
+ }
+ }
+
+ fn visit_f64<E>(self, v: f64) -> Result<Self::Value, E>
+ where
+ E: de::Error,
+ {
+ if v < 0.0 {
+ return Err(de::Error::invalid_value(
+ de::Unexpected::Float(v),
+ &"a non-negative number",
+ ));
+ }
+ if v == 0.0 {
+ Ok(Duration::MAX)
+ } else {
+ Ok(Duration::from_secs_f64(v))
+ }
+ }
+ }
+
+ deserializer.deserialize_any(DurationVisitor)
+}
+
+pub fn deserialize_timeout_opt<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ #[derive(Deserialize)]
+ struct Helper(#[serde(deserialize_with = "deserialize_timeout")] Duration);
+
+ let helper = Option::deserialize(deserializer)?;
+ Ok(helper.map(|Helper(external)| external))
+}
+
+impl<'de> Deserialize<'de> for super::Program {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct VecOrProgram;
+
+ impl<'de> Visitor<'de> for VecOrProgram {
+ type Value = super::Program;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("map or seq")
+ }
+
+ fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::SeqAccess<'de>,
+ {
+ let vec: Box<[String]> =
+ Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
+ Ok(super::Program {
+ command: vec,
+ timeout: None,
+ })
+ }
+
+ fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::MapAccess<'de>,
+ {
+ #[derive(Deserialize)]
+ struct Helper {
+ command: Box<[String]>,
+ #[serde(default, deserialize_with = "deserialize_timeout_opt")]
+ timeout: Option<Duration>,
+ }
+
+ let helper: Helper =
+ Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
+ Ok(super::Program {
+ command: helper.command,
+ timeout: helper.timeout,
+ })
+ }
+ }
+
+ deserializer.deserialize_any(VecOrProgram)
+ }
+}
+
+impl<'de> Deserialize<'de> for super::Route {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ struct VecOrRoute;
+
+ impl<'de> Visitor<'de> for VecOrRoute {
+ type Value = super::Route;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+ formatter.write_str("map or seq")
+ }
+
+ fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::SeqAccess<'de>,
+ {
+ let vec: Box<[super::Program]> =
+ Deserialize::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
+ Ok(super::Route {
+ programs: vec,
+ qos: None,
+ })
+ }
+
+ fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
+ where
+ A: serde::de::MapAccess<'de>,
+ {
+ #[derive(Deserialize)]
+ struct Helper {
+ programs: Box<[super::Program]>,
+ #[serde(default, deserialize_with = "deserialize_qos_opt")]
+ qos: Option<QoS>,
+ }
+
+ let helper: Helper =
+ Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
+ Ok(super::Route {
+ programs: helper.programs,
+ qos: helper.qos,
+ })
+ }
+ }
+
+ deserializer.deserialize_any(VecOrRoute)
+ }
+}