use crate::error::Context;
use serde::Serialize;
use std::fmt::Display;
pub mod capability;
pub mod permission;
#[derive(Debug, clap::ValueEnum, Clone)]
enum FileFormat {
Json,
Toml,
}
impl Display for FileFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Json => write!(f, "json"),
Self::Toml => write!(f, "toml"),
}
}
}
impl FileFormat {
pub fn extension(&self) -> &'static str {
match self {
Self::Json => "json",
Self::Toml => "toml",
}
}
pub fn serialize<S: Serialize>(&self, s: &S) -> crate::Result<String> {
let contents = match self {
Self::Json => serde_json::to_string_pretty(s).context("failed to serialize JSON")?,
Self::Toml => toml_edit::ser::to_string_pretty(s).context("failed to serialize TOML")?,
};
Ok(contents)
}
}
fn split_comma_separated(input: &str) -> impl Iterator<Item = String> + '_ {
input
.split(',')
.map(str::trim)
.filter(|s| !s.is_empty())
.map(ToString::to_string)
}
fn validate_capability_identifier(identifier: &str) -> crate::Result<()> {
if identifier.is_empty() {
crate::error::bail!("capability identifier cannot be empty");
}
if !identifier
.bytes()
.all(|b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_')
{
crate::error::bail!(
"invalid capability identifier `{}`: only ASCII letters, digits, hyphens and underscores are allowed",
identifier
);
}
Ok(())
}
fn validate_permission_identifier(identifier: &str) -> crate::Result<()> {
let parsed = match tauri_utils::acl::identifier::Identifier::try_from(identifier.to_string()) {
Ok(parsed) => parsed,
Err(e) => crate::error::bail!("invalid permission identifier `{}`: {}", identifier, e),
};
if parsed.get_prefix().is_some() {
crate::error::bail!(
"invalid permission identifier `{}`: app permissions cannot have a `:` prefix",
identifier
);
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::{
split_comma_separated, validate_capability_identifier, validate_permission_identifier,
};
#[test]
fn splits_comma_separated_input() {
assert_eq!(
split_comma_separated("fs:default, core:default,, ").collect::<Vec<_>>(),
vec!["fs:default", "core:default"]
);
assert_eq!(split_comma_separated(" ").count(), 0);
}
#[test]
fn capability_identifier_validation() {
assert!(validate_capability_identifier("main-capability").is_ok());
assert!(validate_capability_identifier("main_window2").is_ok());
for invalid in ["", "../../x", "a/b", "a\\b", "..", "a.b", "a:b", "a b"] {
assert!(
validate_capability_identifier(invalid).is_err(),
"{invalid} should be rejected"
);
}
}
#[test]
fn permission_identifier_validation() {
assert!(validate_permission_identifier("allow-greet").is_ok());
for invalid in [
"",
"../../x",
"a/b",
"a\\b",
"..",
"a.b",
"fs:allow-read",
"-a",
] {
assert!(
validate_permission_identifier(invalid).is_err(),
"{invalid} should be rejected"
);
}
}
}