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}