use async_trait::async_trait;
use fuchsia_inspect::{LazyNode, Node};
use futures::FutureExt;
use std::collections::hash_map::HashMap;
use std::sync::{Arc, Mutex, Weak};
const INFO_NODE_NAME: &'static str = "fs.info";
const USAGE_NODE_NAME: &'static str = "fs.usage";
const VOLUMES_NODE_NAME: &'static str = "fs.volumes";
#[async_trait]
pub trait FsInspect {
fn get_info_data(&self) -> InfoData;
async fn get_usage_data(&self) -> UsageData;
}
#[async_trait]
pub trait FsInspectVolume {
async fn get_volume_data(&self) -> VolumeData;
}
pub struct FsInspectTree {
_info: LazyNode,
_usage: LazyNode,
_volumes: LazyNode,
volumes_tracker: Arc<Mutex<HashMap<String, Weak<dyn FsInspectVolume + Send + Sync + 'static>>>>,
}
impl FsInspectTree {
pub fn new(fs: Weak<dyn FsInspect + Send + Sync + 'static>, root: &Node) -> FsInspectTree {
let fs_clone = fs.clone();
let info_node = root.create_lazy_child(INFO_NODE_NAME, move || {
let fs_clone = fs_clone.clone();
async move {
let inspector = fuchsia_inspect::Inspector::default();
if let Some(fs) = fs_clone.upgrade() {
fs.get_info_data().record_into(inspector.root());
}
Ok(inspector)
}
.boxed()
});
let fs_clone = fs.clone();
let usage_node = root.create_lazy_child(USAGE_NODE_NAME, move || {
let fs_clone = fs_clone.clone();
async move {
let inspector = fuchsia_inspect::Inspector::default();
if let Some(fs) = fs_clone.upgrade() {
fs.get_usage_data().await.record_into(inspector.root());
}
Ok(inspector)
}
.boxed()
});
let volumes_tracker = Arc::new(Mutex::new(HashMap::<
String,
Weak<dyn FsInspectVolume + Send + Sync + 'static>,
>::new()));
let tracker_weak = Arc::downgrade(&volumes_tracker);
let volumes_node = root.create_lazy_child(VOLUMES_NODE_NAME, move || {
let tracker_ref = tracker_weak.clone();
async move {
let inspector = fuchsia_inspect::Inspector::default();
let root = inspector.root();
let tracker = match tracker_ref.upgrade() {
Some(tracker) => tracker,
None => return Ok(inspector),
};
let volumes = {
let tracker = tracker.lock().unwrap();
let mut volumes = Vec::with_capacity(tracker.len());
for (name, volume) in tracker.iter() {
volumes.push((name.clone(), volume.clone()));
}
volumes
};
for (name, volume_weak) in volumes {
let volume = match volume_weak.upgrade() {
Some(v) => v,
None => continue,
};
let child = root.create_child(name.clone());
volume.get_volume_data().await.record_into(&child);
root.record(child);
}
Ok(inspector)
}
.boxed()
});
FsInspectTree {
_info: info_node,
_usage: usage_node,
_volumes: volumes_node,
volumes_tracker,
}
}
pub fn register_volume(
self: &Arc<Self>,
name: String,
volume: Weak<dyn FsInspectVolume + Send + Sync + 'static>,
) {
self.volumes_tracker.lock().unwrap().insert(name, volume);
}
pub fn unregister_volume(&self, name: String) {
self.volumes_tracker.lock().unwrap().remove(&name);
}
}
pub struct InfoData {
pub id: u64,
pub fs_type: u64,
pub name: String,
pub version_major: u64,
pub version_minor: u64,
pub block_size: u64,
pub max_filename_length: u64,
pub oldest_version: Option<(u64, u64)>,
}
impl InfoData {
fn record_into(self, node: &Node) {
node.record_uint("id", self.id);
node.record_uint("type", self.fs_type);
node.record_string("name", self.name);
node.record_uint("version_major", self.version_major);
node.record_uint("version_minor", self.version_minor);
node.record_string(
"current_version",
format!("{}.{}", self.version_major, self.version_minor),
);
node.record_uint("block_size", self.block_size);
node.record_uint("max_filename_length", self.max_filename_length);
if let Some((major, minor)) = self.oldest_version {
node.record_uint("oldest_version_major", major);
node.record_uint("oldest_version_minor", minor);
}
}
}
pub struct UsageData {
pub total_bytes: u64,
pub used_bytes: u64,
pub total_nodes: u64,
pub used_nodes: u64,
}
impl UsageData {
fn record_into(self, node: &Node) {
node.record_uint("total_bytes", self.total_bytes);
node.record_uint("used_bytes", self.used_bytes);
node.record_uint("total_nodes", self.total_nodes);
node.record_uint("used_nodes", self.used_nodes);
}
}
pub struct VolumeData {
pub used_bytes: u64,
pub bytes_limit: Option<u64>,
pub used_nodes: u64,
pub encrypted: bool,
pub port_koid: u64,
}
impl VolumeData {
fn record_into(self, node: &Node) {
node.record_uint("used_bytes", self.used_bytes);
if let Some(bytes_limit) = self.bytes_limit {
node.record_uint("bytes_limit", bytes_limit);
}
node.record_uint("used_nodes", self.used_nodes);
node.record_bool("encrypted", self.encrypted);
node.record_uint("port_koid", self.port_koid);
}
}