Create a Rust API for part of //aos:configuration
Change-Id: I00044634f437e72487aaaca28e9a00fe65a4aa48
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
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()
+ );
+ }
+}