vstorage 0.10.0

Common API for various icalendar/vcard storages.
Documentation
// Copyright 2025-2026 Hugo Osvaldo Barrera
//
// SPDX-License-Identifier: EUPL-1.2

use futures_util::future::BoxFuture;
use tokio::sync::mpsc::{self, Receiver};

use crate::{
    ErrorKind, Result,
    watch::{Event, StorageMonitor},
};

use super::VdirStorage;

/// Monitor for a [`VdirStorage`] instance.
///
/// # Quirks
///
/// Due to limitations of the underlying platform-specific interfaces, it's possible that some
/// events are missed. When this happens, an [`Event::Storage`] is returned.
pub struct VdirMonitor {
    receiver: Receiver<Event>,
}

impl VdirMonitor {
    /// Create a new monitor for `storage`.
    ///
    /// # Errors
    ///
    /// If an error occurs setting up the underlying filesystem watcher.
    #[allow(unused)] // Parameters are unused on platforms with no monitor implementation.
    #[allow(clippy::unused_async)] // No await points on platform with no monitor implementation.
    pub async fn new(storage: &VdirStorage) -> Result<VdirMonitor> {
        let (sender, receiver) = mpsc::channel::<Event>(1);

        #[cfg(target_os = "linux")]
        {
            use crate::vdir::inotify::{init_inotify, run_inotify_task};

            let path = storage.path.clone();
            let extension = storage.extension.clone();
            let inotify_watcher = init_inotify(&path).await?;
            tokio::spawn(async move {
                // Emit initial Event::Storage after inotify is set up.
                if sender.send(Event::Storage).await.is_err() {
                    return;
                }
                run_inotify_task(sender, path, extension, inotify_watcher).await;
            });

            return Ok(VdirMonitor { receiver });
        }

        #[cfg(any(
            target_os = "freebsd",
            target_os = "openbsd",
            target_os = "netbsd",
            target_os = "dragonfly"
        ))]
        {
            use crate::vdir::kqueue::run_kqueue_task;

            let path = storage.path.clone();
            std::thread::spawn(move || {
                run_kqueue_task(&sender, &path);
            });

            return Ok(VdirMonitor { receiver });
        }

        // Insert any  other platform-specific backend here.

        #[allow(unreachable_code)]
        Err(ErrorKind::Unsupported.into())
    }
}

impl StorageMonitor for VdirMonitor {
    fn next_event(&mut self) -> BoxFuture<'_, Option<Event>> {
        Box::pin(async { self.receiver.recv().await })
    }
}