tr300 3.15.1

Cross-platform system information report
//! Disk information collector

use crate::error::Result;
use sysinfo::Disks;

/// Disk/volume information
#[derive(Debug, Clone)]
pub struct DiskInfo {
    /// Mount point or drive letter
    pub mount_point: String,
    /// Filesystem type (e.g., "NTFS", "ext4", "APFS")
    pub filesystem: String,
    /// Total space in bytes
    pub total_bytes: u64,
    /// Available space in bytes
    pub available_bytes: u64,
    /// Used space in bytes
    pub used_bytes: u64,
    /// Whether this is a removable disk
    pub is_removable: bool,
    /// Disk name/label
    pub name: String,
}

/// Collect disk information
pub fn collect() -> Result<Vec<DiskInfo>> {
    let disks = Disks::new_with_refreshed_list();
    let mut result = Vec::new();

    for disk in disks.list() {
        let total = disk.total_space();
        let available = disk.available_space();
        let used = total.saturating_sub(available);

        // Skip disks with no space (virtual filesystems, etc.)
        if total == 0 {
            continue;
        }

        result.push(DiskInfo {
            mount_point: disk.mount_point().to_string_lossy().to_string(),
            filesystem: disk.file_system().to_string_lossy().to_string(),
            total_bytes: total,
            available_bytes: available,
            used_bytes: used,
            is_removable: disk.is_removable(),
            name: disk.name().to_string_lossy().to_string(),
        });
    }

    Ok(result)
}

impl DiskInfo {
    /// Get usage percentage
    pub fn usage_percent(&self) -> f32 {
        if self.total_bytes == 0 {
            0.0
        } else {
            (self.used_bytes as f64 / self.total_bytes as f64 * 100.0) as f32
        }
    }

    /// Format bytes as human-readable
    fn format_bytes(bytes: u64) -> String {
        crate::format_bytes(bytes)
    }

    /// Get formatted total space
    pub fn total_formatted(&self) -> String {
        Self::format_bytes(self.total_bytes)
    }

    /// Get formatted available space
    pub fn available_formatted(&self) -> String {
        Self::format_bytes(self.available_bytes)
    }

    /// Get formatted used space
    pub fn used_formatted(&self) -> String {
        Self::format_bytes(self.used_bytes)
    }

    /// Get usage string
    pub fn usage_string(&self) -> String {
        format!(
            "{} / {} ({:.1}%)",
            self.used_formatted(),
            self.total_formatted(),
            self.usage_percent()
        )
    }

    /// Get display name (mount point or name)
    pub fn display_name(&self) -> String {
        if self.name.is_empty() {
            self.mount_point.clone()
        } else {
            format!("{} ({})", self.name, self.mount_point)
        }
    }
}