blob: 4d3fddaf2a03893e89167faebbe745e4f3885125 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::{format_err, Error};
use fidl::endpoints::DiscoverableProtocolMarker;
use fuchsia_component::client::connect_to_protocol;
use fuchsia_zircon::{Status, Vmo};
use once_cell::sync::OnceCell;
use serde_json::Value;
use std::fs::read_dir;
use std::path::{Path, PathBuf};
pub mod macros {
pub use crate::fx_err_and_bail;
pub use crate::parse_arg;
pub use crate::with_line;
}
#[macro_export]
macro_rules! parse_arg {
($args:ident, $func:ident, $name:expr) => {
match $args.get($name) {
Some(v) => match v.$func() {
Some(val) => Ok(val),
None => Err($crate::common_utils::error::Sl4fError::new(
format!("malformed {}", $name).as_str(),
)),
},
None => Err($crate::common_utils::error::Sl4fError::new(
format!("{} missing", $name).as_str(),
)),
}
};
}
#[macro_export]
macro_rules! with_line {
($tag:expr) => {
format!("{}:{}", $tag, line!()).as_str()
};
}
#[macro_export]
macro_rules! fx_err_and_bail {
($tag:expr, $msg:expr) => {{
tracing::error!(tag = $tag, "{}", $msg);
return Err(format_err!($msg));
}};
}
pub fn parse_identifier(args_raw: Value) -> Result<String, Error> {
let id_raw = match args_raw.get("identifier") {
Some(id) => id,
None => return Err(format_err!("Connect peripheral identifier missing")),
};
let id = id_raw.as_str().map(String::from);
match id {
Some(id) => Ok(id),
None => return Err(format_err!("Identifier missing")),
}
}
pub fn parse_identifier_as_u64(args_raw: &Value) -> Result<u64, Error> {
let id = parse_arg!(args_raw, as_str, "identifier")?;
id.parse::<u64>().map_err(|_| format_err!("Cannot cast to u64: {}", id))
}
pub fn parse_service_identifier(args_raw: Value) -> Result<u64, Error> {
parse_arg!(args_raw, as_u64, "service_identifier").map_err(Into::into)
}
pub fn parse_u64_identifier(args_raw: Value) -> Result<u64, Error> {
parse_arg!(args_raw, as_u64, "identifier").map_err(Into::into)
}
pub fn parse_offset(args_raw: Value) -> Result<u64, Error> {
parse_arg!(args_raw, as_u64, "offset").map_err(Into::into)
}
pub fn parse_max_bytes(args_raw: Value) -> Result<u64, Error> {
parse_arg!(args_raw, as_u64, "max_bytes").map_err(Into::into)
}
pub fn parse_psm(args_raw: Value) -> Result<u64, Error> {
parse_arg!(args_raw, as_u64, "psm").map_err(Into::into)
}
pub fn parse_write_value(args_raw: Value) -> Result<Vec<u8>, Error> {
let arr = parse_arg!(args_raw, as_array, "write_value")?;
let mut vector: Vec<u8> = Vec::new();
for value in arr.into_iter() {
match value.as_u64() {
Some(num) => vector.push(num as u8),
None => {}
};
}
Ok(vector)
}
#[derive(Debug)]
pub struct LazyProxy<P: DiscoverableProtocolMarker>(OnceCell<P::Proxy>);
impl<P: DiscoverableProtocolMarker> Default for LazyProxy<P> {
fn default() -> Self {
Self(OnceCell::default())
}
}
impl<P> LazyProxy<P>
where
P: DiscoverableProtocolMarker,
P::Proxy: Clone,
{
pub fn get_or_connect(&self) -> Result<P::Proxy, Error> {
let p: &P::Proxy = self.0.get_or_try_init(|| connect_to_protocol::<P>())?;
Ok(p.clone())
}
#[cfg(test)]
pub fn set(&self, proxy: P::Proxy) -> Result<(), P::Proxy> {
self.0.set(proxy)
}
}
pub fn find_file(dir: &Path, pattern: &str) -> Result<PathBuf, Status> {
for entry in read_dir(dir)? {
let path = entry?.path();
if path.ends_with(pattern) {
return Ok(path);
}
if let Ok(res) = find_file(&path, pattern) {
return Ok(res);
}
}
Err(Status::INTERNAL)
}
/// The offset of the 8-byte size of the JSON data in the VMO.
const JSON_VMO_SIZE_OFFSET: u64 = 0;
/// The offset of the JSON data in the VMO.
const JSON_VMO_DATA_OFFSET: u64 = std::mem::size_of::<u64>() as u64;
/// Write an arbitrary JSON Value into a VMO. The Value can be recovered via read_json_from_vmo().
/// # Arguments
/// * 'vmo' - The VMO into which to write the JSON Value.
/// * 'value' - The JSON Value to write.
pub fn write_json_to_vmo(vmo: &Vmo, value: &Value) -> Result<(), Error> {
let value_str = serde_json::to_string(value)?;
let len = value_str.len() as u64;
let len_bytes = len.to_le_bytes();
vmo.set_size(JSON_VMO_DATA_OFFSET + len)?;
vmo.write(&len_bytes, JSON_VMO_SIZE_OFFSET)?;
vmo.write(value_str.as_bytes(), JSON_VMO_DATA_OFFSET)?;
Ok(())
}
/// Read an arbitrary JSON Value from a VMO. The Value must previously have been written into the
/// VMO by write_json_to_vmo().
/// # Arguments
/// * 'vmo' - The VMO from which the JSON Value is to be read.
pub fn read_json_from_vmo(vmo: &Vmo) -> Result<Value, Error> {
let mut len_bytes = [0u8; std::mem::size_of::<u64>()];
vmo.read(&mut len_bytes, JSON_VMO_SIZE_OFFSET)?;
let len = u64::from_le_bytes(len_bytes);
let mut value_bytes = vec![0u8; len as usize];
vmo.read(&mut value_bytes, JSON_VMO_DATA_OFFSET)?;
Ok(serde_json::from_str(std::str::from_utf8(&value_bytes)?)?)
}
#[cfg(test)]
mod tests {
use super::*;
use fuchsia_zircon::VmoOptions;
use serde_json::json;
#[test]
fn json_to_and_from_vmo() -> Result<(), Error> {
let expected_value = json!([null, true, 1.5, "string", { "key": "value" }]);
let vmo = Vmo::create_with_opts(VmoOptions::RESIZABLE, 0)?;
write_json_to_vmo(&vmo, &expected_value)?;
let value = read_json_from_vmo(&vmo)?;
assert_eq!(value, expected_value);
Ok(())
}
}