-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlib.rs
201 lines (195 loc) · 8.24 KB
/
lib.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
use lazy_static::lazy_static;
use local::notification::Notification;
use local::{AppInfo, CheckUpType};
use request::RemoteInfo;
use std::{collections::HashMap, fs, path::Path};
use threadpool::ThreadPool;
use version_cmp::cmp_version;
pub mod local;
pub mod request;
pub mod version_cmp;
lazy_static! {
/// 忽略的应用 bundle_id 集合
static ref IGNORES: Vec<String> = local::config::get_ignore_config();
/// homebrew 查询别名映射集合
static ref ALIAS: HashMap<String, String> = local::config::get_alias_config();
/// MAS 备用查询区域代码集合
static ref MASAREAS: Vec<String> = local::config::get_mas_areas();
/// 设备版本信息
static ref ARM_SYSTEM_NAME: String = local::plist::get_arm_system_version();
/// terminal-notifier 的安装路径
static ref TERMINAL_NOTIFIER_PATH: String = local::config::get_terminal_notifier_path();
/// 并发查询数量
static ref THREADNUMS: usize = local::config::get_thread_nums();
}
/// 检查更新配置
#[derive(Copy, Clone)]
pub struct CheckOperation {
pub notification: bool,
pub verbose: bool,
pub open_by_app: bool,
}
impl CheckOperation {
/// 检查指定路径下的应用
pub fn check_some(&self, paths: Vec<String>) {
for item in paths {
let path = Path::new(&item);
let buf = path.to_path_buf();
if let Some(app_info) = local::check_app_info(&buf) {
if app_info.check_update_type != CheckUpType::Ignored {
self.check_update(app_info, path)
}
} else if self.notification {
// 通知发送应用信息读取失败
Notification::new_error_notification(format!("{item} 应用信息读取失败")).post()
} else {
// 打印应用信息读取失败
println!("+++++");
println!("{item} 应用信息读取失败");
println!("+++++\n");
}
}
}
/// 检查所有应用
pub fn check_all(&self) {
let temp_self = *self;
let apps_path = Path::new("/Applications");
let n_workers: usize = *THREADNUMS;
let pool = ThreadPool::new(n_workers);
for item in fs::read_dir(apps_path).unwrap() {
// 直接使用 thread::spawn 会产生 `Too many open files` 的问题
pool.execute(move || match item {
Ok(path) => {
let app_info = local::check_app_info(&path.path());
// 这里不处理 else,原因是默认忽略以 `.` 开头的路径和不以 `.app` 结尾的路径
if let Some(info) = app_info {
if info.check_update_type != CheckUpType::Ignored {
temp_self.check_update(info, &path.path());
}
}
}
Err(error) => {
if temp_self.notification {
Notification::new_error_notification(format!("{error:?}")).post()
} else {
println!("+++++");
println!("{error:?}");
println!("+++++\n");
}
}
});
}
pool.join();
}
/// 根据应用类型查询更新并输出
pub fn check_update(&self, app_info: AppInfo, path: &Path) {
let check_update_type = &app_info.check_update_type;
// 默认 version 为 `-1`,约定查询完毕后还是 `-1` 的话就是查询失败
let mut remote_info: RemoteInfo = RemoteInfo {
version: "-1".to_string(),
update_page_url: "".to_string(),
};
// 最多尝试五次
for _ in 0..5 {
remote_info = match check_update_type {
CheckUpType::Ignored => break,
CheckUpType::Mas {
bundle_id,
is_ios_app,
} => request::area_check(bundle_id, *is_ios_app),
CheckUpType::Sparkle(feed_url) => request::sparkle_feed_check(feed_url),
CheckUpType::HomeBrew {
app_name,
bundle_id,
} => request::homebrew_check(app_name, bundle_id),
};
if remote_info.version != *"-1" {
break;
}
}
// 处理查询失败的情况
if remote_info.version == *"-1" {
if self.notification {
Notification::new_remote_get_failed(&app_info).post()
} else {
println!("+++++");
println!("{}", app_info.name);
println!("{}", app_info.check_update_type);
println!("local version {}", app_info.short_version);
println!("remote version check failed");
println!("+++++\n");
}
return;
}
// FIXME: 丑陋的代码,这一段代码变成这样的原因,Sparkle 应用各有各的写法,有的应用只有从 title 读取版本号,有的从 item 有的从 enclosure
// FIXME: 版本号也有问题,有的 sparkle:version 是 x.x.x 的形式,有的 sparkle:shortVersionString 是
// FIXME: homebrew 的接口也有点问题,比如 Version 是 4.0,通过接口查询会变成 4,比如有些应用本地查到是 7.0.2,接口查到是 7.0.2.7,但其实是一个版本
let local_cmp_version = if !app_info.short_version.is_empty() && !app_info.is_sparkle_app()
|| (remote_info.version.contains('.') && app_info.short_version.contains('.'))
{
&app_info.short_version
} else {
&app_info.version
};
let ordering = cmp_version(local_cmp_version, &remote_info.version, false);
if ordering.is_lt() {
// 判断为有更新
if self.notification {
// 判断需要使用通知发送结果
Notification::new_update_notification(
app_info.name.clone(),
local_cmp_version.to_string(),
remote_info.version,
// mas 应用还是打开 Mac App Store
if self.open_by_app && !&app_info.is_mas_app() {
format!("file://{}", path.to_str().unwrap_or_default())
} else {
remote_info.update_page_url
},
self.open_by_app,
)
.post()
} else {
println!("=====");
println!("{}", app_info.name);
if self.verbose {
println!("{}", app_info.check_update_type);
}
println!("{local_cmp_version} -> {}", remote_info.version);
if self.open_by_app {
println!("file://{}", path.to_str().unwrap_or_default());
} else {
println!("{}", remote_info.update_page_url);
}
println!("=====\n");
}
} else if self.verbose {
// 判断为没有更新但是需要详细输出
if self.notification {
Notification::new_verbose_notification(
&app_info,
local_cmp_version.to_string(),
&remote_info,
self.open_by_app,
Some(if self.open_by_app && !&app_info.is_mas_app() {
format!("file://{}", path.to_str().unwrap_or_default())
} else {
remote_info.update_page_url.to_owned()
}),
)
.post()
} else {
println!("-----");
println!("{}", app_info.name);
println!("{}", app_info.check_update_type);
println!("{local_cmp_version} -> {}", remote_info.version);
if self.open_by_app && !app_info.is_mas_app() {
println!("file://{}", path.to_str().unwrap_or_default());
} else {
println!("{}", remote_info.update_page_url);
}
println!("-----\n");
}
}
}
}