diff --git a/packages/client-api/src/providers/create-provider.ts b/packages/client-api/src/providers/create-provider.ts index fccd79f5..541211cf 100644 --- a/packages/client-api/src/providers/create-provider.ts +++ b/packages/client-api/src/providers/create-provider.ts @@ -50,6 +50,8 @@ import type { WeatherProviderConfig, WeatherProvider, } from './weather/weather-provider-types'; +import { createDiskProvider } from './disk/create-disk-provider'; +import type { DiskProvider, DiskProviderConfig } from './disk/disk-provider-types'; export interface ProviderConfigMap { battery: BatteryProviderConfig; @@ -63,6 +65,7 @@ export interface ProviderConfigMap { network: NetworkProviderConfig; weather: WeatherProviderConfig; keyboard: KeyboardProviderConfig; + disk: DiskProviderConfig; } export interface ProviderMap { @@ -77,6 +80,7 @@ export interface ProviderMap { network: NetworkProvider; weather: WeatherProvider; keyboard: KeyboardProvider; + disk: DiskProvider; } export type ProviderType = keyof ProviderConfigMap; @@ -122,6 +126,8 @@ export function createProvider( return createWeatherProvider(config) as any; case 'keyboard': return createKeyboardProvider(config) as any; + case 'disk': + return createDiskProvider(config) as any; default: throw new Error('Not a supported provider type.'); } diff --git a/packages/client-api/src/providers/disk/create-disk-provider.ts b/packages/client-api/src/providers/disk/create-disk-provider.ts new file mode 100644 index 00000000..df2103be --- /dev/null +++ b/packages/client-api/src/providers/disk/create-disk-provider.ts @@ -0,0 +1,30 @@ +import { z } from 'zod'; + +import { createBaseProvider } from '../create-base-provider'; +import { onProviderEmit } from '~/desktop'; +import type { + DiskOutput, + DiskProvider, + DiskProviderConfig, +} from './disk-provider-types'; + +const diskProviderConfigSchema = z.object({ + type: z.literal('disk'), + refreshInterval: z.coerce.number().default(60 * 1000), +}); + +export function createDiskProvider( + config: DiskProviderConfig, +): DiskProvider { + const mergedConfig = diskProviderConfigSchema.parse(config); + + return createBaseProvider(mergedConfig, async queue => { + return onProviderEmit(mergedConfig, ({ result }) => { + if ('error' in result) { + queue.error(result.error); + } else { + queue.output(result.output); + } + }); + }); +} diff --git a/packages/client-api/src/providers/disk/disk-provider-types.ts b/packages/client-api/src/providers/disk/disk-provider-types.ts new file mode 100644 index 00000000..a8b7bf7d --- /dev/null +++ b/packages/client-api/src/providers/disk/disk-provider-types.ts @@ -0,0 +1,26 @@ +import type { Provider } from '../create-base-provider'; + +export interface DiskProviderConfig { + type: 'disk'; + + /** + * How often this provider refreshes in milliseconds. + */ + refreshInterval?: number; +} + +export type DiskProvider = Provider; + +export interface Disk { + name: string; + fileSystem: string; + mountPoint: string; + totalSpace: number; + availableSpace: number; + isRemovable: boolean; + diskType: string; +} + +export interface DiskOutput { + disks: Disk[]; +} diff --git a/packages/client-api/src/providers/index.ts b/packages/client-api/src/providers/index.ts index 1fe7adbb..310dc0b2 100644 --- a/packages/client-api/src/providers/index.ts +++ b/packages/client-api/src/providers/index.ts @@ -5,6 +5,7 @@ export * from './glazewm/glazewm-provider-types'; export * from './host/host-provider-types'; export * from './ip/ip-provider-types'; export * from './keyboard/keyboard-provider-types'; +export * from './disk/disk-provider-types'; export * from './komorebi/komorebi-provider-types'; export * from './memory/memory-provider-types'; export * from './network/network-provider-types'; diff --git a/packages/desktop/src/providers/disk/disk_provider.rs b/packages/desktop/src/providers/disk/disk_provider.rs new file mode 100644 index 00000000..97b4890b --- /dev/null +++ b/packages/desktop/src/providers/disk/disk_provider.rs @@ -0,0 +1,103 @@ +use std::{any::Any, sync::Arc}; + +use serde::{Deserialize, Serialize}; +use sysinfo::{Disk, Disks}; +use tokio::sync::Mutex; + +use crate::{ + common::{to_iec_bytes, to_si_bytes}, + impl_interval_provider, + providers::ProviderOutput, +}; + +#[derive(Deserialize, Debug)] +#[serde(rename_all = "camelCase")] +pub struct DiskProviderConfig { + pub refresh_interval: u64, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DiskOutput { + pub disks: Vec, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DiskInner { + pub name: String, + pub file_system: String, + pub mount_point: String, + pub total_space: DiskSizeMeasure, + pub available_space: DiskSizeMeasure, + pub is_removable: bool, + pub disk_type: String, +} + +pub struct DiskProvider { + config: DiskProviderConfig, + system: Arc>, +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct DiskSizeMeasure { + pub bytes: u64, + pub si_value: f64, + pub si_unit: String, + pub iec_value: f64, + pub iec_unit: String, +} + +impl DiskProvider { + pub fn new( + config: DiskProviderConfig, + system: Arc>, + ) -> DiskProvider { + DiskProvider { config, system } + } + + fn refresh_interval_ms(&self) -> u64 { + self.config.refresh_interval + } + + async fn run_interval(&self) -> anyhow::Result { + let mut disks = self.system.lock().await; + disks.refresh(); + + let list: Vec = disks + .iter() + .map(|disk| -> anyhow::Result { + Ok(DiskInner { + name: disk.name().to_string_lossy().to_string(), + file_system: disk.file_system().to_string_lossy().to_string(), + mount_point: disk.mount_point().to_string_lossy().to_string(), + total_space: Self::to_disk_size_measure(disk.total_space())?, + available_space: Self::to_disk_size_measure( + disk.available_space(), + )?, + is_removable: disk.is_removable(), + disk_type: disk.kind().to_string(), + }) + }) + .collect::>>()?; + + let output = DiskOutput { disks: list }; + Ok(ProviderOutput::Disk(output)) + } + + fn to_disk_size_measure(bytes: u64) -> anyhow::Result { + let (si_value, si_unit) = to_si_bytes(bytes as f64); + let (iec_value, iec_unit) = to_iec_bytes(bytes as f64); + + Ok(DiskSizeMeasure { + bytes, + si_value, + si_unit, + iec_value, + iec_unit, + }) + } +} + +impl_interval_provider!(DiskProvider, true); diff --git a/packages/desktop/src/providers/disk/mod.rs b/packages/desktop/src/providers/disk/mod.rs new file mode 100644 index 00000000..b291bf96 --- /dev/null +++ b/packages/desktop/src/providers/disk/mod.rs @@ -0,0 +1,3 @@ +mod disk_provider; + +pub use disk_provider::*; diff --git a/packages/desktop/src/providers/mod.rs b/packages/desktop/src/providers/mod.rs index 05c1f98e..dce7a9cc 100644 --- a/packages/desktop/src/providers/mod.rs +++ b/packages/desktop/src/providers/mod.rs @@ -1,5 +1,6 @@ mod battery; mod cpu; +mod disk; mod host; mod ip; #[cfg(windows)] diff --git a/packages/desktop/src/providers/provider_config.rs b/packages/desktop/src/providers/provider_config.rs index 4d699003..4410bccc 100644 --- a/packages/desktop/src/providers/provider_config.rs +++ b/packages/desktop/src/providers/provider_config.rs @@ -2,9 +2,9 @@ use serde::Deserialize; use super::{ battery::BatteryProviderConfig, cpu::CpuProviderConfig, - host::HostProviderConfig, ip::IpProviderConfig, - memory::MemoryProviderConfig, network::NetworkProviderConfig, - weather::WeatherProviderConfig, + disk::DiskProviderConfig, host::HostProviderConfig, + ip::IpProviderConfig, memory::MemoryProviderConfig, + network::NetworkProviderConfig, weather::WeatherProviderConfig, }; #[cfg(windows)] use super::{ @@ -21,6 +21,7 @@ pub enum ProviderConfig { #[cfg(windows)] Komorebi(KomorebiProviderConfig), Memory(MemoryProviderConfig), + Disk(DiskProviderConfig), Network(NetworkProviderConfig), Weather(WeatherProviderConfig), #[cfg(windows)] diff --git a/packages/desktop/src/providers/provider_manager.rs b/packages/desktop/src/providers/provider_manager.rs index b54afc80..73b61d35 100644 --- a/packages/desktop/src/providers/provider_manager.rs +++ b/packages/desktop/src/providers/provider_manager.rs @@ -1,6 +1,6 @@ use std::{collections::HashMap, sync::Arc}; -use sysinfo::{Networks, System}; +use sysinfo::{Disks, Networks, System}; use tauri::AppHandle; use tokio::sync::Mutex; use tracing::warn; @@ -12,6 +12,7 @@ use super::{ProviderConfig, ProviderRef}; pub struct SharedProviderState { pub sysinfo: Arc>, pub netinfo: Arc>, + pub diskinfo: Arc>, } /// Manages the creation and cleanup of providers. @@ -29,6 +30,7 @@ impl ProviderManager { shared_state: SharedProviderState { sysinfo: Arc::new(Mutex::new(System::new_all())), netinfo: Arc::new(Mutex::new(Networks::new_with_refreshed_list())), + diskinfo: Arc::new(Mutex::new(Disks::new_with_refreshed_list())), }, } } diff --git a/packages/desktop/src/providers/provider_output.rs b/packages/desktop/src/providers/provider_output.rs index e669d0ab..4ef23ab7 100644 --- a/packages/desktop/src/providers/provider_output.rs +++ b/packages/desktop/src/providers/provider_output.rs @@ -1,8 +1,9 @@ use serde::Serialize; use super::{ - battery::BatteryOutput, cpu::CpuOutput, host::HostOutput, ip::IpOutput, - memory::MemoryOutput, network::NetworkOutput, weather::WeatherOutput, + battery::BatteryOutput, cpu::CpuOutput, disk::DiskOutput, + host::HostOutput, ip::IpOutput, memory::MemoryOutput, + network::NetworkOutput, weather::WeatherOutput, }; #[cfg(windows)] use super::{keyboard::KeyboardOutput, komorebi::KomorebiOutput}; @@ -17,6 +18,7 @@ pub enum ProviderOutput { #[cfg(windows)] Komorebi(KomorebiOutput), Memory(MemoryOutput), + Disk(DiskOutput), Network(NetworkOutput), Weather(WeatherOutput), #[cfg(windows)] diff --git a/packages/desktop/src/providers/provider_ref.rs b/packages/desktop/src/providers/provider_ref.rs index e84bc316..9c72f062 100644 --- a/packages/desktop/src/providers/provider_ref.rs +++ b/packages/desktop/src/providers/provider_ref.rs @@ -11,10 +11,10 @@ use tokio::{ use tracing::{info, warn}; use super::{ - battery::BatteryProvider, cpu::CpuProvider, host::HostProvider, - ip::IpProvider, memory::MemoryProvider, network::NetworkProvider, - weather::WeatherProvider, Provider, ProviderConfig, ProviderOutput, - SharedProviderState, + battery::BatteryProvider, cpu::CpuProvider, disk::DiskProvider, + host::HostProvider, ip::IpProvider, memory::MemoryProvider, + network::NetworkProvider, weather::WeatherProvider, Provider, + ProviderConfig, ProviderOutput, SharedProviderState, }; #[cfg(windows)] use super::{keyboard::KeyboardProvider, komorebi::KomorebiProvider}; @@ -176,6 +176,9 @@ impl ProviderRef { ProviderConfig::Memory(config) => { Box::new(MemoryProvider::new(config, shared_state.sysinfo.clone())) } + ProviderConfig::Disk(config) => { + Box::new(DiskProvider::new(config, shared_state.diskinfo.clone())) + } ProviderConfig::Network(config) => Box::new(NetworkProvider::new( config, shared_state.netinfo.clone(),