Add more helpful Rust Configuration methods

Also switch where they're attached so they can be used with both
versions of Configuration we have in Rust.

Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I944cd0fc05db460573e9df694e812bdf0f31ffd7
diff --git a/aos/configuration.rs b/aos/configuration.rs
index 479a065..a264867 100644
--- a/aos/configuration.rs
+++ b/aos/configuration.rs
@@ -1,9 +1,9 @@
-use std::path::Path;
+use std::{path::Path, ptr};
 
 use thiserror::Error;
 
 use aos_configuration_fbs::aos::Configuration as RustConfiguration;
-use aos_flatbuffers::{Flatbuffer, NonSizePrefixedFlatbuffer};
+use aos_flatbuffers::{transmute_table_to, NonSizePrefixedFlatbuffer};
 
 autocxx::include_cpp! (
 #include "aos/configuration.h"
@@ -19,6 +19,7 @@
 block!("flatbuffers::Verifier")
 
 generate!("aos::configuration::GetChannelForRust")
+generate!("aos::configuration::GetNodeForRust")
 );
 
 #[cxx::bridge]
@@ -39,40 +40,76 @@
 }
 
 #[derive(Clone, Copy, Eq, PartialEq, Debug, Error)]
+pub enum NodeLookupError {
+    #[error("node 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,
+impl<'a> Into<&'a Configuration> for RustConfiguration<'a> {
+    fn into(self) -> &'a Configuration {
+        unsafe { transmute_table_to(&self._tab) }
+    }
+}
+
+/// A trait with useful helper methods for a `Configuration` table. Import this trait to get these
+/// methods on both the `Configuration` struct generated by autocxx from the C++ code generated by
+/// flatbuffers, and the `Configuration` struct generated by flatbuffers in Rust.
+pub trait ConfigurationExt<'a>: Into<&'a Configuration> {
+    fn get_channel(
+        self,
         name: &str,
         typename: &str,
         application_name: &str,
-        node: &Node,
-    ) -> Result<&Channel, ChannelLookupError> {
+        node: Option<&Node>,
+    ) -> Result<&'a 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)
+            ffi::aos::configuration::GetChannelForRust(
+                self.into(),
+                name,
+                typename,
+                application_name,
+                node.map_or(ptr::null(), |p| p),
+            )
         };
         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.
+            // inherited from `self` which owns it.
             Ok(unsafe { &*channel })
         }
     }
+
+    fn get_node(self, name: &str) -> Result<&'a Node, NodeLookupError> {
+        // 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 node = unsafe { ffi::aos::configuration::GetNodeForRust(self.into(), name) };
+        if node.is_null() {
+            Err(NodeLookupError::NotFound)
+        } else {
+            // SAFETY: We know this is a valid pointer now, and we're returning it with the lifetime
+            // inherited from `self` which owns it.
+            Ok(unsafe { &*node })
+        }
+    }
 }
 
+impl<'a, T: Into<&'a Configuration>> ConfigurationExt<'a> for T {}
+
 /// # Panics
 ///
 /// `path` must be valid UTF-8.
 pub fn read_config_from(
     path: &Path,
-) -> Result<impl Flatbuffer<RustConfiguration<'static>>, ReadConfigError> {
+) -> Result<NonSizePrefixedFlatbuffer<RustConfiguration<'static>, Vec<u8>>, ReadConfigError> {
     read_config_from_import_paths(path, &[])
 }
 
@@ -82,7 +119,7 @@
 pub fn read_config_from_import_paths(
     path: &Path,
     extra_import_paths: &[&Path],
-) -> Result<impl Flatbuffer<RustConfiguration<'static>>, ReadConfigError> {
+) -> Result<NonSizePrefixedFlatbuffer<RustConfiguration<'static>, Vec<u8>>, ReadConfigError> {
     let extra_import_paths: Vec<_> = extra_import_paths
         .iter()
         .map(|p| p.to_str().expect("Paths must be UTF-8"))
@@ -103,6 +140,8 @@
 mod tests {
     use super::*;
 
+    use aos_flatbuffers::Flatbuffer;
+
     #[test]
     fn read_config() {
         let config = read_config_from(Path::new("aos/testdata/config1.json")).unwrap();