use std::{path::Path, str::FromStr};
use crate::{acl::Identifier, platform::Target};
use serde::{Deserialize, Serialize};
use super::Scopes;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub enum PermissionEntry {
PermissionRef(Identifier),
ExtendedPermission {
identifier: Identifier,
#[serde(default, flatten)]
scope: Scopes,
},
}
impl PermissionEntry {
pub fn identifier(&self) -> &Identifier {
match self {
Self::PermissionRef(identifier) => identifier,
Self::ExtendedPermission {
identifier,
scope: _,
} => identifier,
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
pub struct Capability {
pub identifier: String,
#[serde(default)]
pub description: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub remote: Option<CapabilityRemote>,
#[serde(default = "default_capability_local")]
pub local: bool,
pub windows: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub webviews: Vec<String>,
pub permissions: Vec<PermissionEntry>,
#[serde(default = "default_platforms", skip_serializing_if = "Vec::is_empty")]
pub platforms: Vec<Target>,
}
fn default_capability_local() -> bool {
true
}
fn default_platforms() -> Vec<Target> {
vec![
Target::Linux,
Target::MacOS,
Target::Windows,
Target::Android,
Target::Ios,
]
}
#[derive(Debug, Default, Clone, Serialize, Deserialize, Eq, PartialEq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(rename_all = "camelCase")]
pub struct CapabilityRemote {
pub urls: Vec<String>,
}
#[derive(Deserialize)]
#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
#[serde(untagged)]
pub enum CapabilityFile {
Capability(Capability),
List {
capabilities: Vec<Capability>,
},
}
impl CapabilityFile {
pub fn load<P: AsRef<Path>>(path: P) -> Result<Self, super::Error> {
let path = path.as_ref();
let capability_file = std::fs::read_to_string(path).map_err(super::Error::ReadFile)?;
let ext = path.extension().unwrap().to_string_lossy().to_string();
let file: Self = match ext.as_str() {
"toml" => toml::from_str(&capability_file)?,
"json" => serde_json::from_str(&capability_file)?,
_ => return Err(super::Error::UnknownCapabilityFormat(ext)),
};
Ok(file)
}
}
impl FromStr for CapabilityFile {
type Err = super::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.chars().next() {
Some('[') => toml::from_str(s).map_err(Into::into),
Some('{') => serde_json::from_str(s).map_err(Into::into),
_ => Err(super::Error::UnknownCapabilityFormat(s.into())),
}
}
}
#[cfg(feature = "build")]
mod build {
use std::convert::identity;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens, TokenStreamExt};
use super::*;
use crate::{literal_struct, tokens::*};
impl ToTokens for CapabilityRemote {
fn to_tokens(&self, tokens: &mut TokenStream) {
let urls = vec_lit(&self.urls, str_lit);
literal_struct!(
tokens,
::tauri::utils::acl::capability::CapabilityRemote,
urls
);
}
}
impl ToTokens for PermissionEntry {
fn to_tokens(&self, tokens: &mut TokenStream) {
let prefix = quote! { ::tauri::utils::acl::capability::PermissionEntry };
tokens.append_all(match self {
Self::PermissionRef(id) => {
quote! { #prefix::PermissionRef(#id) }
}
Self::ExtendedPermission { identifier, scope } => {
quote! { #prefix::ExtendedPermission {
identifier: #identifier,
scope: #scope
} }
}
});
}
}
impl ToTokens for Capability {
fn to_tokens(&self, tokens: &mut TokenStream) {
let identifier = str_lit(&self.identifier);
let description = str_lit(&self.description);
let remote = &self.remote;
let local = self.local;
let windows = vec_lit(&self.windows, str_lit);
let permissions = vec_lit(&self.permissions, identity);
let platforms = vec_lit(&self.platforms, identity);
literal_struct!(
tokens,
::tauri::utils::acl::capability::Capability,
identifier,
description,
remote,
local,
windows,
permissions,
platforms
);
}
}
}