Skip to content

Commit a5f696e

Browse files
crates/triggers: Add decoupled API to make it suitable as a library
1 parent 634f5ac commit a5f696e

File tree

6 files changed

+496
-220
lines changed

6 files changed

+496
-220
lines changed

crates/triggers/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,7 @@ edition.workspace = true
77
dag = { version = "0.1.0", path = "../dag" }
88
fnmatch = { version = "0.1.0", path = "../fnmatch" }
99
serde.workspace = true
10-
serde_yaml.workspace = true
1110
thiserror.workspace = true
11+
12+
[dev-dependencies]
13+
serde_yaml.workspace = true

crates/triggers/src/format.rs

Lines changed: 40 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -3,116 +3,52 @@
33
// SPDX-License-Identifier: MPL-2.0
44

55
use std::collections::BTreeMap;
6+
use std::path::PathBuf;
67

7-
use fnmatch::Pattern;
8-
use serde::Deserialize;
8+
use serde::{Deserialize, Deserializer};
99

10-
/// Filter matched paths to a specific kind
11-
#[derive(Debug, Deserialize)]
12-
#[serde(rename_all = "lowercase")]
13-
pub enum PathKind {
14-
Directory,
15-
Symlink,
16-
}
17-
18-
/// Execution handlers for a trigger
19-
#[derive(Debug, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
20-
#[serde(untagged)]
21-
pub enum Handler {
22-
Run { run: String, args: Vec<String> },
23-
Delete { delete: Vec<String> },
24-
}
10+
use crate::{FileKind, Inhibitor, OsEnv, Pattern};
2511

26-
#[derive(Debug, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord)]
27-
pub struct CompiledHandler(Handler);
28-
29-
impl CompiledHandler {
30-
pub fn handler(&self) -> &Handler {
31-
&self.0
12+
/// Deserializes the "inhibitors" field of a [`Trigger`].
13+
pub fn deserialize_inhibitors<'de, D: Deserializer<'de>>(deserializer: D) -> Result<Vec<Inhibitor>, D::Error> {
14+
#[derive(Default, Deserialize)]
15+
struct Inhibitors {
16+
pub paths: Vec<PathBuf>,
17+
pub environment: Vec<OsEnv>,
3218
}
33-
}
3419

35-
impl Handler {
36-
/// Substitute all paths using matched variables
37-
pub fn compiled(&self, with_match: &fnmatch::Match) -> CompiledHandler {
38-
match self {
39-
Handler::Run { run, args } => {
40-
let mut run = run.clone();
41-
for (key, value) in &with_match.groups {
42-
run = run.replace(&format!("$({key})"), value);
43-
}
44-
let args = args
45-
.iter()
46-
.map(|a| {
47-
let mut a = a.clone();
48-
for (key, value) in &with_match.groups {
49-
a = a.replace(&format!("$({key})"), value);
50-
}
51-
a
52-
})
53-
.collect();
54-
CompiledHandler(Handler::Run { run, args })
55-
}
56-
Handler::Delete { delete } => CompiledHandler(Handler::Delete { delete: delete.clone() }),
57-
}
20+
let de = Inhibitors::deserialize(deserializer)?;
21+
let mut inhibitors = vec![];
22+
for path in de.paths {
23+
inhibitors.push(Inhibitor::Path(path));
24+
}
25+
for env in de.environment {
26+
inhibitors.push(Inhibitor::Environment(env));
27+
}
28+
Ok(inhibitors)
29+
}
30+
31+
/// Deserializes the "paths" field of a [`Trigger`].
32+
pub fn deserialize_patterns<'de, D: Deserializer<'de>>(
33+
deserializer: D,
34+
) -> Result<BTreeMap<Pattern, Vec<String>>, D::Error> {
35+
#[derive(Deserialize)]
36+
struct PathDefinition {
37+
pub handlers: Vec<String>,
38+
#[serde(rename = "type")]
39+
pub kind: Option<FileKind>,
5840
}
59-
}
60-
61-
/// Inhibitors prevent handlers from running based on some constraints
62-
#[derive(Debug, Deserialize)]
63-
pub struct Inhibitors {
64-
pub paths: Vec<String>,
65-
pub environment: Vec<String>,
66-
}
67-
68-
/// Map handlers to a path pattern and kind filter
69-
#[derive(Debug, Deserialize)]
70-
pub struct PathDefinition {
71-
pub handlers: Vec<String>,
72-
#[serde(rename = "type")]
73-
pub kind: Option<PathKind>,
74-
}
75-
76-
/// Serialization format of triggers
77-
#[derive(Debug, Deserialize)]
78-
pub struct Trigger {
79-
/// Unique (global scope) identifier
80-
pub name: String,
81-
82-
/// User friendly description
83-
pub description: String,
84-
85-
/// Run before this trigger name
86-
pub before: Option<String>,
87-
88-
/// Run after this trigger name
89-
pub after: Option<String>,
90-
91-
/// Optional inhibitors
92-
pub inhibitors: Option<Inhibitors>,
93-
94-
/// Map glob / patterns to their configuration
95-
pub paths: BTreeMap<Pattern, PathDefinition>,
96-
97-
/// Named handlers within this trigger scope
98-
pub handlers: BTreeMap<String, Handler>,
99-
}
100-
101-
#[cfg(test)]
102-
mod tests {
103-
use crate::format::Trigger;
104-
105-
#[test]
106-
fn test_trigger_file() {
107-
let trigger: Trigger = serde_yaml::from_str(include_str!("../../../test/trigger.yml")).unwrap();
10841

109-
let (pattern, _) = trigger.paths.iter().next().expect("Missing path entry");
110-
let result = pattern
111-
.matches("/usr/lib/modules/6.6.7-267.current/kernel")
112-
.expect("Couldn't match path");
113-
let version = result.groups.get("version").expect("Missing kernel version");
114-
assert_eq!(version, "6.6.7-267.current", "Wrong kernel version match");
115-
eprintln!("trigger: {trigger:?}");
116-
eprintln!("match: {result:?}");
42+
let de = BTreeMap::<fnmatch::Pattern, PathDefinition>::deserialize(deserializer)?;
43+
let mut paths = BTreeMap::new();
44+
for (pattern, path_definition) in de {
45+
paths.insert(
46+
Pattern {
47+
kind: path_definition.kind,
48+
pattern,
49+
},
50+
path_definition.handlers,
51+
);
11752
}
53+
Ok(paths)
11854
}

crates/triggers/src/iterpaths.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// SPDX-FileCopyrightText: Copyright © 2020-2024 Serpent OS Developers
2+
//
3+
// SPDX-License-Identifier: MPL-2.0
4+
5+
//! Utility functions that accept multiple file paths.
6+
7+
use std::collections::BTreeSet;
8+
9+
use crate::{CompiledHandler, Trigger};
10+
11+
pub fn compiled_handlers(trigger: &Trigger, paths: impl Iterator<Item = String>) -> BTreeSet<CompiledHandler> {
12+
paths.flat_map(|path| trigger.compiled_handlers(path)).collect()
13+
}

0 commit comments

Comments
 (0)