config: add optional filter option for units
This allows the user to write: ```yaml units: - foo.service - bar.service - name: baz.service filter: "^Error: .*$" ``` So a unit can be provided as a string, or as a map which contains both `name` and `filter`.
This commit is contained in:
parent
f22416adac
commit
54f67887aa
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -139,6 +139,7 @@ dependencies = [
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
"url",
|
"url",
|
||||||
|
"void",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2365,6 +2366,12 @@ version = "0.9.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "void"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "want"
|
name = "want"
|
||||||
version = "0.3.0"
|
version = "0.3.0"
|
||||||
|
|
|
@ -17,6 +17,7 @@ serde_yaml = "0.8"
|
||||||
serde = "1.0"
|
serde = "1.0"
|
||||||
systemd = "0.8"
|
systemd = "0.8"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
void = "1"
|
||||||
|
|
||||||
[dependencies.matrix-sdk]
|
[dependencies.matrix-sdk]
|
||||||
git = "https://github.com/matrix-org/matrix-rust-sdk"
|
git = "https://github.com/matrix-org/matrix-rust-sdk"
|
||||||
|
|
|
@ -100,7 +100,7 @@ impl BadNewsBot {
|
||||||
const KEY_MESSAGE: &str = "MESSAGE";
|
const KEY_MESSAGE: &str = "MESSAGE";
|
||||||
|
|
||||||
if let Some(unit) = record.get(KEY_UNIT) {
|
if let Some(unit) = record.get(KEY_UNIT) {
|
||||||
if !self.config.units.contains(unit) {
|
if !self.config.units.iter().map(|u| &u.name).any(|name| name == unit) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
use matrix_sdk::identifiers::RoomId;
|
use matrix_sdk::identifiers::RoomId;
|
||||||
use serde::Deserialize;
|
use serde::de::{self, MapAccess, Visitor};
|
||||||
use std::collections::HashSet;
|
use serde::{Deserialize, Deserializer};
|
||||||
|
use std::fmt;
|
||||||
|
use std::marker::PhantomData;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::str::FromStr;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
use void::Void;
|
||||||
|
|
||||||
/// Holds the configuration for the bot.
|
/// Holds the configuration for the bot.
|
||||||
#[derive(Clone, Deserialize)]
|
#[derive(Clone, Deserialize)]
|
||||||
|
@ -19,5 +23,71 @@ pub struct Config {
|
||||||
/// invitations to this room.
|
/// invitations to this room.
|
||||||
pub room_id: RoomId,
|
pub room_id: RoomId,
|
||||||
/// Units to watch for logs
|
/// Units to watch for logs
|
||||||
pub units: HashSet<String>,
|
pub units: Vec<Unit>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Holds a single unit's configuration.
|
||||||
|
#[derive(Clone, Debug, Deserialize, Eq, PartialEq)]
|
||||||
|
#[serde(from = "SerializedUnit")]
|
||||||
|
pub struct Unit {
|
||||||
|
/// Can be serialized from a string only instead of a map.
|
||||||
|
pub name: String,
|
||||||
|
/// Regex to filter each line read from the unit's logs.
|
||||||
|
pub filter: Option<String>, // FIXME: regex
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
struct SerializedUnit(#[serde(deserialize_with = "unit_name_or_struct")] Unit);
|
||||||
|
|
||||||
|
impl From<SerializedUnit> for Unit {
|
||||||
|
fn from(s: SerializedUnit) -> Self {
|
||||||
|
s.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for Unit {
|
||||||
|
type Err = Void;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(Unit {
|
||||||
|
name: s.to_string(),
|
||||||
|
filter: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unit_name_or_struct<'de, T, D>(deserializer: D) -> Result<T, D::Error>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de> + FromStr<Err = Void>,
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
struct StringOrStruct<T>(PhantomData<fn() -> T>);
|
||||||
|
|
||||||
|
impl<'de, T> Visitor<'de> for StringOrStruct<T>
|
||||||
|
where
|
||||||
|
T: Deserialize<'de> + FromStr<Err = Void>,
|
||||||
|
{
|
||||||
|
type Value = T;
|
||||||
|
|
||||||
|
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
formatter.write_str("string or map")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_str<E>(self, value: &str) -> Result<T, E>
|
||||||
|
where
|
||||||
|
E: de::Error,
|
||||||
|
{
|
||||||
|
Ok(FromStr::from_str(value).unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_map<M>(self, map: M) -> Result<T, M::Error>
|
||||||
|
where
|
||||||
|
M: MapAccess<'de>,
|
||||||
|
{
|
||||||
|
Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deserializer.deserialize_any(StringOrStruct(PhantomData))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue