use crate::domain::{DriveInfo, ImageInfo};
const UNKNOWN_SIZE: f64 = 0.0;
pub const LARGE_DRIVE_SIZE: f64 = 128.0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompatibilityStatusType {
Warning,
Error,
}
#[derive(Debug, Clone)]
pub struct CompatibilityStatus {
pub status_type: CompatibilityStatusType,
pub message: String,
}
impl CompatibilityStatus {
pub fn new(status_type: CompatibilityStatusType, message: String) -> Self {
Self {
status_type,
message,
}
}
pub fn error(message: String) -> Self {
Self::new(CompatibilityStatusType::Error, message)
}
pub fn warning(message: String) -> Self {
Self::new(CompatibilityStatusType::Warning, message)
}
}
pub fn is_system_drive(drive: &DriveInfo) -> bool {
drive.is_system
}
pub fn is_source_drive(drive: &DriveInfo, image: Option<&ImageInfo>) -> bool {
if let Some(img) = image {
let image_path = img.path.to_string_lossy();
if !drive.mount_point.is_empty()
&& drive.mount_point != drive.device_path
&& image_path.starts_with(&drive.mount_point)
{
return true;
}
if image_path.starts_with(&drive.device_path) {
return true;
}
}
false
}
pub fn is_drive_large_enough(drive: &DriveInfo, image: Option<&ImageInfo>) -> bool {
let drive_size_gb = if drive.size_gb > 0.0 {
drive.size_gb
} else {
UNKNOWN_SIZE
};
if let Some(img) = image {
let image_size_gb = img.size_mb / 1024.0;
if drive_size_gb <= 0.0 {
return false;
}
return drive_size_gb >= image_size_gb;
}
true
}
pub fn is_drive_size_recommended(_drive: &DriveInfo, _image: Option<&ImageInfo>) -> bool {
true
}
pub fn is_drive_size_large(drive: &DriveInfo) -> bool {
drive.size_gb > LARGE_DRIVE_SIZE
}
pub fn is_drive_valid(drive: &DriveInfo, image: Option<&ImageInfo>) -> bool {
!drive.disabled && is_drive_large_enough(drive, image) && !is_source_drive(drive, image)
}
pub fn get_drive_image_compatibility_statuses(
drive: &DriveInfo,
image: Option<&ImageInfo>,
) -> Vec<CompatibilityStatus> {
let mut statuses = Vec::new();
if drive.is_read_only {
statuses.push(CompatibilityStatus::error(
"This drive is read-only and cannot be flashed.".to_string(),
));
}
if !is_drive_large_enough(drive, image) {
if let Some(img) = image {
statuses.push(CompatibilityStatus::error(format!(
"Drive is too small. Need at least {:.2} GB, but drive is {:.2} GB.",
img.size_mb / 1024.0,
drive.size_gb
)));
} else {
statuses.push(CompatibilityStatus::error(
"Drive is too small for the image.".to_string(),
));
}
} else {
if is_system_drive(drive) {
statuses.push(CompatibilityStatus::warning(
"This is a system drive. Flashing it may damage your operating system.".to_string(),
));
} else if is_drive_size_large(drive) {
statuses.push(CompatibilityStatus::warning(format!(
"This drive is larger than {}GB. Are you sure this is the right drive?",
LARGE_DRIVE_SIZE as i32
)));
}
if is_source_drive(drive, image) {
statuses.push(CompatibilityStatus::error(
"This drive contains the source image and cannot be selected.".to_string(),
));
}
if !is_drive_size_recommended(drive, image) {
statuses.push(CompatibilityStatus::warning(
"Drive size is smaller than recommended for optimal performance.".to_string(),
));
}
}
statuses
}
pub fn get_list_drive_image_compatibility_statuses(
drives: &[DriveInfo],
image: Option<&ImageInfo>,
) -> Vec<CompatibilityStatus> {
drives
.iter()
.flat_map(|drive| get_drive_image_compatibility_statuses(drive, image))
.collect()
}
pub fn has_drive_image_compatibility_status(drive: &DriveInfo, image: Option<&ImageInfo>) -> bool {
!get_drive_image_compatibility_statuses(drive, image).is_empty()
}
pub fn mark_invalid_drives(drives: &mut [DriveInfo], image: Option<&ImageInfo>) {
for drive in drives.iter_mut() {
let statuses = get_drive_image_compatibility_statuses(drive, image);
drive.disabled = statuses
.iter()
.any(|s| s.status_type == CompatibilityStatusType::Error);
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::path::PathBuf;
fn create_test_drive(size_gb: f64, is_system: bool, is_read_only: bool) -> DriveInfo {
DriveInfo::with_constraints(
"Test Drive".to_string(),
"/media/test".to_string(),
size_gb,
"/dev/sdb".to_string(),
is_system,
is_read_only,
)
}
fn create_test_image(size_mb: f64) -> ImageInfo {
ImageInfo {
path: PathBuf::from("/tmp/test.img"),
name: "test.img".to_string(),
size_mb,
}
}
#[test]
fn test_is_drive_large_enough() {
let drive = create_test_drive(16.0, false, false);
let image = create_test_image(8000.0);
assert!(is_drive_large_enough(&drive, Some(&image)));
}
#[test]
fn test_is_drive_too_small() {
let drive = create_test_drive(4.0, false, false);
let image = create_test_image(8000.0);
assert!(!is_drive_large_enough(&drive, Some(&image)));
}
#[test]
fn test_is_drive_size_large() {
let small_drive = create_test_drive(64.0, false, false);
let large_drive = create_test_drive(256.0, false, false);
assert!(!is_drive_size_large(&small_drive));
assert!(is_drive_size_large(&large_drive));
}
#[test]
fn test_system_drive_warning() {
let drive = create_test_drive(32.0, true, false);
let image = create_test_image(4000.0);
let statuses = get_drive_image_compatibility_statuses(&drive, Some(&image));
assert!(!statuses.is_empty());
assert!(statuses
.iter()
.any(|s| s.status_type == CompatibilityStatusType::Warning));
}
#[test]
fn test_read_only_drive_error() {
let drive = create_test_drive(32.0, false, true);
let image = create_test_image(4000.0);
let statuses = get_drive_image_compatibility_statuses(&drive, Some(&image));
assert!(!statuses.is_empty());
assert!(statuses
.iter()
.any(|s| s.status_type == CompatibilityStatusType::Error));
}
#[test]
fn test_mark_invalid_drives() {
let drives = vec![
create_test_drive(32.0, false, false), create_test_drive(2.0, false, false), create_test_drive(16.0, false, true), ];
let image = create_test_image(4000.0);
let mut drives_mut = drives;
mark_invalid_drives(&mut drives_mut, Some(&image));
assert!(!drives_mut[0].disabled); assert!(drives_mut[1].disabled); assert!(drives_mut[2].disabled); }
#[test]
fn test_is_drive_valid() {
let mut valid_drive = create_test_drive(32.0, false, false);
let invalid_drive = create_test_drive(2.0, false, false);
let image = create_test_image(4000.0);
assert!(is_drive_valid(&valid_drive, Some(&image)));
assert!(!is_drive_valid(&invalid_drive, Some(&image)));
valid_drive.disabled = true;
assert!(!is_drive_valid(&valid_drive, Some(&image)));
}
}