Create a Rust API for part of //aos:configuration
Change-Id: I00044634f437e72487aaaca28e9a00fe65a4aa48
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
diff --git a/aos/BUILD b/aos/BUILD
index 9394295..1380f7d 100644
--- a/aos/BUILD
+++ b/aos/BUILD
@@ -1,5 +1,6 @@
load("@com_github_google_flatbuffers//:build_defs.bzl", "flatbuffer_cc_library", "flatbuffer_py_library", "flatbuffer_rust_library", "flatbuffer_ts_library")
load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
+load("//tools/build_rules:autocxx.bzl", "autocxx_library")
exports_files(["aos_dump_autocomplete.sh"])
@@ -222,6 +223,14 @@
visibility = ["//visibility:public"],
)
+flatbuffer_rust_library(
+ name = "configuration_rust_fbs",
+ srcs = ["configuration.fbs"],
+ crate_name = "aos_configuration_fbs",
+ target_compatible_with = ["//tools/platforms/rust:has_support"],
+ visibility = ["//visibility:public"],
+)
+
cc_library(
name = "configuration",
srcs = [
@@ -246,6 +255,51 @@
],
)
+cc_library(
+ name = "configuration_for_rust",
+ srcs = [
+ "configuration_for_rust.cc",
+ ],
+ hdrs = [
+ "configuration_for_rust.h",
+ ],
+ deps = [
+ ":configuration",
+ ":for_rust",
+ "//third_party/cargo:cxx_cc",
+ ],
+)
+
+autocxx_library(
+ name = "configuration_rs",
+ srcs = ["configuration.rs"],
+ crate_name = "aos_configuration",
+ libs = [
+ ":configuration",
+ ":configuration_for_rust",
+ ":configuration_fbs",
+ ],
+ override_cc_toolchain = "@llvm_toolchain//:cc-clang-x86_64-linux",
+ target_compatible_with = ["//tools/platforms/rust:has_support"],
+ visibility = ["//visibility:public"],
+ deps = [
+ ":configuration_rust_fbs",
+ ":flatbuffers_rs",
+ "//third_party/cargo:thiserror",
+ ],
+)
+
+rust_test(
+ name = "configuration_rs_test",
+ crate = ":configuration_rs",
+ data = [
+ "//aos/testdata:test_configs",
+ ],
+ # TODO: Make Rust play happy with pic vs nopic. Details at:
+ # https://github.com/bazelbuild/rules_rust/issues/118
+ rustc_flags = ["-Crelocation-model=static"],
+)
+
flatbuffer_ts_library(
name = "json_to_flatbuffer_fbs_ts",
srcs = ["json_to_flatbuffer.fbs"],
@@ -637,3 +691,14 @@
"@com_github_google_glog//:glog",
],
)
+
+cc_library(
+ name = "for_rust",
+ hdrs = [
+ "for_rust.h",
+ ],
+ visibility = ["//visibility:public"],
+ deps = [
+ "//third_party/cargo:cxx_cc",
+ ],
+)
diff --git a/aos/configuration.rs b/aos/configuration.rs
new file mode 100644
index 0000000..479a065
--- /dev/null
+++ b/aos/configuration.rs
@@ -0,0 +1,121 @@
+use std::path::Path;
+
+use thiserror::Error;
+
+use aos_configuration_fbs::aos::Configuration as RustConfiguration;
+use aos_flatbuffers::{Flatbuffer, NonSizePrefixedFlatbuffer};
+
+autocxx::include_cpp! (
+#include "aos/configuration.h"
+#include "aos/configuration_for_rust.h"
+#include "aos/configuration_generated.h"
+
+safety!(unsafe)
+
+generate!("aos::Configuration")
+generate!("aos::Channel")
+generate!("aos::Node")
+block!("flatbuffers::String")
+block!("flatbuffers::Verifier")
+
+generate!("aos::configuration::GetChannelForRust")
+);
+
+#[cxx::bridge]
+mod ffi2 {
+ #[namespace = "aos::configuration"]
+ unsafe extern "C++" {
+ include!("aos/configuration_for_rust.h");
+ fn MaybeReadConfigForRust(path: &str, extra_import_paths: &[&str]) -> Vec<u8>;
+ }
+}
+
+pub use ffi::aos::{Channel, Configuration, Node};
+
+#[derive(Clone, Copy, Eq, PartialEq, Debug, Error)]
+pub enum ChannelLookupError {
+ #[error("channel not found")]
+ NotFound,
+}
+
+#[derive(Clone, Copy, Eq, PartialEq, Debug, Error)]
+pub enum ReadConfigError {
+ #[error("duplicate imports or invalid paths")]
+ ReadFailed,
+}
+
+impl Configuration {
+ pub fn get_channel(
+ &self,
+ name: &str,
+ typename: &str,
+ application_name: &str,
+ node: &Node,
+ ) -> Result<&Channel, ChannelLookupError> {
+ // SAFETY: All the input references are valid pointers, and we're not doing anything with
+ // the result yet. It doesn't store any of the input references.
+ let channel = unsafe {
+ ffi::aos::configuration::GetChannelForRust(self, name, typename, application_name, node)
+ };
+ if channel.is_null() {
+ Err(ChannelLookupError::NotFound)
+ } else {
+ // SAFETY: We know this is a valid pointer now, and we're returning it with the lifetime
+ // inherited from `configuration` which owns it.
+ Ok(unsafe { &*channel })
+ }
+ }
+}
+
+/// # Panics
+///
+/// `path` must be valid UTF-8.
+pub fn read_config_from(
+ path: &Path,
+) -> Result<impl Flatbuffer<RustConfiguration<'static>>, ReadConfigError> {
+ read_config_from_import_paths(path, &[])
+}
+
+/// # Panics
+///
+/// `path` and all members of `extra_import_paths` must be valid UTF-8.
+pub fn read_config_from_import_paths(
+ path: &Path,
+ extra_import_paths: &[&Path],
+) -> Result<impl Flatbuffer<RustConfiguration<'static>>, ReadConfigError> {
+ let extra_import_paths: Vec<_> = extra_import_paths
+ .iter()
+ .map(|p| p.to_str().expect("Paths must be UTF-8"))
+ .collect();
+ let buffer = ffi2::MaybeReadConfigForRust(
+ path.to_str().expect("Paths must be UTF-8"),
+ &extra_import_paths,
+ );
+ if buffer.is_empty() {
+ return Err(ReadConfigError::ReadFailed);
+ }
+ // SAFETY: The C++ code returns a valid flatbuffer (unless it returned an error, which we
+ // checked above).
+ return Ok(unsafe { NonSizePrefixedFlatbuffer::new_unchecked(buffer) });
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn read_config() {
+ let config = read_config_from(Path::new("aos/testdata/config1.json")).unwrap();
+ assert!(
+ config
+ .message()
+ .channels()
+ .unwrap()
+ .iter()
+ .find(|channel| channel.type_() == Some(".aos.bar"))
+ .is_some(),
+ "Failed to find the .aos.bar channel: {:?}",
+ config.message()
+ );
+ }
+}
diff --git a/aos/configuration_for_rust.cc b/aos/configuration_for_rust.cc
new file mode 100644
index 0000000..3c3daad
--- /dev/null
+++ b/aos/configuration_for_rust.cc
@@ -0,0 +1,32 @@
+#include "aos/configuration_for_rust.h"
+
+#include "aos/for_rust.h"
+
+namespace aos::configuration {
+
+const Channel *GetChannelForRust(const Configuration *config, rust::Str name,
+ rust::Str type, rust::Str application_name,
+ const Node *node) {
+ return GetChannel(config, RustStrToStringView(name),
+ RustStrToStringView(type),
+ RustStrToStringView(application_name), node);
+}
+
+rust::Vec<uint8_t> MaybeReadConfigForRust(
+ rust::Str path, rust::Slice<const rust::Str> extra_import_paths) {
+ std::vector<std::string_view> cc_extra_import_paths;
+ for (const rust::Str &extra_import_path : extra_import_paths) {
+ cc_extra_import_paths.push_back(RustStrToStringView(extra_import_path));
+ }
+ const auto cc_result = MaybeReadConfig(RustStrToStringView(path));
+ rust::Vec<uint8_t> result;
+ if (cc_result) {
+ result.reserve(cc_result->span().size());
+ for (uint8_t b : cc_result->span()) {
+ result.push_back(b);
+ }
+ }
+ return result;
+}
+
+} // namespace aos::configuration
diff --git a/aos/configuration_for_rust.h b/aos/configuration_for_rust.h
new file mode 100644
index 0000000..ae18346
--- /dev/null
+++ b/aos/configuration_for_rust.h
@@ -0,0 +1,21 @@
+#ifndef AOS_CONFIGURATION_FOR_RUST_H_
+#define AOS_CONFIGURATION_FOR_RUST_H_
+
+#include "aos/configuration.h"
+#include "cxx.h"
+
+namespace aos::configuration {
+
+const Channel *GetChannelForRust(const Configuration *config, rust::Str name,
+ rust::Str type, rust::Str application_name,
+ const Node *node);
+
+// Returns a Configuration flatbuffer. Returns an empty vector on errors.
+// TODO: It would be nice to return more detailed errors (not found vs could not
+// parse vs merging error).
+rust::Vec<uint8_t> MaybeReadConfigForRust(
+ rust::Str path, rust::Slice<const rust::Str> extra_import_paths);
+
+} // namespace aos::configuration
+
+#endif // AOS_CONFIGURATION_FOR_RUST_H_
diff --git a/aos/for_rust.h b/aos/for_rust.h
new file mode 100644
index 0000000..388a854
--- /dev/null
+++ b/aos/for_rust.h
@@ -0,0 +1,23 @@
+#ifndef AOS_FOR_RUST_H_
+#define AOS_FOR_RUST_H_
+
+// This file has some shared utilities for the for_rust C++ code, which provides
+// autocxx-friendly versions of C++ APIs.
+
+#include <string_view>
+
+#include "cxx.h"
+
+namespace aos {
+
+inline rust::Str StringViewToRustStr(std::string_view s) {
+ return rust::Str(s.data(), s.size());
+}
+
+inline std::string_view RustStrToStringView(rust::Str s) {
+ return std::string_view(s.data(), s.size());
+}
+
+} // namespace aos
+
+#endif // AOS_FOR_RUST_H_