Explorar el Código

initial lockfile format and generation script

Lucas Nogueira hace 2 años
padre
commit
50aaf4ec14

+ 64 - 6
core/tauri-build/src/lib.rs

@@ -17,13 +17,16 @@ pub use anyhow::Result;
 use cargo_toml::Manifest;
 use heck::AsShoutySnakeCase;
 
+use serde::{Deserialize, Serialize};
 use tauri_utils::{
-  config::Config,
+  config::{Config, Namespace},
   resources::{external_binaries, resource_relpath, ResourcePaths},
 };
 
 use std::{
+  collections::HashMap,
   env::var_os,
+  fs::write,
   path::{Path, PathBuf},
 };
 
@@ -295,9 +298,9 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
   cfg_alias("desktop", !mobile);
   cfg_alias("mobile", mobile);
 
-  let mut config = serde_json::from_value(tauri_utils::config::parse::read_from(
-    std::env::current_dir().unwrap(),
-  )?)?;
+  let (config, config_path) =
+    tauri_utils::config::parse::read_from(std::env::current_dir().unwrap())?;
+  let mut config = serde_json::from_value(config)?;
   if let Ok(env) = std::env::var("TAURI_CONFIG") {
     let merge_config: serde_json::Value = serde_json::from_str(&env)?;
     json_patch::merge(&mut config, &merge_config);
@@ -475,12 +478,67 @@ pub fn try_build(attributes: Attributes) -> Result<()> {
     }
   }
 
-  let _manifests = plugin::manifests();
+  let manifests = plugin::manifests();
+  let mut resolution = HashMap::<String, MemberResolution>::new();
+
+  for namespace in &config.namespaces {
+    for member in &namespace.members {
+      let member_resolution =
+        resolution
+          .entry(member.clone())
+          .or_insert_with(|| MemberResolution {
+            member: member.clone(),
+            capabilities: Default::default(),
+          });
+
+      for capability in &namespace.capabilities {
+        let (plugin, capability) = manifests.find_capability(capability).unwrap_or_else(|| {
+          panic!("could not find capability specification matching id {capability}")
+        });
+        let resolved_capability = member_resolution
+          .capabilities
+          .entry(capability.id.clone())
+          .or_default();
+        resolved_capability.features.extend(capability.features);
+      }
+    }
+  }
+
+  let lockfile = NamespaceLockFile {
+    version: 1,
+    namespaces: config.namespaces,
+    plugins: manifests,
+    resolution: resolution.into_values().collect(),
+  };
+  write(
+    config_path.parent().unwrap().join("tauri.namespace.lock"),
+    serde_json::to_string_pretty(&lockfile)?,
+  )
+  .context("failed to write namespace lockfile")?;
 
   Ok(())
 }
 
-#[derive(serde::Deserialize)]
+#[derive(Serialize)]
+struct MemberResolution {
+  member: String,
+  capabilities: HashMap<String, ResolvedCapability>,
+}
+
+#[derive(Default, Serialize)]
+struct ResolvedCapability {
+  features: Vec<String>,
+}
+
+#[derive(Serialize)]
+struct NamespaceLockFile {
+  version: u8,
+  namespaces: Vec<Namespace>,
+  plugins: plugin::ManifestMap,
+  resolution: Vec<MemberResolution>,
+}
+
+#[derive(Deserialize)]
 struct CargoMetadata {
   workspace_root: PathBuf,
 }

+ 36 - 11
core/tauri-build/src/plugin.rs

@@ -8,13 +8,14 @@ use std::{
 };
 
 const PLUGIN_METADATA_KEY: &str = "PLUGIN_MANIFEST_PATH";
+const DEFAULT_CAPABILITY_ID: &str = "default";
 
 #[derive(Debug, Serialize, Deserialize)]
 pub enum ScopeType {
   String,
 }
 
