sl4f_lib/common_utils/
common.rs1use anyhow::{format_err, Error};
6use fidl::endpoints::DiscoverableProtocolMarker;
7use fuchsia_component::client::connect_to_protocol;
8use once_cell::sync::OnceCell;
9use serde_json::Value;
10use std::fs::read_dir;
11use std::path::{Path, PathBuf};
12use zx::{Status, Vmo};
13
14pub mod macros {
15 pub use crate::{fx_err_and_bail, parse_arg, with_line};
16}
17
18#[macro_export]
19macro_rules! parse_arg {
20 ($args:ident, $func:ident, $name:expr) => {
21 match $args.get($name) {
22 Some(v) => match v.$func() {
23 Some(val) => Ok(val),
24 None => Err($crate::common_utils::error::Sl4fError::new(
25 format!("malformed {}", $name).as_str(),
26 )),
27 },
28 None => Err($crate::common_utils::error::Sl4fError::new(
29 format!("{} missing", $name).as_str(),
30 )),
31 }
32 };
33}
34
35#[macro_export]
36macro_rules! with_line {
37 ($tag:expr) => {
38 format!("{}:{}", $tag, line!()).as_str()
39 };
40}
41
42#[macro_export]
43macro_rules! fx_err_and_bail {
44 ($tag:expr, $msg:expr) => {{
45 log::error!(tag = $tag; "{}", $msg);
46 return Err(format_err!($msg));
47 }};
48}
49
50pub fn parse_identifier(args_raw: Value) -> Result<String, Error> {
51 let id_raw = match args_raw.get("identifier") {
52 Some(id) => id,
53 None => return Err(format_err!("Connect peripheral identifier missing")),
54 };
55
56 let id = id_raw.as_str().map(String::from);
57
58 match id {
59 Some(id) => Ok(id),
60 None => return Err(format_err!("Identifier missing")),
61 }
62}
63
64pub fn parse_identifier_as_u64(args_raw: &Value) -> Result<u64, Error> {
65 let id = parse_arg!(args_raw, as_str, "identifier")?;
66 id.parse::<u64>().map_err(|_| format_err!("Cannot cast to u64: {}", id))
67}
68
69pub fn parse_service_identifier(args_raw: Value) -> Result<u64, Error> {
70 parse_arg!(args_raw, as_u64, "service_identifier").map_err(Into::into)
71}
72
73pub fn parse_u64_identifier(args_raw: Value) -> Result<u64, Error> {
74 parse_arg!(args_raw, as_u64, "identifier").map_err(Into::into)
75}
76
77pub fn parse_offset(args_raw: Value) -> Result<u64, Error> {
78 parse_arg!(args_raw, as_u64, "offset").map_err(Into::into)
79}
80
81pub fn parse_max_bytes(args_raw: Value) -> Result<u64, Error> {
82 parse_arg!(args_raw, as_u64, "max_bytes").map_err(Into::into)
83}
84
85pub fn parse_psm(args_raw: Value) -> Result<u64, Error> {
86 parse_arg!(args_raw, as_u64, "psm").map_err(Into::into)
87}
88
89pub fn parse_write_value(args_raw: Value) -> Result<Vec<u8>, Error> {
90 let arr = parse_arg!(args_raw, as_array, "write_value")?;
91 let mut vector: Vec<u8> = Vec::new();
92 for value in arr.into_iter() {
93 match value.as_u64() {
94 Some(num) => vector.push(num as u8),
95 None => {}
96 };
97 }
98 Ok(vector)
99}
100
101#[derive(Debug)]
102pub struct LazyProxy<P: DiscoverableProtocolMarker>(OnceCell<P::Proxy>);
103
104impl<P: DiscoverableProtocolMarker> Default for LazyProxy<P> {
105 fn default() -> Self {
106 Self(OnceCell::default())
107 }
108}
109
110impl<P> LazyProxy<P>
111where
112 P: DiscoverableProtocolMarker,
113 P::Proxy: Clone,
114{
115 pub fn get_or_connect(&self) -> Result<P::Proxy, Error> {
116 let p: &P::Proxy = self.0.get_or_try_init(|| connect_to_protocol::<P>())?;
117 Ok(p.clone())
118 }
119
120 #[cfg(test)]
121 pub fn set(&self, proxy: P::Proxy) -> Result<(), P::Proxy> {
122 self.0.set(proxy)
123 }
124}
125
126pub fn find_file(dir: &Path, pattern: &str) -> Result<PathBuf, Status> {
127 for entry in read_dir(dir)? {
128 let path = entry?.path();
129 if path.ends_with(pattern) {
130 return Ok(path);
131 }
132
133 if let Ok(res) = find_file(&path, pattern) {
134 return Ok(res);
135 }
136 }
137 Err(Status::INTERNAL)
138}
139
140const JSON_VMO_SIZE_OFFSET: u64 = 0;
142
143const JSON_VMO_DATA_OFFSET: u64 = std::mem::size_of::<u64>() as u64;
145
146pub fn write_json_to_vmo(vmo: &Vmo, value: &Value) -> Result<(), Error> {
151 let value_str = serde_json::to_string(value)?;
152 let len = value_str.len() as u64;
153 let len_bytes = len.to_le_bytes();
154 vmo.set_size(JSON_VMO_DATA_OFFSET + len)?;
155 vmo.write(&len_bytes, JSON_VMO_SIZE_OFFSET)?;
156 vmo.write(value_str.as_bytes(), JSON_VMO_DATA_OFFSET)?;
157 Ok(())
158}
159
160pub fn read_json_from_vmo(vmo: &Vmo) -> Result<Value, Error> {
165 let mut len_bytes = [0u8; std::mem::size_of::<u64>()];
166 vmo.read(&mut len_bytes, JSON_VMO_SIZE_OFFSET)?;
167 let len = u64::from_le_bytes(len_bytes);
168 let mut value_bytes = vec![0u8; len as usize];
169 vmo.read(&mut value_bytes, JSON_VMO_DATA_OFFSET)?;
170 Ok(serde_json::from_str(std::str::from_utf8(&value_bytes)?)?)
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176 use serde_json::json;
177 use zx::VmoOptions;
178
179 #[test]
180 fn json_to_and_from_vmo() -> Result<(), Error> {
181 let expected_value = json!([null, true, 1.5, "string", { "key": "value" }]);
182 let vmo = Vmo::create_with_opts(VmoOptions::RESIZABLE, 0)?;
183 write_json_to_vmo(&vmo, &expected_value)?;
184 let value = read_json_from_vmo(&vmo)?;
185 assert_eq!(value, expected_value);
186 Ok(())
187 }
188}