rust_single/
lib.rs

1mod core;
2#[cfg(feature = "ipc")]
3mod ipc;
4
5use crate::core::{try_unique, write};
6use fs2::FileExt;
7use std::error::Error;
8use std::fs::{create_dir_all, File, OpenOptions};
9use std::io::{Read, Write};
10use std::path::Path;
11use std::process;
12use std::sync::Arc;
13
14#[derive(thiserror::Error, Debug)]
15pub enum SingleError {
16    #[error("pid and info write err!")]
17    SingleWrite,
18}
19
20type AnyResult<T> = Result<T, Box<dyn Error>>;
21
22pub struct Single {
23    pub is_single: bool,
24    pub pid: Option<u32>,
25    pub info: String,
26    pub path: String,
27    pub path_info: String,
28    #[cfg(feature = "ipc")]
29    pub path_ipc: String,
30    #[cfg(feature = "ipc")]
31    on_wake: Option<Arc<Box<dyn Fn() + Send + Sync>>>,
32    file: Option<File>,
33}
34
35impl Single {
36    fn create(build: SingleBuild) -> AnyResult<Single> {
37        let path_lock = Path::new(&build.path);
38        if let Some(parent) = path_lock.parent() {
39            create_dir_all(parent)?;
40        }
41        let path_info = format!("{}.info", build.path);
42        let path_ipc = format!("{}.ipc", build.path);
43
44        match try_unique(path_lock) {
45            Ok(lock) => {
46                let pid = process::id();
47                let context = format!("{}\\n{}", pid, &build.info);
48
49                match write(&path_info, context) {
50                    Ok(_) => {}
51                    Err(e) => {
52                        log::error!("write pid and info to {} err! {}", path_info, e);
53                        return Err(Box::new(SingleError::SingleWrite));
54                    }
55                }
56
57                let mut single = Single {
58                    is_single: true,
59                    pid: Some(pid),
60                    info: build.info,
61                    path: build.path,
62                    path_info,
63                    #[cfg(feature = "ipc")]
64                    path_ipc,
65                    #[cfg(feature = "ipc")]
66                    on_wake: build.on_wake,
67                    file: Some(lock),
68                };
69
70                #[cfg(feature = "ipc")]
71                {
72                    single = core::ipc_create(single)?;
73                }
74
75                return Ok(single);
76            }
77            Err(e) => {
78                log::error!("获取独占锁异常! {}", e)
79            }
80        }
81
82        let mut options = OpenOptions::new();
83        options.read(true);
84
85        let mut file = options.open(&path_info)?;
86        let mut content = String::new();
87        file.read_to_string(&mut content)?;
88        let (pid_str, info) = content.split_once('\n').unwrap_or(("0", &content));
89        let pid = pid_str.parse::<u32>().ok();
90
91        let mut single = Single {
92            is_single: false,
93            pid,
94            info: info.to_string(),
95            path: build.path,
96            path_info,
97            #[cfg(feature = "ipc")]
98            path_ipc,
99            #[cfg(feature = "ipc")]
100            on_wake: None,
101            file: None,
102        };
103
104        #[cfg(feature = "ipc")]
105        {
106            if build.on_wake.is_some() {
107                single = core::ipc_wake(single)?;
108            }
109        }
110
111        Ok(single)
112    }
113}
114
115impl Drop for Single {
116    fn drop(&mut self) {
117        let option = self.file.take();
118        if let Some(file) = option {
119            let _ = FileExt::unlock(&file);
120            drop(file);
121        }
122    }
123}
124
125#[derive(Default)]
126pub struct SingleBuild {
127    pub info: String,
128    pub path: String,
129    #[cfg(feature = "ipc")]
130    pub on_wake: Option<Arc<Box<dyn Fn() + Send + Sync>>>,
131}
132
133impl SingleBuild {
134    pub fn new<P: AsRef<Path>>(path: P) -> Self {
135        let mut s = SingleBuild::default();
136        s.path = path
137            .as_ref()
138            .to_str()
139            .expect("failed get single path")
140            .to_string();
141        s
142    }
143
144    pub fn with_info(mut self, info: String) -> Self {
145        self.info = info;
146        self
147    }
148
149    #[cfg(feature = "ipc")]
150    pub fn with_ipc<F: Fn() + Send + Sync + 'static>(mut self, f: F) -> Self {
151        self.on_wake = Some(Arc::new(Box::new(f)));
152        self
153    }
154
155    pub fn build(self) -> AnyResult<Single> {
156        Single::create(self)
157    }
158}