-#[derive(Debug, Default, Serialize, Deserialize)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
 pub struct CapabilityScope {
   #[serde(default)]
   allowed: Vec<serde_json::Value>,
@@ -22,14 +23,15 @@ pub struct CapabilityScope {
   blocked: Vec<serde_json::Value>,
 }
 
-#[derive(Debug, Serialize, Deserialize)]
+#[derive(Debug, Clone, Default, Serialize, Deserialize)]
 pub struct Capability {
-  id: Option<String>,
-  description: String,
   #[serde(default)]
-  features: Vec<String>,
+  pub(crate) id: String,
+  pub(crate) description: String,
   #[serde(default)]
-  scope: CapabilityScope,
+  pub(crate) features: Vec<String>,
+  #[serde(default)]
+  pub(crate) scope: CapabilityScope,
 }
 
 #[derive(Debug, Serialize, Deserialize)]
@@ -56,10 +58,10 @@ impl Manifest {
     let mut capability: Capability = serde_json::from_str(default_capability.as_ref())
       .expect("failed to deserialize default capability");
     assert!(
-      capability.id.is_none(),
+      capability.id.is_empty(),
       "default capability cannot have an specific identifier"
     );
-    capability.id.replace("default".into());
+    capability.id = DEFAULT_CAPABILITY_ID.into();
     self.default_capability.replace(capability);
     self
   }
@@ -68,7 +70,7 @@ impl Manifest {
     let capability: Capability =
       serde_json::from_str(capability.as_ref()).expect("failed to deserialize default capability");
     assert!(
-      capability.id.is_some(),
+      !capability.id.is_empty(),
       "capability must have an specific identifier"
     );
     self.capabilities.push(capability);
@@ -106,7 +108,30 @@ pub fn set_manifest(manifest: Manifest) {
   println!("cargo:{PLUGIN_METADATA_KEY}={}", manifest_path.display());
 }
 
-pub(crate) fn manifests() -> HashMap<String, Manifest> {
+#[derive(Serialize)]
+pub(crate) struct ManifestMap(HashMap<String, Manifest>);
+
+impl ManifestMap {
+  pub fn find_capability(&self, id: &str) -> Option<(String, Capability)> {
+    for (plugin, manifest) in &self.0 {
+      if id == format!("{DEFAULT_CAPABILITY_ID}-{plugin}") {
+        return Some((
+          plugin.clone(),
+          manifest.default_capability.clone().unwrap_or_default(),
+        ));
+      }
+      for capability in &manifest.capabilities {
+        if capability.id == id {
+          return Some((plugin.clone(), capability.clone()));
+        }
+      }
+    }
+
+    None
+  }
+}
+
+pub(crate) fn manifests() -> ManifestMap {
   let mut manifests = HashMap::new();
 
   for (key, value) in vars_os() {
@@ -126,5 +151,5 @@ pub(crate) fn manifests() -> HashMap<String, Manifest> {
     }
   }
 
-  manifests
+  ManifestMap(manifests)
 }

+ 2 - 1
core/tauri-codegen/src/lib.rs

@@ -67,7 +67,8 @@ pub fn get_config(path: &Path) -> Result<(Config, PathBuf), CodegenConfigError>
   // it is impossible for the content of two separate configs to get mixed up. The chances are
   // already unlikely unless the developer goes out of their way to run the cli on a different
   // project than the target crate.
-  let mut config = serde_json::from_value(tauri_utils::config::parse::read_from(parent.clone())?)?;
+  let mut config =
+    serde_json::from_value(tauri_utils::config::parse::read_from(parent.clone())?.0)?;
   if let Ok(env) = std::env::var("TAURI_CONFIG") {
     let merge_config: serde_json::Value =
       serde_json::from_str(&env).map_err(CodegenConfigError::FormatInline)?;

+ 43 - 0
core/tauri-config-schema/schema.json

@@ -108,6 +108,14 @@
           "$ref": "#/definitions/PluginConfig"
         }
       ]
+    },
+    "namespaces": {
+      "description": "The namespaces defining what capabilities are enabled.",
+      "default": [],
+      "type": "array",
+      "items": {
+        "$ref": "#/definitions/Namespace"
+      }
     }
   },
   "additionalProperties": false,
