Skip to content

Commit

Permalink
feat(cli): handle known target specific plugins on permission add #10596
Browse files Browse the repository at this point in the history
 (#10598)

Closes #10596
  • Loading branch information
lucasfernog authored Aug 13, 2024
1 parent 712f104 commit f35bcda
Show file tree
Hide file tree
Showing 5 changed files with 170 additions and 62 deletions.
7 changes: 7 additions & 0 deletions .changes/permissions-add-target-specific.md
Original file line number Diff line number Diff line change
@@ -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.
107 changes: 101 additions & 6 deletions tooling/cli/src/acl/permission/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
};
}

Expand All @@ -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))
};
}
}
Expand Down Expand Up @@ -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| {
Expand All @@ -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(
Expand All @@ -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())
Expand All @@ -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()?)?;
Expand Down
58 changes: 2 additions & 56 deletions tooling/cli/src/add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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();
Expand Down
1 change: 1 addition & 0 deletions tooling/cli/src/helpers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
59 changes: 59 additions & 0 deletions tooling/cli/src/helpers/plugins.rs
Original file line number Diff line number Diff line change
@@ -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
}

0 comments on commit f35bcda

Please sign in to comment.