Skip to content

Commit f35bcda

Browse files
authored
feat(cli): handle known target specific plugins on permission add #10596 (#10598)
Closes #10596
1 parent 712f104 commit f35bcda

File tree

5 files changed

+170
-62
lines changed

5 files changed

+170
-62
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
"tauri-cli": patch:enhance
3+
"@tauri-apps/cli": patch:enhance
4+
---
5+
6+
`permission add` and `add` commands now check if the plugin is known and if it is either desktop or mobile only
7+
we add the permission to a target-specific capability.

tooling/cli/src/acl/permission/add.rs

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,36 @@ impl TomlOrJson {
3131
}
3232
}
3333

34-
fn insert_permission(&mut self, idenitifer: String) {
34+
fn platforms(&self) -> Option<Vec<&str>> {
35+
match self {
36+
TomlOrJson::Toml(t) => t.get("platforms").and_then(|k| {
37+
k.as_array()
38+
.and_then(|array| array.iter().map(|v| v.as_str()).collect())
39+
}),
40+
TomlOrJson::Json(j) => j.get("platforms").and_then(|k| {
41+
if let Some(array) = k.as_array() {
42+
let mut items = Vec::new();
43+
for item in array {
44+
if let Some(s) = item.as_str() {
45+
items.push(s);
46+
}
47+
}
48+
Some(items)
49+
} else {
50+
None
51+
}
52+
}),
53+
}
54+
}
55+
56+
fn insert_permission(&mut self, identifier: String) {
3557
match self {
3658
TomlOrJson::Toml(t) => {
3759
let permissions = t.entry("permissions").or_insert_with(|| {
3860
toml_edit::Item::Value(toml_edit::Value::Array(toml_edit::Array::new()))
3961
});
4062
if let Some(permissions) = permissions.as_array_mut() {
41-
permissions.push(idenitifer)
63+
permissions.push(identifier)
4264
};
4365
}
4466

@@ -48,7 +70,7 @@ impl TomlOrJson {
4870
.entry("permissions")
4971
.or_insert_with(|| serde_json::Value::Array(Vec::new()));
5072
if let Some(permissions) = permissions.as_array_mut() {
51-
permissions.push(serde_json::Value::String(idenitifer))
73+
permissions.push(serde_json::Value::String(identifier))
5274
};
5375
}
5476
}
@@ -100,7 +122,13 @@ pub fn command(options: Options) -> Result<()> {
100122
);
101123
}
102124

103-
let capabilities = std::fs::read_dir(&capabilities_dir)?
125+
let known_plugins = crate::helpers::plugins::known_plugins();
126+
let known_plugin = options
127+
.identifier
128+
.split_once(':')
129+
.and_then(|(plugin, _permission)| known_plugins.get(&plugin));
130+
131+
let capabilities_iter = std::fs::read_dir(&capabilities_dir)?
104132
.flatten()
105133
.filter(|e| e.file_type().map(|e| e.is_file()).unwrap_or_default())
106134
.filter_map(|e| {
@@ -109,8 +137,66 @@ pub fn command(options: Options) -> Result<()> {
109137
Some(c) => (c == capability.identifier()).then_some((capability, path)),
110138
None => Some((capability, path)),
111139
})
112-
})
113-
.collect::<Vec<_>>();
140+
});
141+
142+
let (desktop_only, mobile_only) = known_plugin
143+
.map(|p| (p.desktop_only, p.mobile_only))
144+
.unwrap_or_default();
145+
146+
let expected_capability_config = if desktop_only {
147+
Some((
148+
vec![
149+
tauri_utils::platform::Target::MacOS.to_string(),
150+
tauri_utils::platform::Target::Windows.to_string(),
151+
tauri_utils::platform::Target::Linux.to_string(),
152+
],
153+
"desktop",
154+
))
155+
} else if mobile_only {
156+
Some((
157+
vec![
158+
tauri_utils::platform::Target::Android.to_string(),
159+
tauri_utils::platform::Target::Ios.to_string(),
160+
],
161+
"mobile",
162+
))
163+
} else {
164+
None
165+
};
166+
167+
let capabilities = if let Some((expected_platforms, target_name)) = expected_capability_config {
168+
let mut capabilities = capabilities_iter
169+
.filter(|(capability, _path)| {
170+
capability.platforms().map_or(
171+
false, /* allows any target, so we should skip it since we're adding a target-specific plugin */
172+
|platforms| {
173+
// all platforms must be in the expected platforms list
174+
platforms.iter().all(|p| expected_platforms.contains(&p.to_string()))
175+
},
176+
)
177+
})
178+
.collect::<Vec<_>>();
179+
180+
if capabilities.is_empty() {
181+
let identifier = format!("{target_name}-capability");
182+
let capability_path = capabilities_dir.join(target_name).with_extension("json");
183+
log::info!(
184+
"Capability matching platforms {expected_platforms:?} not found, creating {}",
185+
capability_path.display()
186+
);
187+
capabilities.push((
188+
TomlOrJson::Json(serde_json::json!({
189+
"identifier": identifier,
190+
"platforms": expected_platforms
191+
})),
192+
capability_path,
193+
));
194+
}
195+
196+
capabilities
197+
} else {
198+
capabilities_iter.collect::<Vec<_>>()
199+
};
114200

