diff --git a/.bazelignore b/.bazelignore new file mode 100644 index 000000000..378eac25d --- /dev/null +++ b/.bazelignore @@ -0,0 +1 @@ +build diff --git a/.bazelversion b/.bazelversion new file mode 100644 index 000000000..19b860c18 --- /dev/null +++ b/.bazelversion @@ -0,0 +1 @@ +6.4.0 diff --git a/.github/workflows/bazel_build.yaml b/.github/workflows/bazel_build.yaml new file mode 100644 index 000000000..d7a83f57b --- /dev/null +++ b/.github/workflows/bazel_build.yaml @@ -0,0 +1,18 @@ +name: Bazel Build +run-name: ${{ github.actor }} is building with bazel +on: [pull_request] +jobs: + bazel-build: + runs-on: ubuntu-22.04 + steps: + - run: echo branch name is ${{ github.ref }} + - name: Checkout + uses: actions/checkout@v4.1.0 + - name: Mount bazel cache + uses: actions/cache@v3 + with: + path: "~/.cache/bazel" + key: ${{ runner.os }}-bazel-${{ hashFiles('.bazelversion', '.bazelrc', 'WORKSPACE.bazel', 'third-party/bazel/*') }} + - name: Build all + run: > + bazelisk build //... diff --git a/.gitignore b/.gitignore index 60c93294b..a73a331aa 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ .cache/ workspace.yaml .vscode/ +/bazel-* diff --git a/BUILD.bazel b/BUILD.bazel new file mode 100644 index 000000000..b92f9be5d --- /dev/null +++ b/BUILD.bazel @@ -0,0 +1,16 @@ +package(default_visibility = ["//visibility:public"]) + +filegroup( + name = "types", + srcs = glob(["types/**/*.yaml"]), +) + +filegroup( + name = "interfaces", + srcs = glob(["interfaces/**/*.yaml"]), +) + +filegroup( + name = "errors", + srcs = glob(["errors/**/*.yaml"]), +) diff --git a/WORKSPACE.bazel b/WORKSPACE.bazel new file mode 100644 index 000000000..22498f7a3 --- /dev/null +++ b/WORKSPACE.bazel @@ -0,0 +1,59 @@ +workspace(name = "everest-core") + +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") + +http_archive( + name = "rules_rust", + sha256 = "36ab8f9facae745c9c9c1b33d225623d976e78f2cc3f729b7973d8c20934ab95", + urls = ["https://github.com/bazelbuild/rules_rust/releases/download/0.31.0/rules_rust-v0.31.0.tar.gz"], +) + +load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains") + +rules_rust_dependencies() + +rust_register_toolchains( + versions = ["1.74.0"], +) + +load("@rules_rust//crate_universe:repositories.bzl", "crate_universe_dependencies") + +crate_universe_dependencies() + +load("@rules_rust//crate_universe:defs.bzl", "crates_repository", "crate") + +crates_repository( + name = "crate_index", + cargo_lockfile = "//modules/rust_examples:Cargo.lock", + isolated = False, + manifests = [ + "//modules/rust_examples:Cargo.toml", + "//modules/rust_examples/RsExample:Cargo.toml", + "//modules/rust_examples/RsExampleUser:Cargo.toml", + ], + annotations = { + "everestrs": [crate.annotation( + crate_features = ["build_bazel"], + )], + }, +) + +load("@crate_index//:defs.bzl", "crate_repositories") + +crate_repositories() + +load("//third-party/bazel:repos.bzl", "everest_core_repos") + +everest_core_repos() + +load("//third-party/bazel:defs.bzl", "everest_core_defs") + +everest_core_defs() + +load("@everest-framework//third-party/bazel:repos.bzl", "everest_framework_repos") + +everest_framework_repos() + +load("@everest-framework//third-party/bazel:defs.bzl", "everest_framework_deps") + +everest_framework_deps() diff --git a/cmake/everest-generate.cmake b/cmake/everest-generate.cmake index 43ed1756f..53d75f729 100644 --- a/cmake/everest-generate.cmake +++ b/cmake/everest-generate.cmake @@ -150,6 +150,8 @@ if (EVEREST_ENABLE_RS_SUPPORT) echo "[workspace.dependencies]" >> Cargo.toml COMMAND echo "everestrs = { path = \"$\" }" >> Cargo.toml + COMMAND + echo "everestrs-build = { path = \"$\" }" >> Cargo.toml COMMAND echo $ > .everestrs_link_dependencies COMMAND diff --git a/modules/rust_examples/BUILD.bazel b/modules/rust_examples/BUILD.bazel new file mode 100644 index 000000000..e69de29bb diff --git a/modules/rust_examples/Cargo.lock b/modules/rust_examples/Cargo.lock index 847d784bf..1ae93386d 100644 --- a/modules/rust_examples/Cargo.lock +++ b/modules/rust_examples/Cargo.lock @@ -2,6 +2,32 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "RsExample" +version = "0.1.0" +dependencies = [ + "everestrs", + "everestrs-build", + "serde", + "serde_json", +] + +[[package]] +name = "RsExampleUser" +version = "0.1.0" +dependencies = [ + "everestrs", + "everestrs-build", + "serde", + "serde_json", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + [[package]] name = "argh" version = "0.1.12" @@ -42,11 +68,20 @@ dependencies = [ "libc", ] +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cxx" -version = "1.0.107" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbe98ba1789d56fb3db3bee5e032774d4f421b685de7ba703643584ba24effbe" +checksum = "7129e341034ecb940c9072817cd9007974ea696844fc4dd582dc1653a7fbe2e8" dependencies = [ "cc", "cxxbridge-flags", @@ -56,32 +91,71 @@ dependencies = [ [[package]] name = "cxxbridge-flags" -version = "1.0.107" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20888d9e1d2298e2ff473cee30efe7d5036e437857ab68bbfea84c74dba91da2" +checksum = "06fdd177fc61050d63f67f5bd6351fac6ab5526694ea8e359cd9cd3b75857f44" [[package]] name = "cxxbridge-macro" -version = "1.0.107" +version = "1.0.110" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fa16a70dd58129e4dfffdff535fb1bce66673f7bbeec4a5a1765a504e1ccd84" +checksum = "587663dd5fb3d10932c8aecfe7c844db1bcf0aee93eeab08fac13dc1212c2e7f" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "everestrs" version = "0.1.0" +source = "git+https://github.com/everest/everest-framework.git?rev=21b5371ec38158ddfcbe9eea0f92a18154ca3c76#21b5371ec38158ddfcbe9eea0f92a18154ca3c76" dependencies = [ "argh", "cxx", + "everestrs-build", + "log", "serde", "serde_json", "thiserror", ] +[[package]] +name = "everestrs-build" +version = "0.1.0" +source = "git+https://github.com/everest/everest-framework.git?rev=21b5371ec38158ddfcbe9eea0f92a18154ca3c76#21b5371ec38158ddfcbe9eea0f92a18154ca3c76" +dependencies = [ + "anyhow", + "argh", + "convert_case", + "minijinja", + "serde", + "serde_json", + "serde_yaml", +] + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" + +[[package]] +name = "indexmap" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "itoa" version = "1.0.9" @@ -90,9 +164,9 @@ checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "libc" -version = "0.2.147" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "link-cplusplus" @@ -104,39 +178,36 @@ dependencies = [ ] [[package]] -name = "proc-macro2" -version = "1.0.66" +name = "log" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] -name = "quote" -version = "1.0.33" +name = "minijinja" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "208758577ef2c86cf5dd3e85730d161413ec3284e2d73b2ef65d9a24d9971bcb" dependencies = [ - "proc-macro2", + "serde", ] [[package]] -name = "rs_example" -version = "0.1.0" +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" dependencies = [ - "everestrs", - "serde", - "serde_json", + "unicode-ident", ] [[package]] -name = "rs_example_user" -version = "0.1.0" +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ - "everestrs", - "serde", - "serde_json", + "proc-macro2", ] [[package]] @@ -147,18 +218,18 @@ checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "serde" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.188" +version = "1.0.193" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" dependencies = [ "proc-macro2", "quote", @@ -167,20 +238,33 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.107" +version = "1.0.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65" +checksum = "3cc7a1570e38322cfe4154732e5110f887ea57e22b76f4bfd32b5bdd3368666c" dependencies = [ + "indexmap", "itoa", "ryu", "serde", + "unsafe-libyaml", ] [[package]] name = "syn" -version = "2.0.31" +version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "718fa2415bcb8d8bd775917a1bf12a7931b6dfa890753378538118181e0cb398" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", @@ -189,18 +273,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d6d7a740b8a666a7e828dd00da9c0dc290dff53154ea77ac109281de90589b7" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.48" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" dependencies = [ "proc-macro2", "quote", @@ -209,6 +293,18 @@ dependencies = [ [[package]] name = "unicode-ident" -version = "1.0.11" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa" diff --git a/modules/rust_examples/Cargo.toml b/modules/rust_examples/Cargo.toml index ee1c27877..9381e5e7e 100644 --- a/modules/rust_examples/Cargo.toml +++ b/modules/rust_examples/Cargo.toml @@ -6,6 +6,5 @@ members = [ ] [workspace.dependencies] -# This is not used when building with cmake, but you can change this for local -# development to point to a directory that works for you to build with cargo. -everestrs = { path = "../everest-framework/everestrs/everestrs" } +everestrs = { git = "https://github.com/everest/everest-framework.git", rev = "21b5371ec38158ddfcbe9eea0f92a18154ca3c76" } +everestrs-build = { git = "https://github.com/everest/everest-framework.git", rev = "21b5371ec38158ddfcbe9eea0f92a18154ca3c76" } diff --git a/modules/rust_examples/RsExample/BUILD.bazel b/modules/rust_examples/RsExample/BUILD.bazel new file mode 100644 index 000000000..912542029 --- /dev/null +++ b/modules/rust_examples/RsExample/BUILD.bazel @@ -0,0 +1,31 @@ +load("@rules_rust//rust:defs.bzl", "rust_binary") +load("@rules_rust//cargo:defs.bzl", "cargo_build_script") +load("@crate_index//:defs.bzl", "all_crate_deps") + +cargo_build_script( + name = "build_script", + srcs = ["build.rs"], + edition="2021", + build_script_env = { + "EVEREST_CORE_ROOT": "../../../", + }, + deps = all_crate_deps(build=True), + data= [ + "//:types", + "//:interfaces", + "manifest.yaml", + ], +) + +rust_binary( + name = "RsExample", + srcs = glob(["src/**/*.rs"]), + visibility = ["//visibility:public"], + edition = "2021", + deps = all_crate_deps() + [ + "@everest-framework//everestrs/everestrs:everestrs_sys", + "@everest-framework//everestrs/everestrs:everestrs_bridge", + ":build_script", + ], + proc_macro_deps = all_crate_deps(proc_macro=True), +) diff --git a/modules/rust_examples/RsExample/Cargo.toml b/modules/rust_examples/RsExample/Cargo.toml index dc8b6807e..7a708d04e 100644 --- a/modules/rust_examples/RsExample/Cargo.toml +++ b/modules/rust_examples/RsExample/Cargo.toml @@ -7,3 +7,6 @@ edition = "2021" everestrs = { workspace = true } serde = { version = "1.0.175", features = ["derive"] } serde_json = "1" + +[build-dependencies] +everestrs-build = { workspace = true } diff --git a/modules/rust_examples/RsExample/build.rs b/modules/rust_examples/RsExample/build.rs new file mode 100644 index 000000000..f55e422e2 --- /dev/null +++ b/modules/rust_examples/RsExample/build.rs @@ -0,0 +1,13 @@ +use everestrs_build::Builder; + +pub fn main() { + Builder::new( + "manifest.yaml", + vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())], + ) + .generate() + .unwrap(); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=manifest.yaml"); +} diff --git a/modules/rust_examples/RsExample/manifest.yaml b/modules/rust_examples/RsExample/manifest.yaml index 8f810beab..e3e1c56f3 100644 --- a/modules/rust_examples/RsExample/manifest.yaml +++ b/modules/rust_examples/RsExample/manifest.yaml @@ -31,3 +31,4 @@ metadata: license: https://opensource.org/licenses/Apache-2.0 authors: - Holger Rapp +enable_external_mqtt: false \ No newline at end of file diff --git a/modules/rust_examples/RsExample/src/eventually_generated.rs b/modules/rust_examples/RsExample/src/eventually_generated.rs deleted file mode 100644 index eefc63f8f..000000000 --- a/modules/rust_examples/RsExample/src/eventually_generated.rs +++ /dev/null @@ -1,415 +0,0 @@ -//! The things which should be eventually generated from the `manifest`. -//! -//! The naming convention should be: -//! `INTERFACE_NAME` `SERVER|CLIENT` `PUBLISHER|SUBSCRIBER`. -//! -//! The server publisher implements the publisher for the variables. The -//! server subscriber is trait where the user implements the RPC callbacks. -//! The client publisher implements interface for calling the rcps of other -//! servers. The client subscriber is a trait where the user implements -//! callbacks for changed variables. - -#![allow(clippy::let_unit_value, clippy::useless_conversion)] - -use everestrs::{Error, Result, Runtime, Subscriber}; -use std::collections::HashMap; -use std::pin::Pin; -use std::sync::Arc; - -/// Struct holding the config for the `foobar` interface. -#[derive(Debug)] -pub struct FoobarConfig { - /// An interface level bool config. - pub some_bool_config: bool, - - /// An interface level integer config. - pub some_integer_config: i64, -} - -/// Struct holding the module level config. -#[derive(Debug)] -pub struct ModuleConfig { - /// A module level string config. - pub some_string_config: String, - - /// A module level number config - pub some_number_config: f64, - - /// The config for the `foobar` interface. - pub foobar_config: FoobarConfig, -} - -/// Returns the config for the whole module. You can call this function at any -/// time in your code. -pub fn get_config() -> ModuleConfig { - let raw_config = everestrs::get_module_configs(); - let foobar_config = FoobarConfig { - some_bool_config: raw_config - .get("foobar") - .unwrap() - .get("some_bool_config") - .unwrap() - .try_into() - .unwrap(), - - some_integer_config: raw_config - .get("foobar") - .unwrap() - .get("some_integer_config") - .unwrap() - .try_into() - .unwrap(), - }; - - ModuleConfig { - some_string_config: raw_config - .get("!module") - .unwrap() - .get("some_string_config") - .unwrap() - .try_into() - .unwrap(), - - some_number_config: raw_config - .get("!module") - .unwrap() - .get("some_number_config") - .unwrap() - .try_into() - .unwrap(), - foobar_config, - } -} - -/// The trait for the user to provide. Always part of the generated code. -pub trait OnReadySubscriber: Sync + Send { - fn on_ready(&self, pub_impl: &ModulePublisher); -} - -/// The trait for the user to implement. Generated from the -/// `manifest.provides.foobar`. -pub trait ExampleServiceSubscriber: Sync + Send { - /// This command checks if something is stored under a given key - /// - /// `pub_impl`: Handle to all publishers. - /// `key`: Key to check the existence for - /// - /// Returns: Returns 'True' if something was stored for this key*/ - fn uses_something(&self, pub_impl: &ModulePublisher, key: String) -> Result; -} - -/// The publisher generated from `manifest.provides.foobar`. -#[derive(Clone)] -#[allow(unused)] -pub struct ExampleServicePublisher { - runtime: Pin>, -} - -impl ExampleServicePublisher { - pub fn max_current(&self, max_current: f64) -> Result<()> { - self.runtime - .publish_variable("foobar", "max_current", &max_current); - Ok(()) - } -} - -/// The trait for the user to implement. Generated from -/// `manifest.provides.my_store`. -pub trait KvsServiceSubscriber: Sync + Send { - /// This command removes the value stored under a given key - /// - /// `pub_impl`: Handle to all publishers. - /// `key`: Key to delete the value for - fn delete(&self, pub_impl: &ModulePublisher, key: String) -> Result<()>; - - /// This command checks if something is stored under a given key - /// - /// `pub_impl`: Handle to all publishers. - /// `key`: Key to check the existence for - /// - /// Returns: Returns 'True' if something was stored for this key*/ - fn exists(&self, pub_impl: &ModulePublisher, key: String) -> Result; - - /// This command loads the previously stored value for a given key (it will return null if the - /// key does not exist) - /// - /// `pub_impl`: Handle to all publishers. - /// `key`: Key to load the value for - /// - /// Returns: The previously stored value - fn load(&self, pub_impl: &ModulePublisher, key: String) -> Result; - - /// This command stores a value under a given key - /// - /// `pub_impl`: Handle to all publishers. - /// `key`: Key to store the value for - /// `value`: Value to store - fn store( - &self, - pub_impl: &ModulePublisher, - key: String, - value: ::serde_json::Value, - ) -> Result<()>; -} - -/// The publisher for the KvsService. Generated from `manifest.provides.my_store`. -/// -/// The Kvs does not publish anything - so this struct is a Noop. Eventually -/// the code generation might be smart enough to "optimize" such things away but -/// here we include it for completeness. -#[derive(Clone)] -pub struct KvsServicePublisher { - #[allow(unused)] - runtime: Pin>, -} - -impl KvsServicePublisher {} - -/// The subscriber for the KvsClient. Generated from -/// `manifest.requires.their_store`. -/// -/// The Kvs does not publish any variables, therefore the subscriber is empty. -/// Eventually our code generation might be smart enough to omit this, but here -/// we include it for completeness. -pub trait KvsClientSubscriber: Sync + Send {} - -/// This interface defines a simple key-value-store interface. Generated for -/// `manifest.requires.their_store`. -#[derive(Clone)] -pub struct KvsClientPublisher { - runtime: Pin>, -} - -impl KvsClientPublisher { - /// This command removes the value stored under a given key - /// - /// `key`: Key to delete the value for - pub fn delete(&self, key: String) -> Result<()> { - let args = serde_json::json!({ - "key": key, - }); - let blob = self.runtime.call_command("their_store", "delete", &args); - ::serde_json::from_value(blob).map_err(|_| Error::InvalidArgument("return_value")) - } - - /// This command checks if something is stored under a given key - /// - /// `key`: Key to check the existence for - /// - /// Returns: Returns 'True' if something was stored for this key*/ - pub fn exists(&self, key: String) -> Result { - let args = serde_json::json!({ - "key": key, - }); - let blob = self.runtime.call_command("their_store", "exists", &args); - ::serde_json::from_value(blob).map_err(|_| Error::InvalidArgument("return_value")) - } - - /// This command loads the previously stored value for a given key (it will return null if the - /// key does not exist) - /// - /// `key`: Key to load the value for - /// - /// Returns: The previously stored value - pub fn load(&self, key: String) -> Result { - let args = serde_json::json!({ - "key": key, - }); - let blob = self.runtime.call_command("their_store", "load", &args); - ::serde_json::from_value(blob).map_err(|_| Error::InvalidArgument("return_value")) - } - - /// This command stores a value under a given key - /// - /// `key`: Key to store the value for - /// `value`: Value to store - pub fn store(&self, key: String, value: ::serde_json::Value) -> Result<()> { - let args = serde_json::json!({ - "key": key, - "value": value, - }); - let blob = self.runtime.call_command("their_store", "store", &args); - ::serde_json::from_value(blob).map_err(|_| Error::InvalidArgument("return_value")) - } -} - -pub struct ModulePublisher { - pub foobar: ExampleServicePublisher, - pub my_store: KvsServicePublisher, - pub their_store: KvsClientPublisher, -} - -#[allow(dead_code)] -pub struct Module { - /// All subscribers - on_ready: Arc, - foobar: Arc, - my_store: Arc, - their_store: Arc, - - // The publisher - publisher: ModulePublisher, -} - -impl Module { - #[must_use] - pub fn new( - on_ready: Arc, - foobar: Arc, - my_store: Arc, - their_store: Arc, - ) -> Arc { - let runtime = Runtime::new(); - let this = Arc::new(Self { - on_ready, - foobar, - my_store, - their_store, - publisher: ModulePublisher { - foobar: ExampleServicePublisher { - runtime: runtime.clone(), - }, - my_store: KvsServicePublisher { - runtime: runtime.clone(), - }, - their_store: KvsClientPublisher { - runtime: runtime.clone(), - }, - }, - }); - - runtime - .as_ref() - .set_subscriber(Arc::::downgrade(&this)); - - this - } -} - -impl Subscriber for Module { - #[allow(unused_variables)] - fn handle_command( - &self, - implementation_id: &str, - cmd_name: &str, - parameters: HashMap, - ) -> ::everestrs::Result { - match implementation_id { - "foobar" => { - foobar::handle_command(&self.publisher, self.foobar.as_ref(), cmd_name, parameters) - } - "my_store" => my_store::handle_command( - &self.publisher, - self.my_store.as_ref(), - cmd_name, - parameters, - ), - _ => Err(everestrs::Error::InvalidArgument( - "Unknown implementation_id called.", - )), - } - } - - #[allow(unused_variables)] - fn handle_variable( - &self, - implementation_id: &str, - name: &str, - value: serde_json::Value, - ) -> ::everestrs::Result<()> { - Err(everestrs::Error::InvalidArgument( - "Unknown variable received.", - )) - } - - fn on_ready(&self) { - self.on_ready.on_ready(&self.publisher) - } -} - -mod foobar { - use std::collections::HashMap; - - #[allow(unused_variables)] - pub(super) fn handle_command( - pub_impl: &super::ModulePublisher, - foobar_service: &dyn super::ExampleServiceSubscriber, - cmd_name: &str, - mut parameters: HashMap, - ) -> ::everestrs::Result { - match cmd_name { - "uses_something" => { - let key: String = ::serde_json::from_value( - parameters - .remove("key") - .ok_or(everestrs::Error::MissingArgument("key"))?, - ) - .map_err(|_| everestrs::Error::InvalidArgument("key"))?; - let retval = foobar_service.uses_something(pub_impl, key)?; - Ok(retval.into()) - } - _ => Err(everestrs::Error::InvalidArgument("Unknown command called.")), - } - } -} - -mod my_store { - use std::collections::HashMap; - - pub(super) fn handle_command( - pub_impl: &super::ModulePublisher, - my_store_service: &dyn super::KvsServiceSubscriber, - cmd_name: &str, - mut parameters: HashMap, - ) -> ::everestrs::Result { - match cmd_name { - "delete" => { - let key: String = ::serde_json::from_value( - parameters - .remove("key") - .ok_or(everestrs::Error::MissingArgument("key"))?, - ) - .map_err(|_| everestrs::Error::InvalidArgument("key"))?; - let retval = my_store_service.delete(pub_impl, key)?; - Ok(retval.into()) - } - "exists" => { - let key: String = ::serde_json::from_value( - parameters - .remove("key") - .ok_or(everestrs::Error::MissingArgument("key"))?, - ) - .map_err(|_| everestrs::Error::InvalidArgument("key"))?; - let retval = my_store_service.exists(pub_impl, key)?; - Ok(retval.into()) - } - "load" => { - let key: String = ::serde_json::from_value( - parameters - .remove("key") - .ok_or(everestrs::Error::MissingArgument("key"))?, - ) - .map_err(|_| everestrs::Error::InvalidArgument("key"))?; - let retval = my_store_service.load(pub_impl, key)?; - Ok(retval.into()) - } - "store" => { - let key: String = ::serde_json::from_value( - parameters - .remove("key") - .ok_or(everestrs::Error::MissingArgument("key"))?, - ) - .map_err(|_| everestrs::Error::InvalidArgument("key"))?; - let value: ::serde_json::Value = ::serde_json::from_value( - parameters - .remove("value") - .ok_or(everestrs::Error::MissingArgument("value"))?, - ) - .map_err(|_| everestrs::Error::InvalidArgument("value"))?; - let retval = my_store_service.store(pub_impl, key, value)?; - Ok(retval.into()) - } - _ => Err(everestrs::Error::InvalidArgument("Unknown command called.")), - } - } -} diff --git a/modules/rust_examples/RsExample/src/main.rs b/modules/rust_examples/RsExample/src/main.rs index 3d3a88488..16af881a6 100644 --- a/modules/rust_examples/RsExample/src/main.rs +++ b/modules/rust_examples/RsExample/src/main.rs @@ -1,11 +1,11 @@ // EVerest expects binaries to be CamelCased, and Rust wants them to be snake_case. We yield to // EVerest and shut up the compiler warning. #![allow(non_snake_case)] +include!(concat!(env!("OUT_DIR"), "/generated.rs")); -mod eventually_generated; -use eventually_generated::{ - get_config, ExampleServiceSubscriber, KvsClientSubscriber, KvsServiceSubscriber, Module, - ModulePublisher, OnReadySubscriber, +use generated::{ + get_config, Context, ExampleServiceSubscriber, KvsClientSubscriber, KvsServiceSubscriber, + Module, ModulePublisher, OnReadySubscriber, }; use std::sync::Arc; use std::{thread, time}; @@ -15,47 +15,44 @@ pub struct OneClass {} impl KvsServiceSubscriber for OneClass { fn store( &self, - pub_impl: &ModulePublisher, + context: &Context, key: String, value: serde_json::Value, ) -> ::everestrs::Result<()> { - pub_impl.their_store.store(key, value) + context.publisher.their_store.store(key, value) } - fn load( - &self, - pub_impl: &ModulePublisher, - key: String, - ) -> ::everestrs::Result { - pub_impl.their_store.load(key) + fn load(&self, context: &Context, key: String) -> ::everestrs::Result { + context.publisher.their_store.load(key) } - fn delete(&self, pub_impl: &ModulePublisher, key: String) -> ::everestrs::Result<()> { - pub_impl.their_store.delete(key) + fn delete(&self, context: &Context, key: String) -> ::everestrs::Result<()> { + context.publisher.their_store.delete(key) } - fn exists(&self, pub_impl: &ModulePublisher, key: String) -> ::everestrs::Result { - pub_impl.their_store.exists(key) + fn exists(&self, context: &Context, key: String) -> ::everestrs::Result { + context.publisher.their_store.exists(key) } } impl ExampleServiceSubscriber for OneClass { - fn uses_something(&self, pub_impl: &ModulePublisher, key: String) -> ::everestrs::Result { - if !pub_impl.their_store.exists(key.clone())? { + fn uses_something(&self, context: &Context, key: String) -> ::everestrs::Result { + if !context.publisher.their_store.exists(key.clone())? { println!("IT SHOULD NOT AND DOES NOT EXIST"); } let test_array = vec![1, 2, 3]; - pub_impl + context + .publisher .their_store .store(key.clone(), test_array.clone().into())?; - let exi = pub_impl.their_store.exists(key.clone())?; + let exi = context.publisher.their_store.exists(key.clone())?; if exi { println!("IT ACTUALLY EXISTS"); } - let ret: Vec = serde_json::from_value(pub_impl.their_store.load(key)?) + let ret: Vec = serde_json::from_value(context.publisher.their_store.load(key)?) .expect("Wanted an array as return value"); println!("loaded array: {ret:?}, original array: {test_array:?}"); diff --git a/modules/rust_examples/RsExampleUser/BUILD.bazel b/modules/rust_examples/RsExampleUser/BUILD.bazel new file mode 100644 index 000000000..88625bcce --- /dev/null +++ b/modules/rust_examples/RsExampleUser/BUILD.bazel @@ -0,0 +1,31 @@ +load("@rules_rust//rust:defs.bzl", "rust_binary") +load("@rules_rust//cargo:defs.bzl", "cargo_build_script") +load("@crate_index//:defs.bzl", "all_crate_deps") + +cargo_build_script( + name = "build_script", + srcs = ["build.rs"], + edition="2021", + build_script_env = { + "EVEREST_CORE_ROOT": "../../../", + }, + deps = all_crate_deps(build=True), + data= [ + "//:types", + "//:interfaces", + "manifest.yaml", + ], +) + +rust_binary( + name = "RsExampleUser", + srcs = glob(["src/**/*.rs"]), + visibility = ["//visibility:public"], + edition = "2021", + deps = all_crate_deps() + [ + "@everest-framework//everestrs/everestrs:everestrs_sys", + "@everest-framework//everestrs/everestrs:everestrs_bridge", + ":build_script", + ], + proc_macro_deps = all_crate_deps(proc_macro=True), +) diff --git a/modules/rust_examples/RsExampleUser/Cargo.toml b/modules/rust_examples/RsExampleUser/Cargo.toml index 2af483b31..d0ca215eb 100644 --- a/modules/rust_examples/RsExampleUser/Cargo.toml +++ b/modules/rust_examples/RsExampleUser/Cargo.toml @@ -7,3 +7,6 @@ edition = "2021" everestrs = { workspace = true } serde = { version = "1.0.175", features = ["derive"] } serde_json = "1" + +[build-dependencies] +everestrs-build = { workspace = true } diff --git a/modules/rust_examples/RsExampleUser/build.rs b/modules/rust_examples/RsExampleUser/build.rs new file mode 100644 index 000000000..f55e422e2 --- /dev/null +++ b/modules/rust_examples/RsExampleUser/build.rs @@ -0,0 +1,13 @@ +use everestrs_build::Builder; + +pub fn main() { + Builder::new( + "manifest.yaml", + vec![std::env::var("EVEREST_CORE_ROOT").unwrap_or("../../..".to_string())], + ) + .generate() + .unwrap(); + + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=manifest.yaml"); +} diff --git a/modules/rust_examples/RsExampleUser/manifest.yaml b/modules/rust_examples/RsExampleUser/manifest.yaml index 186bf8d26..e05721ea8 100644 --- a/modules/rust_examples/RsExampleUser/manifest.yaml +++ b/modules/rust_examples/RsExampleUser/manifest.yaml @@ -12,3 +12,4 @@ metadata: license: https://opensource.org/licenses/Apache-2.0 authors: - Holger Rapp +enable_external_mqtt: false diff --git a/modules/rust_examples/RsExampleUser/src/eventually_generated.rs b/modules/rust_examples/RsExampleUser/src/eventually_generated.rs deleted file mode 100644 index ddaac99f8..000000000 --- a/modules/rust_examples/RsExampleUser/src/eventually_generated.rs +++ /dev/null @@ -1,179 +0,0 @@ -use everestrs::{Error, Result, Runtime, Subscriber}; -use std::collections::HashMap; -use std::pin::Pin; -use std::sync::Arc; - -/// Trait for the user to implement. -pub trait OnReadySubscriber: Sync + Send { - fn on_ready(&self, publishers: &ModulePublisher); -} - -/// Generated from `manifest.provides.main`. At best we will omit empty traits. -pub trait ExampleUserServiceSubscriber: Sync + Send {} - -/// Generated from `manifest.provides.main`. At best we will omit empty structs. -#[derive(Clone)] -pub struct ExampleUserServicePublisher { - #[allow(unused)] - runtime: Pin>, -} - -impl ExampleUserServicePublisher {} - -/// Generated from `manifest.requires.their_example` and -/// `manifest.requires.another_example`. -trait ExampleClientSubscriber { - fn max_current(&self, publishers: &ModulePublisher, max_current: f64); -} - -/// The publisher for the example module. The class is clone-able and just holds -/// a shared-ptr to the cpp implementation. -#[derive(Clone)] -pub struct ExampleClientPublisher { - runtime: Pin>, -} - -impl ExampleClientPublisher { - /// This command checks if something is stored under a given key - /// - /// `key`: Key to check the existence for - /// - /// Returns: Returns 'True' if something was stored for this key - pub fn uses_something(&self, key: String) -> Result { - let args = serde_json::json!({ - "key": key, - }); - let blob = self - .runtime - .call_command("their_example", "uses_something", &args); - let return_value: bool = - ::serde_json::from_value(blob).map_err(|_| Error::InvalidArgument("return_value"))?; - Ok(return_value) - } -} - -/// The collection of all publishers for this module. -#[derive(Clone)] -pub struct ModulePublisher { - pub main: ExampleUserServicePublisher, - pub their_example: ExampleClientPublisher, - pub another_example: ExampleClientPublisher, -} - -/// Trait for the user to implement. -pub trait ExampleSubscriber: Sync + Send { - fn on_max_current(&self, publishers: &ModulePublisher, value: f64); -} - -/// The struct holding everything necessary for the module to run. -/// -/// If the user may drop the provided subscriber and the code will still work. -/// The user may furthermore drop the [Module] - in this case we will stop -/// receiving callbacks - however the cloned publisher will continue to work -/// until dropped. -#[allow(dead_code)] -pub struct Module { - /// All subscribers. - on_ready: Arc, - main: Arc, - their_example: Arc, - another_example: Arc, - - /// The publisher. - publisher: ModulePublisher, -} - -impl Module { - #[must_use] - pub fn new( - on_ready: Arc, - main: Arc, - their_example: Arc, - another_example: Arc, - ) -> Arc { - let runtime = Runtime::new(); - let publisher = ModulePublisher { - main: ExampleUserServicePublisher { - runtime: runtime.clone(), - }, - their_example: ExampleClientPublisher { - runtime: runtime.clone(), - }, - another_example: ExampleClientPublisher { - runtime: runtime.clone(), - }, - }; - - let this = Arc::new(Self { - on_ready, - main, - their_example, - another_example, - publisher, - }); - runtime - .as_ref() - .set_subscriber(Arc::::downgrade(&this)); - this - } -} - -impl Subscriber for Module { - fn handle_command( - &self, - _implementation_id: &str, - _name: &str, - _parameters: HashMap, - ) -> Result { - Err(Error::InvalidArgument("Unknown command called received.")) - } - - fn handle_variable( - &self, - implementation_id: &str, - name: &str, - value: serde_json::Value, - ) -> Result<()> { - match implementation_id { - "their_example" => example_interface::handle_variable( - &self.publisher, - self.their_example.as_ref(), - name, - value, - ), - "another_example" => example_interface::handle_variable( - &self.publisher, - self.another_example.as_ref(), - name, - value, - ), - _ => Err(Error::InvalidArgument("Unknown variable received.")), - } - } - - fn on_ready(&self) { - self.on_ready.on_ready(&self.publisher) - } -} - -mod example_interface { - // use Runtime; - use super::*; - - pub(super) fn handle_variable( - publishers: &super::ModulePublisher, - their_example_subscriber: &T, - name: &str, - value: serde_json::Value, - ) -> Result<()> { - match name { - "max_current" => { - let v: f64 = ::serde_json::from_value(value) - .map_err(|_| Error::InvalidArgument("max_current"))?; - their_example_subscriber.on_max_current(publishers, v); - Ok(()) - } - _ => Err(Error::InvalidArgument("Unknown variable received.")), - } - } -} diff --git a/modules/rust_examples/RsExampleUser/src/main.rs b/modules/rust_examples/RsExampleUser/src/main.rs index 0c6e55b45..5c7220cfc 100644 --- a/modules/rust_examples/RsExampleUser/src/main.rs +++ b/modules/rust_examples/RsExampleUser/src/main.rs @@ -1,11 +1,15 @@ // EVerest expects binaries to be CamelCased, and Rust wants them to be snake_case. We yield to // EVerest and shut up the compiler warning. #![allow(non_snake_case)] +include!(concat!(env!("OUT_DIR"), "/generated.rs")); use std::sync::{Arc, Mutex}; use std::{thread, time}; -mod eventually_generated; +use generated::{ + Context, ExampleClientSubscriber, ExampleUserServiceSubscriber, Module, ModulePublisher, + OnReadySubscriber, +}; struct ExampleClient { max_current: Mutex>, @@ -21,17 +25,18 @@ impl ExampleClient { } } -impl eventually_generated::ExampleSubscriber for ExampleClient { - fn on_max_current(&self, publishers: &eventually_generated::ModulePublisher, value: f64) { +impl ExampleClientSubscriber for ExampleClient { + fn on_max_current(&self, context: &Context, value: f64) { println!("Received the value {value}"); - let _ = publishers + let _ = context + .publisher .their_example .uses_something("hello_there".to_string()); *self.max_current.lock().unwrap() = Some(value); // Example where we start a thread with the publisher. The cloning is // only done if the user wants to offload the publisher into a thread. - let clone = publishers.clone(); + let clone = context.publisher.clone(); *self.thread.lock().unwrap() = Some(thread::spawn(move || { let _ = clone.another_example.uses_something("foo".to_string()); })) @@ -39,16 +44,16 @@ impl eventually_generated::ExampleSubscriber for ExampleClient { } struct MainService {} -impl eventually_generated::ExampleUserServiceSubscriber for MainService {} +impl ExampleUserServiceSubscriber for MainService {} -struct Module { +struct OurModule { their_example: Arc, another_example: Arc, min_current: Mutex>, } -impl eventually_generated::OnReadySubscriber for Module { - fn on_ready(&self, _pub_impl: &eventually_generated::ModulePublisher) { +impl OnReadySubscriber for OurModule { + fn on_ready(&self, _pub_impl: &ModulePublisher) { let mut their_current = self.their_example.max_current.lock().unwrap(); let mut another_current = self.another_example.max_current.lock().unwrap(); *their_current = Some(1.); @@ -62,13 +67,13 @@ fn main() { let their_example = Arc::new(ExampleClient::new()); let another_example = Arc::new(ExampleClient::new()); let main_service = Arc::new(MainService {}); - let module = Arc::new(Module { + let our_module = Arc::new(OurModule { their_example: their_example.clone(), another_example: another_example.clone(), min_current: Mutex::new(None), }); - let _module = eventually_generated::Module::new( - module.clone(), + let _module = Module::new( + our_module.clone(), main_service.clone(), their_example.clone(), another_example.clone(), diff --git a/third-party/bazel/BUILD.bazel b/third-party/bazel/BUILD.bazel new file mode 100644 index 000000000..5503fa2c1 --- /dev/null +++ b/third-party/bazel/BUILD.bazel @@ -0,0 +1,14 @@ +[ + alias( + name = "boost_{}".format(name), + actual = "@boost//:{}".format(name), + visibility = ["//visibility:public"], + ) + for name in [ + "program_options", + "log", + "current_function", + "uuid", + "exception", + ] +] diff --git a/third-party/bazel/defs.bzl b/third-party/bazel/defs.bzl new file mode 100644 index 000000000..c01c5081e --- /dev/null +++ b/third-party/bazel/defs.bzl @@ -0,0 +1,4 @@ +load("@com_github_nelhage_rules_boost//:boost/boost.bzl", "boost_deps") + +def everest_core_defs(): + boost_deps() diff --git a/third-party/bazel/repos.bzl b/third-party/bazel/repos.bzl new file mode 100644 index 000000000..22953512f --- /dev/null +++ b/third-party/bazel/repos.bzl @@ -0,0 +1,18 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +def everest_core_repos(): + maybe( + http_archive, + name = "com_github_nelhage_rules_boost", + url = "https://github.com/nelhage/rules_boost/archive/4ab574f9a84b42b1809978114a4664184716f4bf.tar.gz", + sha256 = "2215e6910eb763a971b1f63f53c45c0f2b7607df38c96287666d94d954da8cdc", + strip_prefix = "rules_boost-4ab574f9a84b42b1809978114a4664184716f4bf", + ) + + maybe( + http_archive, + name = "everest-framework", + url = "https://github.com/everest/everest-framework/archive/21b5371ec38158ddfcbe9eea0f92a18154ca3c76.tar.gz", + strip_prefix = "everest-framework-21b5371ec38158ddfcbe9eea0f92a18154ca3c76", + )