Skip to content

Commit 4acf7be

Browse files
committed
feat 增加使用system_profiler获取应用的方法
1 parent 0077a92 commit 4acf7be

3 files changed

Lines changed: 188 additions & 0 deletions

File tree

src/local/local_app_info.rs

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
use std::collections::HashMap;
2+
use std::process::Command;
3+
use serde::{Deserialize, Serialize};
4+
5+
#[derive(Debug, Deserialize, Serialize, Clone)]
6+
pub struct ApplicationInfo {
7+
#[serde(rename = "_name")]
8+
pub name: String,
9+
#[serde(rename = "obtained_from")]
10+
pub obtained_from: String,
11+
pub version: Option<String>,
12+
#[serde(rename = "lastModified")]
13+
pub last_modified: String,
14+
pub kind: Option<String>,
15+
#[serde(rename = "arch_kind")]
16+
pub architecture: String,
17+
#[serde(rename = "signed_by")]
18+
pub signed_by: Option<Vec<String>>,
19+
pub path: Option<String>,
20+
}
21+
22+
pub fn get_applications_info() -> Result<HashMap<String, ApplicationInfo>, Box<dyn std::error::Error>> {
23+
let output = Command::new("system_profiler")
24+
.args(&["SPApplicationsDataType", "-json"])
25+
.output()?;
26+
27+
if !output.status.success() {
28+
return Err(format!("命令执行失败: {}", String::from_utf8_lossy(&output.stderr)).into());
29+
}
30+
31+
let json_str = String::from_utf8(output.stdout)?;
32+
33+
// 直接解析为 HashMap
34+
let parsed: HashMap<String, Vec<ApplicationInfo>> = serde_json::from_str(&json_str)?;
35+
36+
if let Some(apps) = parsed.get("SPApplicationsDataType") {
37+
let applications: HashMap<String, ApplicationInfo> = apps
38+
.iter()
39+
.map(|app| (app.name.clone(), app.clone()))
40+
.collect();
41+
Ok(applications)
42+
} else {
43+
Err("未找到应用数据".into())
44+
}
45+
}
46+
47+
// 扩展功能:应用过滤
48+
pub struct ApplicationFilter<'a> {
49+
pub name_contains: Option<&'a str>,
50+
pub obtained_from: Option<&'a str>,
51+
pub architecture: Option<&'a str>,
52+
}
53+
54+
impl ApplicationInfo {
55+
pub fn matches_filter(&self, filter: &ApplicationFilter) -> bool {
56+
if let Some(name) = filter.name_contains {
57+
if !self.name.to_lowercase().contains(&name.to_lowercase()) {
58+
return false;
59+
}
60+
}
61+
62+
if let Some(source) = filter.obtained_from {
63+
if self.obtained_from != source {
64+
return false;
65+
}
66+
}
67+
68+
if let Some(arch) = filter.architecture {
69+
if self.architecture != arch {
70+
return false;
71+
}
72+
}
73+
74+
true
75+
}
76+
}
77+
78+
// 使用过滤功能
79+
pub fn filter_applications() -> Result<(), Box<dyn std::error::Error>> {
80+
let applications = get_applications_info()?;
81+
82+
let filter = ApplicationFilter {
83+
name_contains: Some("chrome"),
84+
obtained_from: Some("Identified Developer"),
85+
architecture: None,
86+
};
87+
88+
println!("过滤结果:");
89+
for app in applications.values().filter(|a| a.matches_filter(&filter)) {
90+
println!(" - {} (来源: {})", app.name, app.obtained_from);
91+
}
92+
93+
Ok(())
94+
}
95+
96+
#[allow(dead_code)]
97+
fn main() -> Result<(), Box<dyn std::error::Error>> {
98+
match get_applications_info() {
99+
Ok(applications) => {
100+
println!("找到 {} 个应用", applications.len());
101+
102+
// 按来源分类统计
103+
let mut by_source: HashMap<String, Vec<&ApplicationInfo>> = HashMap::new();
104+
for app in applications.values() {
105+
by_source.entry(app.obtained_from.clone())
106+
.or_insert_with(Vec::new)
107+
.push(app);
108+
}
109+
110+
// 打印统计信息
111+
println!("\n应用来源统计:");
112+
for (source, apps) in &by_source {
113+
println!(" {}: {} 个应用", source, apps.len());
114+
}
115+
116+
// 打印前10个应用的信息
117+
println!("\n前10个应用详情:");
118+
for (i, app) in applications.values().take(10).enumerate() {
119+
println!("{}. {}", i + 1, app.name);
120+
println!(" 版本: {:?}", app.version);
121+
println!(" 来源: {}", app.obtained_from);
122+
println!(" 架构: {}", app.architecture);
123+
println!(" 签名: {:?}", app.signed_by);
124+
println!();
125+
}
126+
127+
// 查找特定来源的应用
128+
println!("来自 App Store 的应用:");
129+
for app in applications.values().filter(|a| a.obtained_from == "Apple") {
130+
println!(" - {} (v{:?})", app.name, app.version);
131+
}
132+
}
133+
Err(e) => {
134+
eprintln!("错误: {}", e);
135+
}
136+
}
137+
138+
Ok(())
139+
}

src/local/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ use std::{fmt::Display, path::Path};
44
pub mod config;
55
pub mod notification;
66
pub mod plist;
7+
pub mod local_app_info;
78

89
/// 应用信息结构体
910
#[derive(Debug)]

tests/local_app_info_test.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use std::collections::HashMap;
2+
3+
use appcu::local::local_app_info::{self, ApplicationInfo};
4+
5+
#[test]
6+
fn local_app_info_test() {
7+
match local_app_info::get_applications_info() {
8+
Ok(applications) => {
9+
println!("找到 {} 个应用", applications.len());
10+
11+
// 按来源分类统计
12+
let mut by_source: HashMap<String, Vec<&ApplicationInfo>> = HashMap::new();
13+
for app in applications.values() {
14+
by_source.entry(app.obtained_from.clone())
15+
.or_insert_with(Vec::new)
16+
.push(app);
17+
}
18+
19+
// 打印统计信息
20+
println!("\n应用来源统计:");
21+
for (source, apps) in &by_source {
22+
println!(" {}: {} 个应用", source, apps.len());
23+
}
24+
25+
// 打印前10个应用的信息
26+
println!("\n前10个应用详情:");
27+
for (i, app) in applications.values().take(10).enumerate() {
28+
println!("{}. {}", i + 1, app.name);
29+
println!(" 版本: {:?}", app.version);
30+
println!(" 来源: {:?}", app.obtained_from);
31+
println!(" 架构: {:?}", app.architecture);
32+
println!(" 签名: {:?}", app.signed_by);
33+
println!(" 签名: {:?}", app.path);
34+
println!();
35+
}
36+
37+
// 查找特定来源的应用
38+
println!("来自 App Store 的应用:");
39+
for app in applications.values().filter(|a| a.obtained_from == "Apple") {
40+
println!(" - {} (v{:?})", app.name, app.version);
41+
}
42+
}
43+
Err(e) => {
44+
eprintln!("错误: {}", e);
45+
assert!(true, "获取应用信息报错")
46+
}
47+
}
48+
}

0 commit comments

Comments
 (0)