115201
let mut capabilities = if capabilities.len() > 1 {
116202
let selections = prompts::multiselect(
@@ -132,6 +218,11 @@ pub fn command(options: Options) -> Result<()> {
132218
.as_slice(),
133219
None,
134220
)?;
221+
222+
if selections.is_empty() {
223+
anyhow::bail!("You did not select any capabilities to update");
224+
}
225+
135226
selections
136227
.into_iter()
137228
.map(|idx| capabilities[idx].clone())
@@ -140,6 +231,10 @@ pub fn command(options: Options) -> Result<()> {
140231
capabilities
141232
};
142233

234+
if capabilities.is_empty() {
235+
anyhow::bail!("Could not find a capability to update");
236+
}
237+
143238
for (capability, path) in &mut capabilities {
144239
capability.insert_permission(options.identifier.clone());
145240
std::fs::write(&*path, capability.to_string()?)?;

tooling/cli/src/add.rs

Lines changed: 2 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -16,61 +16,7 @@ use crate::{
1616
Result,
1717
};
1818

19-
use std::{collections::HashMap, process::Command};
20-
21-
#[derive(Default)]
22-
struct PluginMetadata {
23-
desktop_only: bool,
24-
mobile_only: bool,
25-
rust_only: bool,
26-
builder: bool,
27-
}
28-
29-
// known plugins with particular cases
30-
fn plugins() -> HashMap<&'static str, PluginMetadata> {
31-
let mut plugins: HashMap<&'static str, PluginMetadata> = HashMap::new();
32-
33-
// desktop-only
34-
for p in [
35-
"authenticator",
36-
"autostart",
37-
"cli",
38-
"global-shortcut",
39-
"positioner",
40-
"single-instance",
41-
"updater",
42-
"window-state",
43-
] {
44-
plugins.entry(p).or_default().desktop_only = true;
45-
}
46-
47-
// mobile-only
48-
for p in ["barcode-scanner", "biometric", "nfc"] {
49-
plugins.entry(p).or_default().mobile_only = true;
50-
}
51-
52-
// uses builder pattern
53-
for p in [
54-
"global-shortcut",
55-
"localhost",
56-
"log",
57-
"sql",
58-
"store",
59-
"stronghold",
60-
"updater",
61-
"window-state",
62-
] {
63-
plugins.entry(p).or_default().builder = true;
64-
}
65-
66-
// rust-only
67-
#[allow(clippy::single_element_loop)]
68-
for p in ["localhost", "persisted-scope", "single-instance"] {
69-
plugins.entry(p).or_default().rust_only = true;
70-
}
71-
72-
plugins
73-
}
19+
use std::process::Command;
7420

7521
#[derive(Debug, Parser)]
7622
#[clap(about = "Add a tauri plugin to the project")]
@@ -104,7 +50,7 @@ pub fn command(options: Options) -> Result<()> {
10450
let crate_name = format!("tauri-plugin-{plugin}");
10551
let npm_name = format!("@tauri-apps/plugin-{plugin}");
10652

107-
let mut plugins = plugins();
53+
let mut plugins = crate::helpers::plugins::known_plugins();
10854
let metadata = plugins.remove(plugin).unwrap_or_default();
10955

11056
let app_dir = resolve_app_dir();

tooling/cli/src/helpers/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod config;
99
pub mod flock;
1010
pub mod framework;
1111
pub mod npm;
12+
pub mod plugins;
1213
pub mod prompts;
1314
pub mod template;
1415
pub mod updater_signature;

tooling/cli/src/helpers/plugins.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2+
// SPDX-License-Identifier: Apache-2.0
3+
// SPDX-License-Identifier: MIT
4+
5+
use std::collections::HashMap;
6+
7+
#[derive(Default)]
8+
pub struct PluginMetadata {
9+
pub desktop_only: bool,
10+
pub mobile_only: bool,
11+
pub rust_only: bool,
12+
pub builder: bool,
13+
}
14+
15+
// known plugins with particular cases
16+
pub fn known_plugins() -> HashMap<&'static str, PluginMetadata> {
17+
let mut plugins: HashMap<&'static str, PluginMetadata> = HashMap::new();
18+
19+
// desktop-only
20+
for p in [
21+
"authenticator",
22+
"autostart",
23+
"cli",
24+
"global-shortcut",
25+
"positioner",
26+
"single-instance",
27+
"updater",
28+
"window-state",
29+
] {
30+
plugins.entry(p).or_default().desktop_only = true;
31+
}
32+
33+
// mobile-only
34+
for p in ["barcode-scanner", "biometric", "nfc", "haptics"] {
35+
plugins.entry(p).or_default().mobile_only = true;
36+
}
37+
38+
// uses builder pattern
39+
for p in [
40+
"global-shortcut",
41+
"localhost",
42+
"log",
43+
"sql",
44+
"store",
45+
"stronghold",
46+
"updater",
47+
"window-state",
48+
] {
49+
plugins.entry(p).or_default().builder = true;
50+
}
51+
52+
// rust-only
53+
#[allow(clippy::single_element_loop)]
54+
for p in ["localhost", "persisted-scope", "single-instance"] {
55+
plugins.entry(p).or_default().rust_only = true;
56+
}
57+
58+
plugins
59+
}

0 commit comments

Comments
 (0)