@@ -2258,6 +2266,41 @@
       "description": "The plugin configs holds a HashMap mapping a plugin name to its configuration object.\n\nSee more: https://tauri.app/v1/api/config#pluginconfig",
       "type": "object",
       "additionalProperties": true
+    },
+    "Namespace": {
+      "description": "A namespace defining a set of capabilities that are enabled for a given window.",
+      "type": "object",
+      "required": [
+        "capabilities",
+        "description",
+        "id",
+        "members"
+      ],
+      "properties": {
+        "id": {
+          "description": "Identifier of this namespace. Must be unique.\n\nIt is recommended to use `drop-` or `allow-` prefixes to ensure the rule can be easily categorized.",
+          "type": "string"
+        },
+        "description": {
+          "description": "Describes the namespace in a human readable format.",
+          "type": "string"
+        },
+        "members": {
+          "description": "The windows that can use the configuration of this namespace.",
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        },
+        "capabilities": {
+          "description": "List of capabilities attached to this namespace.",
+          "type": "array",
+          "items": {
+            "type": "string"
+          }
+        }
+      },
+      "additionalProperties": false
     }
   }
 }

+ 3 - 3
core/tauri-utils/src/config/parse.rs

@@ -192,12 +192,12 @@ pub fn is_configuration_file(path: &Path) -> bool {
 /// Merging the configurations using [JSON Merge Patch (RFC 7396)].
 ///
 /// [JSON Merge Patch (RFC 7396)]: https://datatracker.ietf.org/doc/html/rfc7396.
-pub fn read_from(root_dir: PathBuf) -> Result<Value, ConfigError> {
-  let mut config: Value = parse_value(root_dir.join("tauri.conf.json"))?.0;
+pub fn read_from(root_dir: PathBuf) -> Result<(Value, PathBuf), ConfigError> {
+  let (mut config, path) = parse_value(root_dir.join("tauri.conf.json"))?;
   if let Some((platform_config, _)) = read_platform(root_dir)? {
     merge(&mut config, &platform_config);
   }
-  Ok(config)
+  Ok((config, path))
 }
 
 /// Reads the platform-specific configuration file from the given root directory if it exists.

+ 60 - 0
examples/api/src-tauri/tauri.namespace.lock

@@ -0,0 +1,60 @@
+{
+  "version": 1,
+  "namespaces": [
+    {
+      "id": "main",
+      "description": "Main window namespace",
+      "members": [
+        "main"
+      ],
+      "capabilities": [
+        "allow-ping"
+      ]
+    }
+  ],
+  "plugins": {
+    "sample": {
+      "plugin": "sample",
+      "default_capability": {
+        "id": "default",
+        "description": "Default empty capability set",
+        "features": [],
+        "scope": {
+          "allowed": [],
+          "blocked": []
+        }
+      },
+      "capabilities": [
+        {
+          "id": "allow-ping",
+          "description": "Allows the ping command",
+          "features": [
+            "ping"
+          ],
+          "scope": {
+            "allowed": [],
+            "blocked": []
+          }
+        }
+      ],
+      "features": [
+        "ping"
+      ],
+      "scope_type": [
+        "String"
+      ]
+    }
+  },
+  "resolution": [
+    {
+      "member": "main",
+      "capabilities": {
+        "allow-ping": {
+          "features": [
+            "ping"
+          ]
+        }
+      }
+    }
+  ]
+}

+ 2 - 2
tooling/cli/Cargo.lock

@@ -3907,9 +3907,9 @@ dependencies = [
 
 [[package]]
 name = "tar"
-version = "0.4.38"
+version = "0.4.39"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6"
+checksum = "ec96d2ffad078296368d46ff1cb309be1c23c513b4ab0e22a45de0185275ac96"
 dependencies = [
  "filetime",
  "libc",