blob: 479a0650b5e6626755c32997f267823b1175d120 [file] [log] [blame]
Brian Silverman7edd1ce2022-07-23 16:10:54 -07001use std::path::Path;
2
3use thiserror::Error;
4
5use aos_configuration_fbs::aos::Configuration as RustConfiguration;
6use aos_flatbuffers::{Flatbuffer, NonSizePrefixedFlatbuffer};
7
8autocxx::include_cpp! (
9#include "aos/configuration.h"
10#include "aos/configuration_for_rust.h"
11#include "aos/configuration_generated.h"
12
13safety!(unsafe)
14
15generate!("aos::Configuration")
16generate!("aos::Channel")
17generate!("aos::Node")
18block!("flatbuffers::String")
19block!("flatbuffers::Verifier")
20
21generate!("aos::configuration::GetChannelForRust")
22);
23
24#[cxx::bridge]
25mod ffi2 {
26 #[namespace = "aos::configuration"]
27 unsafe extern "C++" {
28 include!("aos/configuration_for_rust.h");
29 fn MaybeReadConfigForRust(path: &str, extra_import_paths: &[&str]) -> Vec<u8>;
30 }
31}
32
33pub use ffi::aos::{Channel, Configuration, Node};
34
35#[derive(Clone, Copy, Eq, PartialEq, Debug, Error)]
36pub enum ChannelLookupError {
37 #[error("channel not found")]
38 NotFound,
39}
40
41#[derive(Clone, Copy, Eq, PartialEq, Debug, Error)]
42pub enum ReadConfigError {
43 #[error("duplicate imports or invalid paths")]
44 ReadFailed,
45}
46
47impl Configuration {
48 pub fn get_channel(
49 &self,
50 name: &str,
51 typename: &str,
52 application_name: &str,
53 node: &Node,
54 ) -> Result<&Channel, ChannelLookupError> {
55 // SAFETY: All the input references are valid pointers, and we're not doing anything with
56 // the result yet. It doesn't store any of the input references.
57 let channel = unsafe {
58 ffi::aos::configuration::GetChannelForRust(self, name, typename, application_name, node)
59 };
60 if channel.is_null() {
61 Err(ChannelLookupError::NotFound)
62 } else {
63 // SAFETY: We know this is a valid pointer now, and we're returning it with the lifetime
64 // inherited from `configuration` which owns it.
65 Ok(unsafe { &*channel })
66 }
67 }
68}
69
70/// # Panics
71///
72/// `path` must be valid UTF-8.
73pub fn read_config_from(
74 path: &Path,
75) -> Result<impl Flatbuffer<RustConfiguration<'static>>, ReadConfigError> {
76 read_config_from_import_paths(path, &[])
77}
78
79/// # Panics
80///
81/// `path` and all members of `extra_import_paths` must be valid UTF-8.
82pub fn read_config_from_import_paths(
83 path: &Path,
84 extra_import_paths: &[&Path],
85) -> Result<impl Flatbuffer<RustConfiguration<'static>>, ReadConfigError> {
86 let extra_import_paths: Vec<_> = extra_import_paths
87 .iter()
88 .map(|p| p.to_str().expect("Paths must be UTF-8"))
89 .collect();
90 let buffer = ffi2::MaybeReadConfigForRust(
91 path.to_str().expect("Paths must be UTF-8"),
92 &extra_import_paths,
93 );
94 if buffer.is_empty() {
95 return Err(ReadConfigError::ReadFailed);
96 }
97 // SAFETY: The C++ code returns a valid flatbuffer (unless it returned an error, which we
98 // checked above).
99 return Ok(unsafe { NonSizePrefixedFlatbuffer::new_unchecked(buffer) });
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[test]
107 fn read_config() {
108 let config = read_config_from(Path::new("aos/testdata/config1.json")).unwrap();
109 assert!(
110 config
111 .message()
112 .channels()
113 .unwrap()
114 .iter()
115 .find(|channel| channel.type_() == Some(".aos.bar"))
116 .is_some(),
117 "Failed to find the .aos.bar channel: {:?}",
118 config.message()
119 );
120 }
121}