diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml new file mode 100644 index 0000000..7c89858 --- /dev/null +++ b/.github/workflows/build-and-test.yml @@ -0,0 +1,23 @@ +name: Build and test + +on: + pull_request: + workflow_dispatch: + +jobs: + build_test: + strategy: + matrix: + os: [ubuntu-latest, ubuntu-20.04, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: setup rust stable + run: curl https://sh.rustup.rs -sSf | sh -s -- -y + + - name: unit tests + run: | + cp tests/*.json . + cargo test --all --release \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index db2635c..e80b73b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pulse" -version = "0.0.3" +version = "0.0.4" authors = ["Jerboa"] edition="2021" @@ -33,6 +33,7 @@ serde = {version="1.0", features=["derive"]} serde_json = "1.0" reqwest = { version = "0.11", features = ["json"] } regex = "1.10.2" +semver = "1.0.20" [profile.dev] opt-level = 0 diff --git a/src/lib.rs b/src/lib.rs index 9fe5d2e..e6899d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,14 @@ +use semver::{BuildMetadata, Prerelease, Version}; + pub mod web; pub mod server; pub mod stats; pub mod util; +const MAJOR: &str = env!("CARGO_PKG_VERSION_MAJOR"); +const MINOR: &str = env!("CARGO_PKG_VERSION_MINOR"); +const PATCH: &str = env!("CARGO_PKG_VERSION_PATCH"); + const DEBUG: bool = true; pub fn debug(msg: String, context: Option) @@ -15,4 +21,16 @@ pub fn debug(msg: String, context: Option) None => println!("[DEBUG] {msg}") } +} + +pub fn program_version() -> Version +{ + Version + { + major: MAJOR.parse().unwrap(), + minor: MINOR.parse().unwrap(), + patch: PATCH.parse().unwrap(), + pre: Prerelease::EMPTY, + build: BuildMetadata::EMPTY + } } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index fff7843..f38e4e7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,8 +1,17 @@ use pulse::server::https::Server; +use pulse::program_version; #[tokio::main] async fn main() { + let args: Vec = std::env::args().collect(); + + if args.iter().any(|x| x == "-v") + { + println!("Version: {}", program_version()); + std::process::exit(0); + } + let server = Server::new(0,0,0,0); server.serve().await; diff --git a/src/main_http.rs b/src/main_http.rs index b4755d7..02c492c 100644 --- a/src/main_http.rs +++ b/src/main_http.rs @@ -1,11 +1,20 @@ #[cfg(feature = "http")] -use pulse::{server::http::ServerHttp, stats}; +use pulse::server::http::ServerHttp; +use pulse::program_version; #[cfg(feature = "http")] #[tokio::main] async fn main() { + let args: Vec = std::env::args().collect(); + + if args.iter().any(|x| x == "-v") + { + println!("Version: {}", program_version()); + std::process::exit(0); + } + let server = ServerHttp::new(0,0,0,0); server.serve().await; diff --git a/src/web/event.rs b/src/web/event.rs index 476a887..543a9b9 100644 --- a/src/web/event.rs +++ b/src/web/event.rs @@ -15,21 +15,32 @@ pub const CONFIG_PATH: &str = "event_config.json"; const TEMPLATE_REPLACE_REGEX: &str = "<[^<>]+>"; #[derive(Clone, Serialize, Deserialize)] -pub struct Template +pub struct Criterion { #[serde(default)] check_value_path: String, #[serde(default)] - check_value: String, + check_value_in: Vec, + #[serde(default)] + check_value_not_in: Vec +} + +#[derive(Clone, Serialize, Deserialize)] +pub struct Template +{ + #[serde(default="default_criteria")] + criteria: Vec, #[serde(default)] body: String } +fn default_criteria() -> Vec { Vec::::new() } + impl Template { pub fn new() -> Template { - Template {check_value_path: String::new(), check_value: String::new(), body: String::new()} + Template {criteria: Vec::::new(), body: String::new()} } } @@ -171,40 +182,73 @@ pub fn expand_template(template: String, data: HashMap, data: HashMap) -> String +pub fn satisfied(criterion: Criterion, data: &HashMap) -> bool { - if templates.is_empty() - { - return "".to_string() - } - else if templates.len() == 1 + + if criterion.check_value_path == "" { - return templates[0].body.clone() + return true } - for template in templates - { - let path: Vec<&str> = template.check_value_path.split("/").collect(); + let path: Vec<&str> = criterion.check_value_path.split("/").collect(); - let extracted_value= match path.len() + let extracted_value= match path.len() + { + 0 => None, + 1 => + { + if data.contains_key(path[0]) + { + Some(&data[path[0]]) + } + else + { + None + } + }, + _ => { - 0 => None, - 1 => Some(&data[path[0]]), - _ => + let p = ["/", &path[1..path.len()].join("/")].join(""); + if data.contains_key(path[0]) { - let p = ["/", &path[1..path.len()].join("/")].join(""); data[path[0]].pointer(&p) } - }; - - if extracted_value.is_none() - { - continue + else + { + None + } + } + }; + + if extracted_value.is_none() + { + return false + } + + let string_value = extracted_value.unwrap().to_string().replace("\"", ""); + + if (criterion.check_value_in.is_empty() || criterion.check_value_in.contains(&string_value)) && + (criterion.check_value_not_in.is_empty() || !criterion.check_value_not_in.contains(&string_value)) + { + return true + } + else + { + return false + } +} - let string_value = extracted_value.unwrap().to_string().replace("\"", ""); +pub fn select_template(templates: Vec