//! Disk stats for Unix-like systems that aren't supported through other means.
//! Officially, for now, this means Linux and macOS.

mod file_systems;

mod usage;

cfg_select! {
    target_os = "linux" => {
        mod linux;
        pub use linux::*;
    }
    target_os = "macos" => {
        mod other;
        use other::*;

        mod macos;
        pub use macos::*;
    }
    _ => {
        mod other;
        use other::*;
    }
}

use file_systems::*;
use usage::*;

use super::{DiskHarvest, keep_disk_entry};
use crate::collection::DataCollector;

/// Returns the disk usage of the mounted (and for now, physical) disks.
pub fn get_disk_usage(collector: &DataCollector) -> anyhow::Result<Vec<DiskHarvest>> {
    let disk_filter = &collector.filters.disk_filter;
    let mount_filter = &collector.filters.mount_filter;
    let mut vec_disks: Vec<DiskHarvest> = Vec::new();

    #[cfg(target_os = "linux")]
    let mut mounted_names = std::collections::HashSet::new();

    for partition in physical_partitions()? {
        let name = partition.get_device_name();
        let mount_point = partition.mount_point().to_string_lossy().to_string();

        #[cfg(target_os = "linux")]
        if collector.include_unmounted_disks
            && let Some(base) = name.rsplit('/').next()
        {
            mounted_names.insert(base.to_string());
        }

        // Precedence ordering in the case where name and mount filters disagree,
        // "allow" takes precedence over "deny".
        //
        // For implementation, we do this as follows:
        // 1. Is the entry allowed through any filter? That is, does it match an entry
        //    in a filter where `is_list_ignored` is `false`? If so, we always keep this
        //    entry.
        // 2. Is the entry denied through any filter? That is, does it match an entry in
        //    a filter where `is_list_ignored` is `true`? If so, we always deny this
        //    entry.
        // 3. Anything else is allowed.

        if keep_disk_entry(&name, &mount_point, disk_filter, mount_filter) {
            // The usage line can fail in some cases (for example, if you use Void Linux +
            // LUKS, see https://github.com/ClementTsang/bottom/issues/419 for details).
            if let Ok(usage) = partition.usage() {
                let total = usage.total();

                vec_disks.push(DiskHarvest {
                    free_space: Some(usage.free()),
                    used_space: Some(total - usage.available()),
                    total_space: Some(total),
                    mount_point,
                    name,
                });
            } else {
                vec_disks.push(DiskHarvest {
                    free_space: None,
                    used_space: None,
                    total_space: None,
                    mount_point,
                    name,
                });
            }
        }
    }

    #[cfg(target_os = "linux")]
    {
        // If we're including unmounted disks, then we'll add any disks that we previously
        // did not mark as mounted.
        if collector.include_unmounted_disks {
            for disk in unmounted_disks(&mounted_names) {
                if keep_disk_entry(&disk.name, &disk.mount_point, disk_filter, &None) {
                    vec_disks.push(disk);
                }
            }
        }
    }

    Ok(vec_disks)
}
