cnx_contrib/widgets/
disk_usage.rs1use anyhow::Result;
2use byte_unit::{Byte, ByteUnit};
3use cnx::text::{Attributes, Text};
4use cnx::widgets::{Widget, WidgetStream};
5use nix::sys::statvfs::statvfs;
6use std::time::Duration;
7use tokio::time;
8use tokio_stream::wrappers::IntervalStream;
9use tokio_stream::StreamExt;
10
11#[derive(Debug)]
13pub struct DiskInfo {
14 pub total: Byte,
16 pub used: Byte,
18 pub free: Byte,
20}
21
22impl DiskInfo {
23 fn new(path: &str) -> Result<Self> {
24 let stat = statvfs(path)?;
25 let total_size = stat.blocks() * stat.fragment_size();
26 let used = (stat.blocks() - stat.blocks_free()) * stat.fragment_size();
27 let available = stat.blocks_available() * stat.fragment_size();
28 let total = byte_unit::Byte::from_bytes(total_size as u128);
29 let used = byte_unit::Byte::from_bytes(used as u128);
30 let free: Byte = byte_unit::Byte::from_bytes(available as u128);
31
32 let disk_info = DiskInfo { total, used, free };
33 Ok(disk_info)
34 }
35}
36
37pub struct DiskUsage {
40 attr: Attributes,
41 path: String,
42 render: Option<Box<dyn Fn(DiskInfo) -> String>>,
43}
44
45impl DiskUsage {
46 pub fn new(
85 attr: Attributes,
86 path: String,
87 render: Option<Box<dyn Fn(DiskInfo) -> String>>,
88 ) -> Self {
89 Self { attr, render, path }
90 }
91
92 fn tick(&self) -> Result<Vec<Text>> {
93 let disk_info = DiskInfo::new(self.path.as_ref())?;
94 let disk_default_str = format!(
95 "Disk: {}/{}",
96 disk_info.used.get_adjusted_unit(ByteUnit::GiB).format(0),
97 disk_info.total.get_adjusted_unit(ByteUnit::GiB).format(0)
98 );
99
100 let text: String = self
101 .render
102 .as_ref()
103 .map_or(disk_default_str, |disk| (disk)(disk_info));
104 let texts = vec![Text {
105 attr: self.attr.clone(),
106 text,
107 stretch: false,
108 markup: true,
109 }];
110 Ok(texts)
111 }
112}
113
114impl Widget for DiskUsage {
115 fn into_stream(self: Box<Self>) -> Result<WidgetStream> {
116 let one_hour = Duration::from_secs(3600);
117 let interval = time::interval(one_hour);
118 let stream = IntervalStream::new(interval).map(move |_| self.tick());
119
120 Ok(Box::pin(stream))
121 }
122}