use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct PermissionsConfig {
#[serde(default)]
pub location: Option<LocationPermission>,
#[serde(default)]
pub camera: Option<SimplePermission>,
#[serde(default)]
pub microphone: Option<SimplePermission>,
#[serde(default)]
pub notifications: Option<SimplePermission>,
#[serde(default)]
pub photos: Option<StoragePermission>,
#[serde(default)]
pub bluetooth: Option<SimplePermission>,
#[serde(default, rename = "background-location")]
pub background_location: Option<SimplePermission>,
#[serde(default)]
pub contacts: Option<StoragePermission>,
#[serde(default)]
pub calendar: Option<StoragePermission>,
#[serde(default)]
pub biometrics: Option<SimplePermission>,
#[serde(default)]
pub nfc: Option<SimplePermission>,
#[serde(default)]
pub motion: Option<SimplePermission>,
#[serde(default)]
pub health: Option<StoragePermission>,
#[serde(default)]
pub speech: Option<SimplePermission>,
#[serde(default, rename = "media-library")]
pub media_library: Option<SimplePermission>,
#[serde(default)]
pub siri: Option<SimplePermission>,
#[serde(default)]
pub homekit: Option<SimplePermission>,
#[serde(default, rename = "local-network")]
pub local_network: Option<SimplePermission>,
#[serde(default, rename = "nearby-wifi")]
pub nearby_wifi: Option<SimplePermission>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct SimplePermission {
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct DeepLinkConfig {
#[serde(default)]
pub schemes: Vec<String>,
#[serde(default)]
pub hosts: Vec<String>,
#[serde(default)]
pub paths: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct BackgroundConfig {
#[serde(default)]
pub location: bool,
#[serde(default)]
pub audio: bool,
#[serde(default)]
pub fetch: bool,
#[serde(default, rename = "remote-notifications")]
pub remote_notifications: bool,
#[serde(default)]
pub voip: bool,
#[serde(default)]
pub bluetooth: bool,
#[serde(default, rename = "external-accessory")]
pub external_accessory: bool,
#[serde(default)]
pub processing: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct LocationPermission {
#[serde(default)]
pub precision: LocationPrecision,
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, JsonSchema)]
pub enum LocationPrecision {
#[default]
#[serde(rename = "fine")]
Fine,
#[serde(rename = "coarse")]
Coarse,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct StoragePermission {
#[serde(default)]
pub access: StorageAccess,
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq, JsonSchema)]
pub enum StorageAccess {
#[serde(rename = "read")]
Read,
#[serde(rename = "write")]
Write,
#[default]
#[serde(rename = "read-write")]
ReadWrite,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct RawPermission {
pub description: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct IosConfig {
#[serde(default)]
pub identifier: Option<String>,
#[serde(default)]
pub publisher: Option<String>,
#[serde(default)]
pub icon: Option<Vec<String>>,
#[serde(default)]
pub resources: Option<Vec<String>>,
#[serde(default)]
pub copyright: Option<String>,
#[serde(default)]
pub category: Option<String>,
#[serde(default)]
pub short_description: Option<String>,
#[serde(default)]
pub long_description: Option<String>,
#[serde(default)]
pub deployment_target: Option<String>,
#[serde(default)]
pub info_plist: Option<PathBuf>,
#[serde(default)]
pub entitlements: IosEntitlements,
#[serde(default)]
pub plist: HashMap<String, serde_json::Value>,
#[serde(default)]
pub raw: IosRawConfig,
#[serde(default)]
pub url_schemes: Vec<String>,
#[serde(default)]
pub background_modes: Vec<String>,
#[serde(default)]
pub document_types: Vec<IosDocumentType>,
#[serde(default)]
pub exported_type_identifiers: Vec<IosTypeIdentifier>,
#[serde(default)]
pub imported_type_identifiers: Vec<IosTypeIdentifier>,
#[serde(default)]
pub widget_extensions: Vec<WidgetExtensionConfig>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct WidgetExtensionConfig {
pub source: String,
pub display_name: String,
pub bundle_id_suffix: String,
#[serde(default)]
pub deployment_target: Option<String>,
pub module_name: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct IosDocumentType {
pub name: String,
#[serde(default)]
pub extensions: Vec<String>,
#[serde(default)]
pub mime_types: Vec<String>,
#[serde(default)]
pub types: Vec<String>,
#[serde(default)]
pub icon: Option<String>,
#[serde(default)]
pub role: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct IosTypeIdentifier {
pub identifier: String,
#[serde(default)]
pub description: Option<String>,
#[serde(default)]
pub conforms_to: Vec<String>,
#[serde(default)]
pub extensions: Vec<String>,
#[serde(default)]
pub mime_types: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct IosEntitlements {
#[serde(default, rename = "app-groups")]
pub app_groups: Vec<String>,
#[serde(default, rename = "aps-environment")]
pub aps_environment: Option<String>,
#[serde(default, rename = "associated-domains")]
pub associated_domains: Vec<String>,
#[serde(default)]
pub icloud: bool,
#[serde(default, rename = "keychain-access-groups")]
pub keychain_access_groups: Vec<String>,
#[serde(default, rename = "apple-pay")]
pub apple_pay: bool,
#[serde(default)]
pub healthkit: bool,
#[serde(default)]
pub homekit: bool,
#[serde(flatten)]
pub additional: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct IosRawConfig {
#[serde(default)]
pub info_plist: Option<String>,
#[serde(default)]
pub entitlements: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct AndroidConfig {
#[serde(default)]
pub identifier: Option<String>,
#[serde(default)]
pub publisher: Option<String>,
#[serde(default)]
pub icon: Option<Vec<String>>,
#[serde(default)]
pub resources: Option<Vec<String>>,
#[serde(default)]
pub copyright: Option<String>,
#[serde(default)]
pub category: Option<String>,
#[serde(default)]
pub short_description: Option<String>,
#[serde(default)]
pub long_description: Option<String>,
#[serde(default)]
pub signing: Option<AndroidSigningConfig>,
#[serde(default)]
pub min_sdk: Option<u32>,
#[serde(default)]
pub target_sdk: Option<u32>,
#[serde(default)]
pub compile_sdk: Option<u32>,
#[serde(default)]
pub lib_name: Option<String>,
#[serde(default)]
pub features: Vec<String>,
#[serde(default)]
pub manifest: Option<PathBuf>,
#[serde(default)]
pub gradle_dependencies: Vec<String>,
#[serde(default)]
pub gradle_plugins: Vec<String>,
#[serde(default)]
pub proguard_rules: Vec<PathBuf>,
#[serde(default)]
pub permissions: HashMap<String, RawPermission>,
#[serde(default)]
pub raw: AndroidRawConfig,
#[serde(default)]
pub application: AndroidApplicationConfig,
#[serde(default)]
pub url_schemes: Vec<String>,
#[serde(default)]
pub intent_filters: Vec<AndroidIntentFilter>,
#[serde(default)]
pub foreground_service_types: Vec<String>,
#[serde(default)]
pub queries: AndroidQueries,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AndroidSigningConfig {
pub jks_file: PathBuf,
pub jks_password: String,
pub key_alias: String,
pub key_password: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AndroidIntentFilter {
#[serde(default)]
pub actions: Vec<String>,
#[serde(default)]
pub categories: Vec<String>,
#[serde(default)]
pub data: Vec<AndroidIntentData>,
#[serde(default)]
pub auto_verify: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct AndroidIntentData {
#[serde(default)]
pub scheme: Option<String>,
#[serde(default)]
pub host: Option<String>,
#[serde(default)]
pub port: Option<String>,
#[serde(default)]
pub path: Option<String>,
#[serde(default)]
pub path_prefix: Option<String>,
#[serde(default)]
pub path_pattern: Option<String>,
#[serde(default)]
pub mime_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct AndroidQueries {
#[serde(default)]
pub packages: Vec<String>,
#[serde(default)]
pub intents: Vec<AndroidQueryIntent>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct AndroidQueryIntent {
pub action: String,
#[serde(default)]
pub scheme: Option<String>,
#[serde(default)]
pub mime_type: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct AndroidRawConfig {
#[serde(default)]
pub manifest: Option<String>,
#[serde(default)]
pub application_attrs: Option<String>,
#[serde(default)]
pub application: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct AndroidApplicationConfig {
#[serde(default)]
pub uses_cleartext_traffic: Option<bool>,
#[serde(default)]
pub theme: Option<String>,
#[serde(default)]
pub supports_rtl: Option<bool>,
#[serde(default)]
pub large_heap: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct MacosConfig {
#[serde(default)]
pub identifier: Option<String>,
#[serde(default)]
pub publisher: Option<String>,
#[serde(default)]
pub icon: Option<Vec<String>>,
#[serde(default)]
pub resources: Option<Vec<String>>,
#[serde(default)]
pub copyright: Option<String>,
#[serde(default)]
pub short_description: Option<String>,
#[serde(default)]
pub long_description: Option<String>,
#[serde(default)]
pub bundle_version: Option<String>,
#[serde(default)]
pub bundle_name: Option<String>,
#[serde(default)]
pub signing_identity: Option<String>,
#[serde(default)]
pub provider_short_name: Option<String>,
#[serde(default)]
pub entitlements_file: Option<String>,
#[serde(default)]
pub exception_domain: Option<String>,
#[serde(default)]
pub license: Option<String>,
#[serde(default)]
pub hardened_runtime: Option<bool>,
#[serde(default)]
pub files: HashMap<PathBuf, PathBuf>,
#[serde(default)]
pub minimum_system_version: Option<String>,
#[serde(default)]
pub info_plist: Option<PathBuf>,
#[serde(default)]
pub frameworks: Vec<String>,
#[serde(default)]
pub entitlements: MacosEntitlements,
#[serde(default)]
pub plist: HashMap<String, serde_json::Value>,
#[serde(default)]
pub raw: MacosRawConfig,
#[serde(default)]
pub url_schemes: Vec<String>,
#[serde(default)]
pub document_types: Vec<IosDocumentType>,
#[serde(default)]
pub exported_type_identifiers: Vec<IosTypeIdentifier>,
#[serde(default)]
pub imported_type_identifiers: Vec<IosTypeIdentifier>,
#[serde(default)]
pub category: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct MacosEntitlements {
#[serde(default, rename = "app-sandbox")]
pub app_sandbox: Option<bool>,
#[serde(default, rename = "files-user-selected")]
pub files_user_selected: Option<bool>,
#[serde(default, rename = "files-user-selected-readonly")]
pub files_user_selected_readonly: Option<bool>,
#[serde(default, rename = "network-client")]
pub network_client: Option<bool>,
#[serde(default, rename = "network-server")]
pub network_server: Option<bool>,
#[serde(default)]
pub camera: Option<bool>,
#[serde(default)]
pub microphone: Option<bool>,
#[serde(default)]
pub usb: Option<bool>,
#[serde(default)]
pub bluetooth: Option<bool>,
#[serde(default)]
pub print: Option<bool>,
#[serde(default)]
pub location: Option<bool>,
#[serde(default)]
pub addressbook: Option<bool>,
#[serde(default)]
pub calendars: Option<bool>,
#[serde(default, rename = "disable-library-validation")]
pub disable_library_validation: Option<bool>,
#[serde(default, rename = "allow-jit")]
pub allow_jit: Option<bool>,
#[serde(default, rename = "allow-unsigned-executable-memory")]
pub allow_unsigned_executable_memory: Option<bool>,
#[serde(flatten)]
pub additional: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct MacosRawConfig {
#[serde(default)]
pub info_plist: Option<String>,
#[serde(default)]
pub entitlements: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct WindowsConfig {
#[serde(default)]
pub identifier: Option<String>,
#[serde(default)]
pub publisher: Option<String>,
#[serde(default)]
pub icon: Option<Vec<String>>,
#[serde(default)]
pub resources: Option<Vec<String>>,
#[serde(default)]
pub copyright: Option<String>,
#[serde(default)]
pub category: Option<String>,
#[serde(default)]
pub short_description: Option<String>,
#[serde(default)]
pub long_description: Option<String>,
#[serde(default)]
pub digest_algorithm: Option<String>,
#[serde(default)]
pub certificate_thumbprint: Option<String>,
#[serde(default)]
pub timestamp_url: Option<String>,
#[serde(default)]
pub tsp: Option<bool>,
#[serde(default)]
pub wix: Option<WindowsWixSettings>,
#[serde(default)]
pub nsis: Option<WindowsNsisSettings>,
#[serde(default)]
pub icon_path: Option<PathBuf>,
#[serde(default)]
pub webview_install_mode: Option<WindowsWebviewInstallMode>,
#[serde(default)]
pub allow_downgrades: Option<bool>,
#[serde(default)]
pub sign_command: Option<WindowsSignCommand>,
#[serde(default)]
pub capabilities: Vec<String>,
#[serde(default)]
pub restricted_capabilities: Vec<String>,
#[serde(default)]
pub device_capabilities: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct WindowsWixSettings {
#[serde(default)]
pub language: Vec<(String, Option<PathBuf>)>,
#[serde(default)]
pub template: Option<PathBuf>,
#[serde(default)]
pub fragment_paths: Vec<PathBuf>,
#[serde(default)]
pub component_group_refs: Vec<String>,
#[serde(default)]
pub component_refs: Vec<String>,
#[serde(default)]
pub feature_group_refs: Vec<String>,
#[serde(default)]
pub feature_refs: Vec<String>,
#[serde(default)]
pub merge_refs: Vec<String>,
#[serde(default)]
pub skip_webview_install: Option<bool>,
#[serde(default)]
pub license: Option<PathBuf>,
#[serde(default)]
pub enable_elevated_update_task: Option<bool>,
#[serde(default)]
pub banner_path: Option<PathBuf>,
#[serde(default)]
pub dialog_image_path: Option<PathBuf>,
#[serde(default)]
pub fips_compliant: Option<bool>,
#[serde(default)]
pub version: Option<String>,
#[serde(default)]
#[schemars(with = "Option<String>")]
pub upgrade_code: Option<uuid::Uuid>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct WindowsNsisSettings {
#[serde(default)]
pub template: Option<PathBuf>,
#[serde(default)]
pub license: Option<PathBuf>,
#[serde(default)]
pub header_image: Option<PathBuf>,
#[serde(default)]
pub sidebar_image: Option<PathBuf>,
#[serde(default)]
pub installer_icon: Option<PathBuf>,
#[serde(default)]
pub install_mode: Option<String>,
#[serde(default)]
pub languages: Option<Vec<String>>,
#[serde(default)]
pub custom_language_files: Option<HashMap<String, PathBuf>>,
#[serde(default)]
pub display_language_selector: Option<bool>,
#[serde(default)]
pub start_menu_folder: Option<String>,
#[serde(default)]
pub installer_hooks: Option<PathBuf>,
#[serde(default)]
pub minimum_webview2_version: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type")]
pub enum WindowsWebviewInstallMode {
Skip,
DownloadBootstrapper {
#[serde(default)]
silent: bool,
},
EmbedBootstrapper {
#[serde(default)]
silent: bool,
},
OfflineInstaller {
#[serde(default)]
silent: bool,
},
FixedRuntime { path: PathBuf },
}
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
pub struct WindowsSignCommand {
pub cmd: String,
pub args: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct LinuxConfig {
#[serde(default)]
pub identifier: Option<String>,
#[serde(default)]
pub publisher: Option<String>,
#[serde(default)]
pub icon: Option<Vec<String>>,
#[serde(default)]
pub resources: Option<Vec<String>>,
#[serde(default)]
pub copyright: Option<String>,
#[serde(default)]
pub category: Option<String>,
#[serde(default)]
pub short_description: Option<String>,
#[serde(default)]
pub long_description: Option<String>,
#[serde(default)]
pub deb: Option<LinuxDebSettings>,
#[serde(default)]
pub flatpak_permissions: Vec<String>,
#[serde(default)]
pub dbus_access: Vec<String>,
#[serde(default)]
pub categories: Vec<String>,
#[serde(default)]
pub keywords: Vec<String>,
#[serde(default)]
pub mime_types: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, JsonSchema)]
pub struct LinuxDebSettings {
#[serde(default)]
pub depends: Option<Vec<String>>,
#[serde(default)]
pub recommends: Option<Vec<String>>,
#[serde(default)]
pub provides: Option<Vec<String>>,
#[serde(default)]
pub conflicts: Option<Vec<String>>,
#[serde(default)]
pub replaces: Option<Vec<String>>,
#[serde(default)]
pub files: HashMap<PathBuf, PathBuf>,
#[serde(default)]
pub desktop_template: Option<PathBuf>,
#[serde(default)]
pub section: Option<String>,
#[serde(default)]
pub priority: Option<String>,
#[serde(default)]
pub changelog: Option<PathBuf>,
#[serde(default)]
pub pre_install_script: Option<PathBuf>,
#[serde(default)]
pub post_install_script: Option<PathBuf>,
#[serde(default)]
pub pre_remove_script: Option<PathBuf>,
#[serde(default)]
pub post_remove_script: Option<PathBuf>,
}
pub fn generate_manifest_schema() -> schemars::schema::RootSchema {
let mut schema = schemars::schema_for!(super::DioxusConfig);
simplify_schema(&mut schema.schema);
for def in schema.definitions.values_mut() {
if let schemars::schema::Schema::Object(obj) = def {
simplify_schema(obj);
}
}
schema
}
fn simplify_schema(schema: &mut schemars::schema::SchemaObject) {
schema.metadata().default = None;
let mut ref_to_promote = None;
if let Some(subschemas) = &schema.subschemas {
if let Some(all_of) = &subschemas.all_of {
if all_of.len() == 1 {
if let schemars::schema::Schema::Object(inner) = &all_of[0] {
if inner.reference.is_some()
&& inner.instance_type.is_none()
&& inner.object.is_none()
&& inner.array.is_none()
&& inner.subschemas.is_none()
{
ref_to_promote = inner.reference.clone();
}
}
}
}
}
if let Some(r) = ref_to_promote {
schema.subschemas = None;
schema.reference = Some(r);
}
if let Some(subschemas) = &mut schema.subschemas {
if let Some(all_of) = &mut subschemas.all_of {
for s in all_of {
if let schemars::schema::Schema::Object(obj) = s {
simplify_schema(obj);
}
}
}
if let Some(any_of) = &mut subschemas.any_of {
for s in any_of {
if let schemars::schema::Schema::Object(obj) = s {
simplify_schema(obj);
}
}
}
if let Some(one_of) = &mut subschemas.one_of {
for s in one_of {
if let schemars::schema::Schema::Object(obj) = s {
simplify_schema(obj);
}
}
}
}
if let Some(object) = &mut schema.object {
for prop in object.properties.values_mut() {
if let schemars::schema::Schema::Object(obj) = prop {
simplify_schema(obj);
}
}
if let Some(additional) = &mut object.additional_properties {
if let schemars::schema::Schema::Object(obj) = additional.as_mut() {
simplify_schema(obj);
}
}
}
if let Some(array) = &mut schema.array {
if let Some(items) = &mut array.items {
match items {
schemars::schema::SingleOrVec::Single(s) => {
if let schemars::schema::Schema::Object(obj) = s.as_mut() {
simplify_schema(obj);
}
}
schemars::schema::SingleOrVec::Vec(v) => {
for s in v {
if let schemars::schema::Schema::Object(obj) = s {
simplify_schema(obj);
}
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_permissions() {
let toml = r#"
[permissions]
location = { precision = "fine", description = "Track your runs" }
camera = { description = "Take photos" }
"#;
#[derive(Deserialize)]
struct Config {
permissions: PermissionsConfig,
}
let config: Config = toml::from_str(toml).unwrap();
let loc = config.permissions.location.unwrap();
assert_eq!(loc.precision, LocationPrecision::Fine);
assert_eq!(loc.description, "Track your runs");
assert!(config.permissions.camera.is_some());
}
#[test]
fn test_parse_ios_config() {
let toml = r#"
[ios]
deployment_target = "15.0"
[ios.entitlements]
app-groups = ["group.com.example.app"]
"#;
#[derive(Deserialize)]
struct Config {
ios: IosConfig,
}
let config: Config = toml::from_str(toml).unwrap();
assert_eq!(config.ios.deployment_target, Some("15.0".to_string()));
assert_eq!(
config.ios.entitlements.app_groups,
vec!["group.com.example.app"]
);
}
#[test]
fn test_parse_android_config() {
let toml = r#"
[android]
min_sdk = 24
target_sdk = 34
[android.permissions]
"android.permission.FOREGROUND_SERVICE" = { description = "Background" }
"#;
#[derive(Deserialize)]
struct Config {
android: AndroidConfig,
}
let config: Config = toml::from_str(toml).unwrap();
assert_eq!(config.android.min_sdk, Some(24));
assert!(config
.android
.permissions
.contains_key("android.permission.FOREGROUND_SERVICE"));
}
#[test]
fn test_parse_deep_links() {
let toml = r#"
[deep_links]
schemes = ["myapp", "com.example.app"]
hosts = ["example.com", "*.example.com"]
paths = ["/app/*", "/share/*"]
"#;
#[derive(Deserialize)]
struct Config {
deep_links: DeepLinkConfig,
}
let config: Config = toml::from_str(toml).unwrap();
assert_eq!(config.deep_links.schemes, vec!["myapp", "com.example.app"]);
assert_eq!(
config.deep_links.hosts,
vec!["example.com", "*.example.com"]
);
assert_eq!(config.deep_links.paths, vec!["/app/*", "/share/*"]);
}
#[test]
fn test_parse_background_modes() {
let toml = r#"
[background]
location = true
audio = true
fetch = true
remote-notifications = true
"#;
#[derive(Deserialize)]
struct Config {
background: BackgroundConfig,
}
let config: Config = toml::from_str(toml).unwrap();
assert!(config.background.location);
assert!(config.background.audio);
assert!(config.background.fetch);
assert!(config.background.remote_notifications);
assert!(!config.background.voip);
}
#[test]
fn test_parse_ios_url_schemes_and_background() {
let toml = r#"
[ios]
deployment_target = "15.0"
url_schemes = ["myapp-ios"]
background_modes = ["location", "fetch", "remote-notification"]
[[ios.document_types]]
name = "My Document"
extensions = ["mydoc"]
role = "Editor"
"#;
#[derive(Deserialize)]
struct Config {
ios: IosConfig,
}
let config: Config = toml::from_str(toml).unwrap();
assert_eq!(config.ios.url_schemes, vec!["myapp-ios"]);
assert_eq!(
config.ios.background_modes,
vec!["location", "fetch", "remote-notification"]
);
assert_eq!(config.ios.document_types.len(), 1);
assert_eq!(config.ios.document_types[0].name, "My Document");
}
#[test]
fn test_parse_android_intent_filters() {
let toml = r#"
[android]
min_sdk = 24
url_schemes = ["myapp-android"]
foreground_service_types = ["location", "mediaPlayback"]
[[android.intent_filters]]
actions = ["android.intent.action.VIEW"]
categories = ["android.intent.category.DEFAULT", "android.intent.category.BROWSABLE"]
auto_verify = true
[[android.intent_filters.data]]
scheme = "https"
host = "example.com"
path_prefix = "/app"
"#;
#[derive(Deserialize)]
struct Config {
android: AndroidConfig,
}
let config: Config = toml::from_str(toml).unwrap();
assert_eq!(config.android.url_schemes, vec!["myapp-android"]);
assert_eq!(
config.android.foreground_service_types,
vec!["location", "mediaPlayback"]
);
assert_eq!(config.android.intent_filters.len(), 1);
assert!(config.android.intent_filters[0].auto_verify);
}
#[test]
fn test_parse_macos_url_schemes() {
let toml = r#"
[macos]
minimum_system_version = "11.0"
url_schemes = ["myapp-macos"]
category = "public.app-category.productivity"
[[macos.document_types]]
name = "My Format"
extensions = ["myfmt"]
"#;
#[derive(Deserialize)]
struct Config {
macos: MacosConfig,
}
let config: Config = toml::from_str(toml).unwrap();
assert_eq!(config.macos.url_schemes, vec!["myapp-macos"]);
assert_eq!(
config.macos.category,
Some("public.app-category.productivity".to_string())
);
assert_eq!(config.macos.document_types.len(), 1);
}
#[test]
fn test_generate_schema() {
let schema = generate_manifest_schema();
let json = serde_json::to_string_pretty(&schema).unwrap();
assert!(json.contains("ApplicationConfig"));
assert!(json.contains("WebConfig"));
assert!(json.contains("BundleConfig"));
assert!(json.contains("ComponentConfig"));
assert!(json.contains("PermissionsConfig"));
assert!(json.contains("DeepLinkConfig"));
assert!(json.contains("BackgroundConfig"));
assert!(json.contains("IosConfig"));
assert!(json.contains("AndroidConfig"));
assert!(json.contains("MacosConfig"));
assert!(json.contains("WindowsConfig"));
assert!(json.contains("LinuxConfig"));
assert!(json.contains("location"));
assert!(json.contains("camera"));
assert!(json.contains("deployment_target"));
assert!(json.contains("min_sdk"));
assert!(json.contains("asset_dir"));
assert!(json.contains("public_dir"));
assert!(json.contains("pre_compress"));
assert!(json.contains("wasm_opt"));
assert!(json.contains("identifier"));
assert!(json.contains("publisher"));
}
}