lcpfs 2026.1.102

LCP File System - A ZFS-inspired copy-on-write filesystem for Rust
// Copyright 2025 LunaOS Contributors
// SPDX-License-Identifier: Apache-2.0

//! Type definitions for online defragmentation

use alloc::string::String;
use alloc::vec::Vec;
use thiserror_no_std::Error;

/// Error type for defragmentation operations
#[derive(Debug, Clone, PartialEq, Eq, Error)]
pub enum DefragError {
    /// Dataset not found
    #[error("Dataset not found: {0}")]
    DatasetNotFound(String),

    /// Object not found
    #[error("Object not found: {0}")]
    ObjectNotFound(u64),

    /// Not enough contiguous space available
    #[error("No contiguous space available for {0} blocks")]
    NoContiguousSpace(u64),

    /// IO error during defragmentation
    #[error("IO error: {0}")]
    IoError(String),

    /// Task not found
    #[error("Defrag task not found: {0}")]
    TaskNotFound(u64),

    /// Task was cancelled
    #[error("Defrag task cancelled")]
    Cancelled,

    /// File is busy (locked by another operation)
    #[error("File is busy")]
    FileBusy,

    /// Defragmentation not needed
    #[error("File is not fragmented")]
    NotFragmented,
}

/// A contiguous extent on disk
#[derive(Debug, Clone)]
pub struct Extent {
    /// Physical start offset on disk
    pub pstart: u64,
    /// Logical start offset in file
    pub lstart: u64,
    /// Length in bytes
    pub length: u64,
    /// Device ID
    pub device_id: u32,
}

/// Defragmentation options
#[derive(Debug, Clone)]
pub struct DefragOptions {
    /// Minimum fragmentation score to defrag (0-100)
    pub min_fragmentation: u8,
    /// Minimum file size to consider
    pub min_file_size: u64,
    /// Maximum file size to consider (0 = unlimited)
    pub max_file_size: u64,
    /// Whether to run in background with throttling
    pub background: bool,
    /// Throttle: milliseconds to pause between operations
    pub throttle_ms: u64,
    /// Skip files that are open for writing
    pub skip_open_files: bool,
    /// Maximum number of files to process (0 = unlimited)
    pub max_files: usize,
    /// Dry run - don't actually move data
    pub dry_run: bool,
}

impl Default for DefragOptions {
    fn default() -> Self {
        Self {
            min_fragmentation: 30,
            min_file_size: 4096,
            max_file_size: 0,
            background: false,
            throttle_ms: 10,
            skip_open_files: true,
            max_files: 0,
            dry_run: false,
        }
    }
}

/// Statistics from a defragmentation operation
#[derive(Debug, Clone, Default)]
pub struct DefragStats {
    /// Number of files processed
    pub files_processed: u64,
    /// Number of files actually defragmented
    pub files_defragmented: u64,
    /// Number of files skipped
    pub files_skipped: u64,
    /// Total bytes moved
    pub bytes_moved: u64,
    /// Extents before defragmentation
    pub extents_before: u64,
    /// Extents after defragmentation
    pub extents_after: u64,
    /// Duration in milliseconds
    pub duration_ms: u64,
}

/// Progress of a defragmentation task
#[derive(Debug, Clone)]
pub struct DefragProgress {
    /// Task ID
    pub task_id: u64,
    /// Current status
    pub status: DefragStatus,
    /// Number of files processed so far
    pub files_processed: u64,
    /// Total files to process (estimate)
    pub files_total: u64,
    /// Bytes moved so far
    pub bytes_moved: u64,
    /// Current file being processed
    pub current_file: Option<String>,
    /// Error message if failed
    pub error: Option<String>,
}

/// Status of a defragmentation task
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum DefragStatus {
    /// Task is queued but not started
    Pending,
    /// Task is running
    Running,
    /// Task completed successfully
    Completed,
    /// Task was cancelled
    Cancelled,
    /// Task failed with error
    Failed,
}

/// Recommendation for defragmentation
#[derive(Debug, Clone)]
pub struct DefragRecommendation {
    /// Should defragmentation be performed?
    pub should_defrag: bool,
    /// Current fragmentation score
    pub fragmentation_score: u8,
    /// Number of extents
    pub extent_count: usize,
    /// Estimated benefit (0-100)
    pub estimated_benefit: u8,
    /// Reason for recommendation
    pub reason: String,
}

/// Background defragmentation task
pub struct DefragTask {
    /// Unique task ID
    pub id: u64,
    /// Dataset being defragmented
    pub dataset: String,
    /// Options for this task
    pub options: DefragOptions,
    /// Current status
    pub status: DefragStatus,
    /// Accumulated statistics
    pub stats: DefragStats,
    /// Current file path
    pub current_file: Option<String>,
    /// Error message if any
    pub error: Option<String>,
    /// Whether cancellation was requested
    pub cancel_requested: bool,
}

impl DefragTask {
    /// Create a new defrag task
    pub fn new(id: u64, dataset: String, options: DefragOptions) -> Self {
        Self {
            id,
            dataset,
            options,
            status: DefragStatus::Pending,
            stats: DefragStats::default(),
            current_file: None,
            error: None,
            cancel_requested: false,
        }
    }
}