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}