tauri-plugin-deeprespond-im 1.0.0-alpha

深应智能 IM 的 Tauri 插件:本地存储、安全存储、窗口间的锁与消息、系统事件、通知、角标、文件
//! 单实例(《2.功能概述文档》第 4.6 节):同一份数据只允许一个进程使用。插件在数据目录中加文件锁,
//! 拿不到的进程不创建引擎客户端,界面窗口的客户端以 invalid_state(another_instance)失败。

use std::fs::{File, OpenOptions};
use std::path::Path;

use fs4::fs_std::FileExt;

/// 持有期间文件锁有效;进程退出(含崩溃)时由系统释放。
pub struct InstanceLock {
    _file: File,
}

/// 取得锁;另一个进程持有时返回 None。
pub fn acquire(data_dir: &Path) -> std::io::Result<Option<InstanceLock>> {
    std::fs::create_dir_all(data_dir)?;
    let file = OpenOptions::new().read(true).write(true).create(true).truncate(false).open(data_dir.join("instance.lock"))?;
    match file.try_lock_exclusive() {
        Ok(true) => Ok(Some(InstanceLock { _file: file })),
        Ok(false) => Ok(None),
        Err(e) if e.kind() == std::io::ErrorKind::WouldBlock => Ok(None),
        Err(e) => Err(e),
    }
}

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

    #[test]
    fn second_lock_fails_until_released() {
        let dir = tempfile::tempdir().unwrap();
        let first = acquire(dir.path()).unwrap();
        assert!(first.is_some());
        assert!(acquire(dir.path()).unwrap().is_none());
        drop(first);
        assert!(acquire(dir.path()).unwrap().is_some());
    }
}