feat(cli): handle known target specific plugins on permission add #10596 (#10598)

Closes #10596
This commit is contained in:
Lucas Fernandes Nogueira 2024-08-13 14:01:10 -03:00 committed by GitHub
parent 712f1049fa
commit f35bcda289
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 170 additions and 62 deletions

View File

@ -0,0 +1,7 @@
---
"tauri-cli": patch:enhance
"@tauri-apps/cli": patch:enhance
---
`permission add` and `add` commands now check if the plugin is known and if it is either desktop or mobile only
we add the permission to a target-specific capability.

View File

@ -31,14 +31,36 @@ impl TomlOrJson {
}
}
fn insert_permission(&mut self, idenitifer: String) {
fn platforms(&self) -> Option<Vec<&str>> {
match self {
TomlOrJson::Toml(t) => t.get("platforms").and_then(|k| {
k.as_array()
.and_then(|array| array.iter().map(|v| v.as_str()).collect())
}),
TomlOrJson::Json(j) => j.get("platforms").and_then(|k| {
if let Some(array) = k.as_array() {
let mut items = Vec::new();
for item in array {
if let Some(s) = item.as_str() {
items.push(s);
}
}
Some(items)
} else {
None
}
}),
}
}
fn insert_permission(&mut self, identifier: String) {
match self {
TomlOrJson::Toml(t) => {
let permissions = t.entry("permissions").or_insert_with(|| {
toml_edit::Item::Value(toml_edit::Value::Array(toml_edit::Array::new()))
});
if let Some(permissions) = permissions.as_array_mut() {
permissions.push(idenitifer)
permissions.push(identifier)
};
}
@ -48,7 +70,7 @@ impl TomlOrJson {
.entry("permissions")
.or_insert_with(|| serde_json::Value::Array(Vec::new()));
if let Some(permissions) = permissions.as_array_mut() {
permissions.push(serde_json::Value::String(idenitifer))
permissions.push(serde_json::Value::String(identifier))
};
}
}
@ -100,7 +122,13 @@ pub fn command(options: Options) -> Result<()> {
);
}
let capabilities = std::fs::read_dir(&capabilities_dir)?
let known_plugins = crate::helpers::plugins::known_plugins();
let known_plugin = options
.identifier
.split_once(':')
.and_then(|(plugin, _permission)| known_plugins.get(&plugin));
let capabilities_iter = std::fs::read_dir(&capabilities_dir)?
.flatten()
.filter(|e| e.file_type().map(|e| e.is_file()).unwrap_or_default())
.filter_map(|e| {
@ -109,8 +137,66 @@ pub fn command(options: Options) -> Result<()> {
Some(c) => (c == capability.identifier()).then_some((capability, path)),
None => Some((capability, path)),
})
})
.collect::<Vec<_>>();
});
let (desktop_only, mobile_only) = known_plugin
.map(|p| (p.desktop_only, p.mobile_only))
.unwrap_or_default();
let expected_capability_config = if desktop_only {
Some((
vec![
tauri_utils::platform::Target::MacOS.to_string(),
tauri_utils::platform::Target::Windows.to_string(),
tauri_utils::platform::Target::Linux.to_string(),
],
"desktop",
))
} else if mobile_only {
Some((
vec![
tauri_utils::platform::Target::Android.to_string(),
tauri_utils::platform::Target::Ios.to_string(),
],
"mobile",
))
} else {
None
};
let capabilities = if let Some((expected_platforms, target_name)) = expected_capability_config {
let mut capabilities = capabilities_iter
.filter(|(capability, _path)| {
capability.platforms().map_or(
false, /* allows any target, so we should skip it since we're adding a target-specific plugin */
|platforms| {
// all platforms must be in the expected platforms list
platforms.iter().all(|p| expected_platforms.contains(&p.to_string()))
},
)
})
.collect::<Vec<_>>();
if capabilities.is_empty() {
let identifier = format!("{target_name}-capability");
let capability_path = capabilities_dir.join(target_name).with_extension("json");
log::info!(
"Capability matching platforms {expected_platforms:?} not found, creating {}",
capability_path.display()
);
capabilities.push((
TomlOrJson::Json(serde_json::json!({
"identifier": identifier,
"platforms": expected_platforms
})),
capability_path,
));
}
capabilities
} else {
capabilities_iter.collect::<Vec<_>>()
};
let mut capabilities = if capabilities.len() > 1 {
let selections = prompts::multiselect(
@ -132,6 +218,11 @@ pub fn command(options: Options) -> Result<()> {
.as_slice(),
None,
)?;
if selections.is_empty() {
anyhow::bail!("You did not select any capabilities to update");
}
selections
.into_iter()
.map(|idx| capabilities[idx].clone())
@ -140,6 +231,10 @@ pub fn command(options: Options) -> Result<()> {
capabilities
};
if capabilities.is_empty() {
anyhow::bail!("Could not find a capability to update");
}
for (capability, path) in &mut capabilities {
capability.insert_permission(options.identifier.clone());
std::fs::write(&*path, capability.to_string()?)?;

View File

@ -16,61 +16,7 @@ use crate::{
Result,
};
use std::{collections::HashMap, process::Command};
#[derive(Default)]
struct PluginMetadata {
desktop_only: bool,
mobile_only: bool,
rust_only: bool,
builder: bool,
}
// known plugins with particular cases
fn plugins() -> HashMap<&'static str, PluginMetadata> {
let mut plugins: HashMap<&'static str, PluginMetadata> = HashMap::new();
// desktop-only
for p in [
"authenticator",
"autostart",
"cli",
"global-shortcut",
"positioner",
"single-instance",
"updater",
"window-state",
] {
plugins.entry(p).or_default().desktop_only = true;
}
// mobile-only
for p in ["barcode-scanner", "biometric", "nfc"] {
plugins.entry(p).or_default().mobile_only = true;
}
// uses builder pattern
for p in [
"global-shortcut",
"localhost",
"log",
"sql",
"store",
"stronghold",
"updater",
"window-state",
] {
plugins.entry(p).or_default().builder = true;
}
// rust-only
#[allow(clippy::single_element_loop)]
for p in ["localhost", "persisted-scope", "single-instance"] {
plugins.entry(p).or_default().rust_only = true;
}
plugins
}
use std::process::Command;
#[derive(Debug, Parser)]
#[clap(about = "Add a tauri plugin to the project")]
@ -104,7 +50,7 @@ pub fn command(options: Options) -> Result<()> {
let crate_name = format!("tauri-plugin-{plugin}");
let npm_name = format!("@tauri-apps/plugin-{plugin}");
let mut plugins = plugins();
let mut plugins = crate::helpers::plugins::known_plugins();
let metadata = plugins.remove(plugin).unwrap_or_default();
let app_dir = resolve_app_dir();

View File

@ -9,6 +9,7 @@ pub mod config;
pub mod flock;
pub mod framework;
pub mod npm;
pub mod plugins;
pub mod prompts;
pub mod template;
pub mod updater_signature;

View File

@ -0,0 +1,59 @@
// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use std::collections::HashMap;
#[derive(Default)]
pub struct PluginMetadata {
pub desktop_only: bool,
pub mobile_only: bool,
pub rust_only: bool,
pub builder: bool,
}
// known plugins with particular cases
pub fn known_plugins() -> HashMap<&'static str, PluginMetadata> {
let mut plugins: HashMap<&'static str, PluginMetadata> = HashMap::new();
// desktop-only
for p in [
"authenticator",
"autostart",
"cli",
"global-shortcut",
"positioner",
"single-instance",
"updater",
"window-state",
] {
plugins.entry(p).or_default().desktop_only = true;
}
// mobile-only
for p in ["barcode-scanner", "biometric", "nfc", "haptics"] {
plugins.entry(p).or_default().mobile_only = true;
}
// uses builder pattern
for p in [
"global-shortcut",
"localhost",
"log",
"sql",
"store",
"stronghold",
"updater",
"window-state",
] {
plugins.entry(p).or_default().builder = true;
}
// rust-only
#[allow(clippy::single_element_loop)]
for p in ["localhost", "persisted-scope", "single-instance"] {
plugins.entry(p).or_default().rust_only = true;
}
plugins
}