rebecca-core 0.3.0

Core planning, safety, scanning, and history models for Rebecca.
Documentation
use std::path::Path;
use std::sync::{
    Arc,
    atomic::{AtomicBool, Ordering},
};

use crate::error::{RebeccaError, Result};

#[derive(Debug, Clone)]
pub struct ScanCancellationToken {
    state: Arc<ScanCancellationState>,
}

#[derive(Debug, Default)]
struct ScanCancellationState {
    cancelled: AtomicBool,
    parent: Option<ScanCancellationToken>,
}

impl Default for ScanCancellationToken {
    fn default() -> Self {
        Self {
            state: Arc::new(ScanCancellationState::default()),
        }
    }
}

impl ScanCancellationToken {
    pub fn new() -> Self {
        Self::default()
    }

    pub fn child_token(&self) -> Self {
        Self {
            state: Arc::new(ScanCancellationState {
                cancelled: AtomicBool::new(false),
                parent: Some(self.clone()),
            }),
        }
    }

    pub fn cancel(&self) {
        self.state.cancelled.store(true, Ordering::SeqCst);
    }

    pub fn is_cancelled(&self) -> bool {
        self.state.cancelled.load(Ordering::SeqCst)
            || self
                .state
                .parent
                .as_ref()
                .is_some_and(ScanCancellationToken::is_cancelled)
    }
}

#[derive(Debug, Clone, Copy)]
pub enum ScanProgressEvent<'a> {
    FileMeasured {
        path: &'a Path,
        file_size: u64,
        files_scanned: u64,
        bytes_scanned: u64,
    },
}

pub(crate) fn check_not_cancelled(cancellation: &ScanCancellationToken) -> Result<()> {
    if cancellation.is_cancelled() {
        return Err(RebeccaError::OperationCancelled(
            "scan was cancelled".to_string(),
        ));
    }

    Ok(())
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn child_cancellation_token_follows_parent_without_cancelling_parent() {
        let parent = ScanCancellationToken::new();
        let child = parent.child_token();

        assert!(!parent.is_cancelled());
        assert!(!child.is_cancelled());

        child.cancel();

        assert!(!parent.is_cancelled());
        assert!(child.is_cancelled());

        let sibling = parent.child_token();
        parent.cancel();

        assert!(parent.is_cancelled());
        assert!(sibling.is_cancelled());
    }
}