Skip to main content

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;
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
20pub type AnyResult<T> = Result<T, Box<dyn Error + Send + Sync>>;
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(AnyResult<Vec<u8>>) + 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 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        Ok(single)
105    }
106
107    #[cfg(feature = "ipc")]
108    pub fn wake(&self, content: &str) -> AnyResult<()> {
109        let bytes = content.as_bytes();
110        self.wake_bytes(bytes)
111    }
112
113    #[cfg(feature = "ipc")]
114    pub fn wake_bytes(&self, bytes: &[u8]) -> AnyResult<()> {
115        use crate::ipc::IpcStream;
116        let mut stream = IpcStream::new(&self.path_ipc)?;
117        stream.write_bytes(bytes)
118    }
119}
120
121impl Drop for Single {
122    fn drop(&mut self) {
123        let option = self.file.take();
124        if let Some(file) = option {
125            let _ = FileExt::unlock(&file);
126            drop(file);
127        }
128    }
129}
130
131#[derive(Default)]
132pub struct SingleBuild {
133    pub info: String,
134    pub path: String,
135    #[cfg(feature = "ipc")]
136    pub on_wake: Option<Arc<Box<dyn Fn(AnyResult<Vec<u8>>) + Send + Sync>>>,
137}
138
139impl SingleBuild {
140    pub fn new<P: AsRef<Path>>(path: P) -> Self {
141        let mut s = SingleBuild::default();
142        s.path = path
143            .as_ref()
144            .to_str()
145            .expect("failed get single path")
146            .to_string();
147        s
148    }
149
150    pub fn with_info(mut self, info: String) -> Self {
151        self.info = info;
152        self
153    }
154
155    #[cfg(feature = "ipc")]
156    pub fn with_ipc<F: Fn(AnyResult<Vec<u8>>) + Send + Sync + 'static>(mut self, f: F) -> Self {
157        self.on_wake = Some(Arc::new(Box::new(f)));
158        self
159    }
160
161    pub fn build(self) -> AnyResult<Single> {
162        Single::create(self)
163    }
164}