mirror of
https://framagit.org/ppom/reaction
synced 2026-03-14 12:45:47 +01:00
Separate treedb into its own crate
This commit is contained in:
parent
c595552504
commit
f414245168
15 changed files with 214 additions and 192 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
|
@ -2841,6 +2841,7 @@ dependencies = [
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
"treedb",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
@ -3962,6 +3963,21 @@ dependencies = [
|
||||||
"tracing-log",
|
"tracing-log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "treedb"
|
||||||
|
version = "1.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
|
"futures",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tempfile",
|
||||||
|
"thiserror 1.0.69",
|
||||||
|
"tokio",
|
||||||
|
"tokio-util",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
version = "0.2.5"
|
version = "0.2.5"
|
||||||
|
|
|
||||||
27
Cargo.toml
27
Cargo.toml
|
|
@ -34,7 +34,7 @@ assets = [
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
# Time types
|
# Time types
|
||||||
chrono = { workspace = true }
|
chrono.workspace = true
|
||||||
# CLI parsing
|
# CLI parsing
|
||||||
clap = { version = "4.5.4", features = ["derive"] }
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
# Unix interfaces
|
# Unix interfaces
|
||||||
|
|
@ -48,17 +48,19 @@ serde_json.workspace = true
|
||||||
serde_yaml = "0.9.34"
|
serde_yaml = "0.9.34"
|
||||||
jrsonnet-evaluator = "0.4.2"
|
jrsonnet-evaluator = "0.4.2"
|
||||||
# Error macro
|
# Error macro
|
||||||
thiserror = "1.0.63"
|
thiserror.workspace = true
|
||||||
# Async runtime & helpers
|
# Async runtime & helpers
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
tokio = { workspace = true, features = ["full", "tracing"] }
|
tokio = { workspace = true, features = ["full", "tracing"] }
|
||||||
tokio-util = { workspace = true, features = ["codec"] }
|
tokio-util = { workspace = true, features = ["codec"] }
|
||||||
# Async logging
|
# Async logging
|
||||||
tracing = "0.1.40"
|
tracing.workspace = true
|
||||||
tracing-subscriber = "0.3.18"
|
tracing-subscriber = "0.3.18"
|
||||||
|
# Database
|
||||||
|
treedb.workspace = true
|
||||||
# Reaction plugin system
|
# Reaction plugin system
|
||||||
remoc = { workspace = true }
|
remoc.workspace = true
|
||||||
reaction-plugin = { workspace = true }
|
reaction-plugin.workspace = true
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
clap = { version = "4.5.4", features = ["derive"] }
|
clap = { version = "4.5.4", features = ["derive"] }
|
||||||
|
|
@ -69,13 +71,20 @@ tracing = "0.1.40"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
tempfile = "3.12.0"
|
treedb.workspace = true
|
||||||
|
treedb.features = ["test"]
|
||||||
|
tempfile.workspace = true
|
||||||
assert_fs.workspace = true
|
assert_fs.workspace = true
|
||||||
assert_cmd = "2.0.17"
|
assert_cmd = "2.0.17"
|
||||||
predicates = "3.1.3"
|
predicates = "3.1.3"
|
||||||
|
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["plugins/reaction-plugin", "plugins/reaction-plugin-cluster", "plugins/reaction-plugin-virtual"]
|
members = [
|
||||||
|
"crates/treedb",
|
||||||
|
"plugins/reaction-plugin",
|
||||||
|
"plugins/reaction-plugin-cluster",
|
||||||
|
"plugins/reaction-plugin-virtual"
|
||||||
|
]
|
||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
assert_fs = "1.1.3"
|
assert_fs = "1.1.3"
|
||||||
|
|
@ -84,6 +93,10 @@ futures = "0.3.30"
|
||||||
remoc = { version = "0.18.3" }
|
remoc = { version = "0.18.3" }
|
||||||
serde = { version = "1.0.203", features = ["derive"] }
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
serde_json = { version = "1.0.117", features = ["arbitrary_precision"] }
|
serde_json = { version = "1.0.117", features = ["arbitrary_precision"] }
|
||||||
|
tempfile = "3.12.0"
|
||||||
|
thiserror = "1.0.63"
|
||||||
tokio = { version = "1.40.0" }
|
tokio = { version = "1.40.0" }
|
||||||
tokio-util = { version = "0.7.12" }
|
tokio-util = { version = "0.7.12" }
|
||||||
|
tracing = "0.1.40"
|
||||||
reaction-plugin = { path = "plugins/reaction-plugin" }
|
reaction-plugin = { path = "plugins/reaction-plugin" }
|
||||||
|
treedb = { path = "crates/treedb" }
|
||||||
|
|
|
||||||
23
crates/treedb/Cargo.toml
Normal file
23
crates/treedb/Cargo.toml
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
[package]
|
||||||
|
name = "treedb"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
test = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
chrono.workspace = true
|
||||||
|
futures.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
thiserror.workspace = true
|
||||||
|
tokio.workspace = true
|
||||||
|
tokio.features = ["rt-multi-thread", "macros", "io-util", "time", "fs", "tracing"]
|
||||||
|
tokio-util.workspace = true
|
||||||
|
tokio-util.features = ["rt"]
|
||||||
|
tracing.workspace = true
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
tempfile.workspace = true
|
||||||
|
|
||||||
|
|
@ -6,7 +6,7 @@ use std::{
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
|
|
||||||
use crate::concepts::{Match, MatchTime, Time};
|
use crate::time::Time;
|
||||||
|
|
||||||
/// Tries to convert a [`Value`] into a [`String`]
|
/// Tries to convert a [`Value`] into a [`String`]
|
||||||
pub fn to_string(val: &Value) -> Result<String, String> {
|
pub fn to_string(val: &Value) -> Result<String, String> {
|
||||||
|
|
@ -50,8 +50,8 @@ pub fn to_time(val: &Value) -> Result<Time, String> {
|
||||||
string_to_time(val.as_str().ok_or("not a string number")?)
|
string_to_time(val.as_str().ok_or("not a string number")?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to convert a [`Value`] into a [`Match`]
|
/// Tries to convert a [`Value`] into a [`Vec<String>`]
|
||||||
pub fn to_match(val: &Value) -> Result<Match, String> {
|
pub fn to_match(val: &Value) -> Result<Vec<String>, String> {
|
||||||
val.as_array()
|
val.as_array()
|
||||||
.ok_or("not an array")?
|
.ok_or("not an array")?
|
||||||
.iter()
|
.iter()
|
||||||
|
|
@ -59,15 +59,6 @@ pub fn to_match(val: &Value) -> Result<Match, String> {
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Tries to convert a [`Value`] into a [`MatchTime`]
|
|
||||||
pub fn to_matchtime(val: &Value) -> Result<MatchTime, String> {
|
|
||||||
let map = val.as_object().ok_or("not an object")?;
|
|
||||||
Ok(MatchTime {
|
|
||||||
m: to_match(map.get("m").ok_or("no m in object")?)?,
|
|
||||||
t: to_time(map.get("t").ok_or("no t in object")?)?,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Tries to convert a [`Value`] into a [`BTreeSet<Time>`]
|
/// Tries to convert a [`Value`] into a [`BTreeSet<Time>`]
|
||||||
pub fn to_timeset(val: &Value) -> Result<BTreeSet<Time>, String> {
|
pub fn to_timeset(val: &Value) -> Result<BTreeSet<Time>, String> {
|
||||||
val.as_array()
|
val.as_array()
|
||||||
|
|
@ -90,8 +81,6 @@ pub fn to_timemap(val: &Value) -> Result<BTreeMap<Time, u64>, String> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
|
|
||||||
use serde_json::Map;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -165,62 +154,6 @@ mod tests {
|
||||||
assert!(to_timeset(&(None::<String>.into())).is_err());
|
assert!(to_timeset(&(None::<String>.into())).is_err());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_to_matchtime() {
|
|
||||||
assert_eq!(
|
|
||||||
to_matchtime(&Value::Object(Map::from_iter(
|
|
||||||
BTreeMap::from([
|
|
||||||
("m".into(), ["plip", "ploup"].into()),
|
|
||||||
("t".into(), "12345678".into()),
|
|
||||||
])
|
|
||||||
.into_iter()
|
|
||||||
))),
|
|
||||||
Ok(MatchTime {
|
|
||||||
m: vec!["plip".into(), "ploup".into()],
|
|
||||||
t: Time::from_nanos(12345678),
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
to_matchtime(&Value::Object(Map::from_iter(
|
|
||||||
BTreeMap::from([("m".into(), ["plip", "ploup"].into()),]).into_iter()
|
|
||||||
)))
|
|
||||||
.is_err()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
to_matchtime(&Value::Object(Map::from_iter(
|
|
||||||
BTreeMap::from([("t".into(), 12345678.into()),]).into_iter()
|
|
||||||
)))
|
|
||||||
.is_err()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
to_matchtime(&Value::Object(Map::from_iter(
|
|
||||||
BTreeMap::from([("m".into(), "ploup".into()), ("t".into(), 12345678.into()),])
|
|
||||||
.into_iter()
|
|
||||||
)))
|
|
||||||
.is_err()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(
|
|
||||||
to_matchtime(&Value::Object(Map::from_iter(
|
|
||||||
BTreeMap::from([
|
|
||||||
("m".into(), ["plip", "ploup"].into()),
|
|
||||||
("t".into(), [1234567].into()),
|
|
||||||
])
|
|
||||||
.into_iter()
|
|
||||||
)))
|
|
||||||
.is_err()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert!(to_timeset(&([""].into())).is_err());
|
|
||||||
assert!(to_timeset(&(["ploup"].into())).is_err());
|
|
||||||
assert!(to_timeset(&(true.into())).is_err());
|
|
||||||
assert!(to_timeset(&(8.into())).is_err());
|
|
||||||
assert!(to_timeset(&(None::<String>.into())).is_err());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_to_timemap() {
|
fn test_to_timemap() {
|
||||||
let time1 = 1234567;
|
let time1 = 1234567;
|
||||||
|
|
@ -17,7 +17,6 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use reaction_plugin::shutdown::ShutdownToken;
|
|
||||||
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
use serde::{Deserialize, Serialize, de::DeserializeOwned};
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use tokio::{
|
use tokio::{
|
||||||
|
|
@ -25,16 +24,16 @@ use tokio::{
|
||||||
sync::{mpsc, oneshot},
|
sync::{mpsc, oneshot},
|
||||||
time::{MissedTickBehavior, interval},
|
time::{MissedTickBehavior, interval},
|
||||||
};
|
};
|
||||||
|
use tokio_util::{sync::CancellationToken, task::task_tracker::TaskTrackerToken};
|
||||||
use crate::concepts::{Config, Time, now};
|
|
||||||
|
|
||||||
pub mod helpers;
|
|
||||||
|
|
||||||
// Database
|
// Database
|
||||||
|
|
||||||
use raw::{ReadDB, WriteDB};
|
use raw::{ReadDB, WriteDB};
|
||||||
|
use time::{Time, now};
|
||||||
|
|
||||||
|
pub mod helpers;
|
||||||
mod raw;
|
mod raw;
|
||||||
|
pub mod time;
|
||||||
|
|
||||||
/// Any order the Database can receive
|
/// Any order the Database can receive
|
||||||
enum Order {
|
enum Order {
|
||||||
|
|
@ -63,13 +62,11 @@ pub type LoadedDB = HashMap<String, LoadedTree>;
|
||||||
const DB_NAME: &str = "reaction.db";
|
const DB_NAME: &str = "reaction.db";
|
||||||
const DB_NEW_NAME: &str = "reaction.new.db";
|
const DB_NEW_NAME: &str = "reaction.new.db";
|
||||||
|
|
||||||
impl Config {
|
fn path_of(state_directory: &Path, name: &str) -> PathBuf {
|
||||||
fn path_of(&self, name: &str) -> PathBuf {
|
if state_directory.as_os_str().is_empty() {
|
||||||
if self.state_directory.is_empty() {
|
|
||||||
name.into()
|
name.into()
|
||||||
} else {
|
} else {
|
||||||
PathBuf::from(&self.state_directory).join(name)
|
PathBuf::from(state_directory).join(name)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -90,9 +87,13 @@ impl Database {
|
||||||
/// to have the Database properly quit.
|
/// to have the Database properly quit.
|
||||||
///
|
///
|
||||||
/// You can wait for [`Self::quit`] returned channel to know how it went.
|
/// You can wait for [`Self::quit`] returned channel to know how it went.
|
||||||
pub async fn open(config: &Config, shutdown: ShutdownToken) -> Result<Database, IoError> {
|
pub async fn open(
|
||||||
let (manager, entry_tx) = DatabaseManager::open(config).await?;
|
path_directory: &Path,
|
||||||
let error_rx = manager.manager(shutdown);
|
cancellation_token: CancellationToken,
|
||||||
|
task_tracker_token: TaskTrackerToken,
|
||||||
|
) -> Result<Database, IoError> {
|
||||||
|
let (manager, entry_tx) = DatabaseManager::open(path_directory).await?;
|
||||||
|
let error_rx = manager.manager(cancellation_token, task_tracker_token);
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
entry_tx: Some(entry_tx),
|
entry_tx: Some(entry_tx),
|
||||||
error_rx,
|
error_rx,
|
||||||
|
|
@ -133,9 +134,11 @@ struct DatabaseManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseManager {
|
impl DatabaseManager {
|
||||||
pub async fn open(config: &Config) -> Result<(DatabaseManager, mpsc::Sender<Order>), IoError> {
|
pub async fn open(
|
||||||
let path = config.path_of(DB_NAME);
|
path_directory: &Path,
|
||||||
let new_path = config.path_of(DB_NEW_NAME);
|
) -> Result<(DatabaseManager, mpsc::Sender<Order>), IoError> {
|
||||||
|
let path = path_of(path_directory, DB_NAME);
|
||||||
|
let new_path = path_of(path_directory, DB_NEW_NAME);
|
||||||
|
|
||||||
let (write_db, loaded_db) = rotate_db(&path, &new_path, true).await?;
|
let (write_db, loaded_db) = rotate_db(&path, &new_path, true).await?;
|
||||||
|
|
||||||
|
|
@ -156,7 +159,11 @@ impl DatabaseManager {
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn manager(mut self, shutdown: ShutdownToken) -> oneshot::Receiver<Result<(), String>> {
|
pub fn manager(
|
||||||
|
mut self,
|
||||||
|
cancellation_token: CancellationToken,
|
||||||
|
_task_tracker_token: TaskTrackerToken,
|
||||||
|
) -> oneshot::Receiver<Result<(), String>> {
|
||||||
let (error_tx, error_rx) = oneshot::channel();
|
let (error_tx, error_rx) = oneshot::channel();
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
let mut interval = interval(self.flush_every);
|
let mut interval = interval(self.flush_every);
|
||||||
|
|
@ -168,17 +175,17 @@ impl DatabaseManager {
|
||||||
tokio::select! {
|
tokio::select! {
|
||||||
order = self.entry_rx.recv() => {
|
order = self.entry_rx.recv() => {
|
||||||
if let Err(err) = self.handle_order(order).await {
|
if let Err(err) = self.handle_order(order).await {
|
||||||
shutdown.ask_shutdown();
|
cancellation_token.cancel();
|
||||||
break err;
|
break err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = interval.tick() => {
|
_ = interval.tick() => {
|
||||||
if let Err(err) = self.flush().await {
|
if let Err(err) = self.flush().await {
|
||||||
shutdown.ask_shutdown();
|
cancellation_token.cancel();
|
||||||
break Some(err);
|
break Some(err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = shutdown.wait() => break None
|
_ = cancellation_token.cancelled() => break None
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -467,70 +474,30 @@ impl<K: KeyType, V: ValueType> Tree<K, V> {
|
||||||
self.tree.remove(&key)
|
self.tree.remove(&key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test"))]
|
||||||
|
pub fn tree(&self) -> &BTreeMap<K, V> {
|
||||||
|
&self.tree
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(any(test, feature = "test"))]
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use std::{
|
|
||||||
collections::{BTreeMap, BTreeSet, HashMap},
|
|
||||||
io::Error as IoError,
|
|
||||||
path::Path,
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use reaction_plugin::shutdown::ShutdownController;
|
|
||||||
use serde_json::Value;
|
|
||||||
use tempfile::{NamedTempFile, TempDir};
|
|
||||||
use tokio::fs::{File, write};
|
|
||||||
|
|
||||||
use crate::concepts::{Config, Time, now};
|
|
||||||
|
|
||||||
use super::{
|
|
||||||
DB_NAME, Database, DatabaseManager, Entry, KeyType, LoadedDB, Tree, ValueType, helpers::*,
|
|
||||||
raw::WriteDB, rotate_db,
|
|
||||||
};
|
|
||||||
|
|
||||||
impl DatabaseManager {
|
impl DatabaseManager {
|
||||||
pub fn set_loaded_db(&mut self, loaded_db: LoadedDB) {
|
pub fn set_loaded_db(&mut self, loaded_db: LoadedDB) {
|
||||||
self.loaded_db = loaded_db;
|
self.loaded_db = loaded_db;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(any(test, feature = "test"))]
|
||||||
impl Database {
|
impl Database {
|
||||||
pub async fn from_dir(
|
pub async fn from_dir(dir_path: &Path, loaded_db: Option<LoadedDB>) -> Result<Self, IoError> {
|
||||||
dir_path: &Path,
|
use tokio_util::task::TaskTracker;
|
||||||
loaded_db: Option<LoadedDB>,
|
|
||||||
) -> Result<Self, IoError> {
|
|
||||||
let config_path = dir_path.join("reaction.jsonnet");
|
|
||||||
write(
|
|
||||||
&config_path,
|
|
||||||
format!(
|
|
||||||
"
|
|
||||||
{{
|
|
||||||
state_directory: {dir_path:?},
|
|
||||||
patterns: {{ pattern: {{ regex: \"prout\" }} }},
|
|
||||||
streams: {{ dummy: {{
|
|
||||||
cmd: [\"dummy\"],
|
|
||||||
filters: {{ dummy: {{
|
|
||||||
regex: [\"dummy\"],
|
|
||||||
actions: {{ dummy: {{
|
|
||||||
cmd: [\"dummy\"]
|
|
||||||
}} }}
|
|
||||||
}} }}
|
|
||||||
}} }}
|
|
||||||
}}
|
|
||||||
"
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let config = Config::from_path(&config_path).unwrap();
|
let (mut manager, entry_tx) = DatabaseManager::open(dir_path).await?;
|
||||||
let (mut manager, entry_tx) = DatabaseManager::open(&config).await?;
|
|
||||||
if let Some(loaded_db) = loaded_db {
|
if let Some(loaded_db) = loaded_db {
|
||||||
manager.set_loaded_db(loaded_db)
|
manager.set_loaded_db(loaded_db)
|
||||||
}
|
}
|
||||||
let error_rx = manager.manager(ShutdownController::new().token());
|
let error_rx = manager.manager(CancellationToken::new(), TaskTracker::new().token());
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
entry_tx: Some(entry_tx),
|
entry_tx: Some(entry_tx),
|
||||||
error_rx,
|
error_rx,
|
||||||
|
|
@ -538,11 +505,19 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K: KeyType, V: ValueType> Tree<K, V> {
|
#[cfg(test)]
|
||||||
pub fn tree(&self) -> &BTreeMap<K, V> {
|
mod tests {
|
||||||
&self.tree
|
|
||||||
}
|
use std::{
|
||||||
}
|
collections::{BTreeMap, BTreeSet, HashMap},
|
||||||
|
time::Duration,
|
||||||
|
};
|
||||||
|
|
||||||
|
use serde_json::Value;
|
||||||
|
use tempfile::{NamedTempFile, TempDir};
|
||||||
|
use tokio::fs::File;
|
||||||
|
|
||||||
|
use super::{DB_NAME, Database, Entry, Time, helpers::*, now, raw::WriteDB, rotate_db};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_rotate_db() {
|
async fn test_rotate_db() {
|
||||||
|
|
@ -13,7 +13,7 @@ use tokio::{
|
||||||
};
|
};
|
||||||
use tracing::error;
|
use tracing::error;
|
||||||
|
|
||||||
use crate::concepts::Time;
|
use crate::time::Time;
|
||||||
|
|
||||||
use super::{Entry, LoadedDB};
|
use super::{Entry, LoadedDB};
|
||||||
|
|
||||||
|
|
@ -265,11 +265,9 @@ mod tests {
|
||||||
use tokio::fs::{File, read, write};
|
use tokio::fs::{File, read, write};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
concepts::{Time, now},
|
|
||||||
treedb::{
|
|
||||||
Entry,
|
Entry,
|
||||||
raw::{DB_TREE_ID, DatabaseError, ReadDB, WriteDB},
|
raw::{DB_TREE_ID, DatabaseError, ReadDB, WriteDB},
|
||||||
},
|
time::{Time, now},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
|
|
@ -81,6 +81,10 @@ impl ShutdownToken {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn split(self) -> (CancellationToken, TaskTrackerToken) {
|
||||||
|
(self.shutdown_notifyer, self._task_tracker_token)
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns a future that will resolve only when a shutdown request happened.
|
/// Returns a future that will resolve only when a shutdown request happened.
|
||||||
pub fn wait(&self) -> WaitForCancellationFuture<'_> {
|
pub fn wait(&self) -> WaitForCancellationFuture<'_> {
|
||||||
self.shutdown_notifyer.cancelled()
|
self.shutdown_notifyer.cancelled()
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
use std::{
|
use std::{
|
||||||
collections::{btree_map::Entry, BTreeMap},
|
collections::{BTreeMap, btree_map::Entry},
|
||||||
fs::File,
|
fs::File,
|
||||||
io,
|
io,
|
||||||
path::Path,
|
path::Path,
|
||||||
|
|
@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use tracing::{debug, error, info, warn};
|
use tracing::{debug, error, info, warn};
|
||||||
|
|
||||||
use super::{merge_attrs, Pattern, Plugin, Stream};
|
use super::{Pattern, Plugin, Stream, merge_attrs};
|
||||||
|
|
||||||
pub type Patterns = BTreeMap<String, Arc<Pattern>>;
|
pub type Patterns = BTreeMap<String, Arc<Pattern>>;
|
||||||
|
|
||||||
|
|
@ -55,7 +55,10 @@ impl Config {
|
||||||
e.insert(pattern);
|
e.insert(pattern);
|
||||||
}
|
}
|
||||||
Entry::Occupied(e) => {
|
Entry::Occupied(e) => {
|
||||||
return Err(format!("pattern {} is already defined. pattern definitions can't be spread accross multiple files.", e.key()));
|
return Err(format!(
|
||||||
|
"pattern {} is already defined. pattern definitions can't be spread accross multiple files.",
|
||||||
|
e.key()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -328,7 +331,7 @@ enum ConfigError {
|
||||||
mod jsonnet {
|
mod jsonnet {
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use jrsonnet_evaluator::{error::LocError, EvaluationState, FileImportResolver};
|
use jrsonnet_evaluator::{EvaluationState, FileImportResolver, error::LocError};
|
||||||
|
|
||||||
use super::ConfigError;
|
use super::ConfigError;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ mod filter;
|
||||||
mod pattern;
|
mod pattern;
|
||||||
mod plugin;
|
mod plugin;
|
||||||
mod stream;
|
mod stream;
|
||||||
mod time;
|
|
||||||
|
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
|
@ -17,7 +16,7 @@ pub use filter::{Duplicate, Filter};
|
||||||
pub use pattern::{Pattern, PatternType};
|
pub use pattern::{Pattern, PatternType};
|
||||||
pub use plugin::Plugin;
|
pub use plugin::Plugin;
|
||||||
pub use stream::Stream;
|
pub use stream::Stream;
|
||||||
pub use time::{Time, now};
|
pub use treedb::time::{Time, now};
|
||||||
|
|
||||||
pub type Match = Vec<String>;
|
pub type Match = Vec<String>;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ use crate::{
|
||||||
concepts::{Action, Duplicate, Filter, Match, Pattern, Time},
|
concepts::{Action, Duplicate, Filter, Match, Pattern, Time},
|
||||||
daemon::plugin::Plugins,
|
daemon::plugin::Plugins,
|
||||||
protocol::{Order, PatternStatus},
|
protocol::{Order, PatternStatus},
|
||||||
treedb::Database,
|
|
||||||
};
|
};
|
||||||
|
use treedb::Database;
|
||||||
|
|
||||||
use state::State;
|
use state::State;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
use std::collections::{BTreeMap, BTreeSet};
|
use std::collections::{BTreeMap, BTreeSet};
|
||||||
|
|
||||||
use crate::{
|
use serde_json::Value;
|
||||||
concepts::{Filter, Match, MatchTime, Time},
|
use treedb::{Database, Tree, helpers::*};
|
||||||
treedb::{
|
|
||||||
Database, Tree,
|
use crate::concepts::{Filter, Match, MatchTime, Time};
|
||||||
helpers::{to_match, to_matchtime, to_time, to_timemap, to_u64},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn filter_ordered_times_db_name(filter: &Filter) -> String {
|
pub fn filter_ordered_times_db_name(filter: &Filter) -> String {
|
||||||
format!(
|
format!(
|
||||||
|
|
@ -241,18 +238,30 @@ impl State {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Tries to convert a [`Value`] into a [`MatchTime`]
|
||||||
|
pub fn to_matchtime(val: &Value) -> Result<MatchTime, String> {
|
||||||
|
let map = val.as_object().ok_or("not an object")?;
|
||||||
|
Ok(MatchTime {
|
||||||
|
m: to_match(map.get("m").ok_or("no m in object")?)?,
|
||||||
|
t: to_time(map.get("t").ok_or("no t in object")?)?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::collections::{BTreeMap, HashMap};
|
use std::collections::{BTreeMap, HashMap};
|
||||||
|
|
||||||
use serde_json::json;
|
use serde_json::{Map, Value, json};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
concepts::{Action, Duplicate, Filter, Pattern, Time, filter_tests::ok_filter, now},
|
concepts::{
|
||||||
daemon::filter::state::State,
|
Action, Duplicate, Filter, MatchTime, Pattern, Time, filter_tests::ok_filter, now,
|
||||||
|
},
|
||||||
tests::TempDatabase,
|
tests::TempDatabase,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::{State, to_matchtime};
|
||||||
|
|
||||||
// Tests `new`, `clear_past_matches` and `load_matches_from_ordered_times`
|
// Tests `new`, `clear_past_matches` and `load_matches_from_ordered_times`
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn state_new() {
|
async fn state_new() {
|
||||||
|
|
@ -593,4 +602,54 @@ mod tests {
|
||||||
state.remove_trigger(&one).await;
|
state.remove_trigger(&one).await;
|
||||||
assert!(state.triggers.tree().is_empty());
|
assert!(state.triggers.tree().is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_to_matchtime() {
|
||||||
|
assert_eq!(
|
||||||
|
to_matchtime(&Value::Object(Map::from_iter(
|
||||||
|
BTreeMap::from([
|
||||||
|
("m".into(), ["plip", "ploup"].into()),
|
||||||
|
("t".into(), "12345678".into()),
|
||||||
|
])
|
||||||
|
.into_iter()
|
||||||
|
))),
|
||||||
|
Ok(MatchTime {
|
||||||
|
m: vec!["plip".into(), "ploup".into()],
|
||||||
|
t: Time::from_nanos(12345678),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
to_matchtime(&Value::Object(Map::from_iter(
|
||||||
|
BTreeMap::from([("m".into(), ["plip", "ploup"].into()),]).into_iter()
|
||||||
|
)))
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
to_matchtime(&Value::Object(Map::from_iter(
|
||||||
|
BTreeMap::from([("t".into(), 12345678.into()),]).into_iter()
|
||||||
|
)))
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
to_matchtime(&Value::Object(Map::from_iter(
|
||||||
|
BTreeMap::from([("m".into(), "ploup".into()), ("t".into(), 12345678.into()),])
|
||||||
|
.into_iter()
|
||||||
|
)))
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(
|
||||||
|
to_matchtime(&Value::Object(Map::from_iter(
|
||||||
|
BTreeMap::from([
|
||||||
|
("m".into(), ["plip", "ploup"].into()),
|
||||||
|
("t".into(), [1234567].into()),
|
||||||
|
])
|
||||||
|
.into_iter()
|
||||||
|
)))
|
||||||
|
.is_err()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,11 +16,9 @@ use tokio::{
|
||||||
sync::Semaphore,
|
sync::Semaphore,
|
||||||
};
|
};
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
use treedb::Database;
|
||||||
|
|
||||||
use crate::{
|
use crate::concepts::{Config, now};
|
||||||
concepts::{Config, now},
|
|
||||||
treedb::Database,
|
|
||||||
};
|
|
||||||
use filter::FilterManager;
|
use filter::FilterManager;
|
||||||
pub use filter::React;
|
pub use filter::React;
|
||||||
use plugin::Plugins;
|
use plugin::Plugins;
|
||||||
|
|
@ -116,7 +114,9 @@ async fn daemon_start(
|
||||||
let mut plugins = Plugins::new(config, shutdown.clone()).await?;
|
let mut plugins = Plugins::new(config, shutdown.clone()).await?;
|
||||||
|
|
||||||
// Open Database
|
// Open Database
|
||||||
*db = Some(Database::open(config, shutdown.clone()).await?);
|
let (cancellation, task_tracker) = shutdown.clone().split();
|
||||||
|
let path = PathBuf::from(config.state_directory.clone());
|
||||||
|
*db = Some(Database::open(&path, cancellation, task_tracker).await?);
|
||||||
|
|
||||||
let (state, stream_managers) = {
|
let (state, stream_managers) = {
|
||||||
// Semaphore limiting action execution concurrency
|
// Semaphore limiting action execution concurrency
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,3 @@ pub mod concepts;
|
||||||
pub mod daemon;
|
pub mod daemon;
|
||||||
pub mod protocol;
|
pub mod protocol;
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
pub mod treedb;
|
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use std::{
|
||||||
|
|
||||||
use tempfile::TempDir;
|
use tempfile::TempDir;
|
||||||
|
|
||||||
use crate::treedb::{Database, LoadedDB};
|
use treedb::{Database, LoadedDB};
|
||||||
|
|
||||||
pub struct Fixture {
|
pub struct Fixture {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue