diff --git a/.changes/config.json b/.changes/config.json
index 81a6f4ab..844eaa99 100644
--- a/.changes/config.json
+++ b/.changes/config.json
@@ -68,6 +68,7 @@
"os",
"process",
"shell",
+ "store",
"updater"
]
},
@@ -90,6 +91,7 @@
"os-js",
"process-js",
"shell-js",
+ "store-js",
"updater-js"
],
"postversion": "pnpm install --no-frozen-lockfile"
diff --git a/.changes/store-remove-mobile-plugin.md b/.changes/store-remove-mobile-plugin.md
new file mode 100644
index 00000000..64baadec
--- /dev/null
+++ b/.changes/store-remove-mobile-plugin.md
@@ -0,0 +1,5 @@
+---
+"store": patch:breaking
+---
+
+Implement mobile support in Rust directly. This changes the store directories, invalidating all previously generated stores.
diff --git a/Cargo.lock b/Cargo.lock
index 20472251..99c7da52 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -227,6 +227,7 @@ dependencies = [
"tauri-plugin-os",
"tauri-plugin-process",
"tauri-plugin-shell",
+ "tauri-plugin-store",
"tauri-plugin-updater",
"tiny_http",
"window-shadows",
diff --git a/examples/api/package.json b/examples/api/package.json
index 8d28c501..51083ba2 100644
--- a/examples/api/package.json
+++ b/examples/api/package.json
@@ -23,6 +23,7 @@
"@tauri-apps/plugin-os": "2.0.0-rc.0",
"@tauri-apps/plugin-process": "2.0.0-rc.0",
"@tauri-apps/plugin-shell": "2.0.0-rc.0",
+ "@tauri-apps/plugin-store": "2.0.0-rc.0",
"@tauri-apps/plugin-updater": "2.0.0-rc.0",
"@zerodevx/svelte-json-view": "1.0.9"
},
diff --git a/examples/api/src-tauri/Cargo.toml b/examples/api/src-tauri/Cargo.toml
index 5ee22765..8a9eaa98 100644
--- a/examples/api/src-tauri/Cargo.toml
+++ b/examples/api/src-tauri/Cargo.toml
@@ -34,6 +34,7 @@ tauri-plugin-notification = { path = "../../../plugins/notification", version =
tauri-plugin-os = { path = "../../../plugins/os", version = "2.0.0-rc.0" }
tauri-plugin-process = { path = "../../../plugins/process", version = "2.0.0-rc.0" }
tauri-plugin-shell = { path = "../../../plugins/shell", version = "2.0.0-rc.2" }
+tauri-plugin-store = { path = "../../../plugins/store", version = "2.0.0-rc.2" }
[dependencies.tauri]
workspace = true
diff --git a/examples/api/src-tauri/capabilities/base.json b/examples/api/src-tauri/capabilities/base.json
index 1a030079..b76e898c 100644
--- a/examples/api/src-tauri/capabilities/base.json
+++ b/examples/api/src-tauri/capabilities/base.json
@@ -78,6 +78,11 @@
}
],
"deny": ["$APPDATA/db/*.stronghold"]
- }
+ },
+ "store:allow-entries",
+ "store:allow-get",
+ "store:allow-set",
+ "store:allow-save",
+ "store:allow-load"
]
}
diff --git a/examples/api/src-tauri/gen/schemas/desktop-schema.json b/examples/api/src-tauri/gen/schemas/desktop-schema.json
index fd3e2e34..ae911020 100644
--- a/examples/api/src-tauri/gen/schemas/desktop-schema.json
+++ b/examples/api/src-tauri/gen/schemas/desktop-schema.json
@@ -7265,6 +7265,181 @@
"shell:deny-stdin-write"
]
},
+ {
+ "description": "store:default -> This permission set configures what kind of\noperations are available from the store plugin.\n\n#### Granted Permissions\n\nAll operations are enabled by default.\n\n",
+ "type": "string",
+ "enum": [
+ "store:default"
+ ]
+ },
+ {
+ "description": "store:allow-clear -> Enables the clear command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-clear"
+ ]
+ },
+ {
+ "description": "store:allow-delete -> Enables the delete command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-delete"
+ ]
+ },
+ {
+ "description": "store:allow-entries -> Enables the entries command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-entries"
+ ]
+ },
+ {
+ "description": "store:allow-get -> Enables the get command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-get"
+ ]
+ },
+ {
+ "description": "store:allow-has -> Enables the has command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-has"
+ ]
+ },
+ {
+ "description": "store:allow-keys -> Enables the keys command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-keys"
+ ]
+ },
+ {
+ "description": "store:allow-length -> Enables the length command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-length"
+ ]
+ },
+ {
+ "description": "store:allow-load -> Enables the load command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-load"
+ ]
+ },
+ {
+ "description": "store:allow-reset -> Enables the reset command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-reset"
+ ]
+ },
+ {
+ "description": "store:allow-save -> Enables the save command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-save"
+ ]
+ },
+ {
+ "description": "store:allow-set -> Enables the set command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-set"
+ ]
+ },
+ {
+ "description": "store:allow-values -> Enables the values command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-values"
+ ]
+ },
+ {
+ "description": "store:deny-clear -> Denies the clear command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-clear"
+ ]
+ },
+ {
+ "description": "store:deny-delete -> Denies the delete command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-delete"
+ ]
+ },
+ {
+ "description": "store:deny-entries -> Denies the entries command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-entries"
+ ]
+ },
+ {
+ "description": "store:deny-get -> Denies the get command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-get"
+ ]
+ },
+ {
+ "description": "store:deny-has -> Denies the has command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-has"
+ ]
+ },
+ {
+ "description": "store:deny-keys -> Denies the keys command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-keys"
+ ]
+ },
+ {
+ "description": "store:deny-length -> Denies the length command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-length"
+ ]
+ },
+ {
+ "description": "store:deny-load -> Denies the load command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-load"
+ ]
+ },
+ {
+ "description": "store:deny-reset -> Denies the reset command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-reset"
+ ]
+ },
+ {
+ "description": "store:deny-save -> Denies the save command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-save"
+ ]
+ },
+ {
+ "description": "store:deny-set -> Denies the set command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-set"
+ ]
+ },
+ {
+ "description": "store:deny-values -> Denies the values command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-values"
+ ]
+ },
{
"description": "updater:default -> This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n",
"type": "string",
diff --git a/examples/api/src-tauri/gen/schemas/mobile-schema.json b/examples/api/src-tauri/gen/schemas/mobile-schema.json
index ba399e4d..1cce6dd1 100644
--- a/examples/api/src-tauri/gen/schemas/mobile-schema.json
+++ b/examples/api/src-tauri/gen/schemas/mobile-schema.json
@@ -7341,6 +7341,181 @@
"enum": [
"shell:deny-stdin-write"
]
+ },
+ {
+ "description": "store:default -> This permission set configures what kind of\noperations are available from the store plugin.\n\n#### Granted Permissions\n\nAll operations are enabled by default.\n\n",
+ "type": "string",
+ "enum": [
+ "store:default"
+ ]
+ },
+ {
+ "description": "store:allow-clear -> Enables the clear command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-clear"
+ ]
+ },
+ {
+ "description": "store:allow-delete -> Enables the delete command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-delete"
+ ]
+ },
+ {
+ "description": "store:allow-entries -> Enables the entries command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-entries"
+ ]
+ },
+ {
+ "description": "store:allow-get -> Enables the get command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-get"
+ ]
+ },
+ {
+ "description": "store:allow-has -> Enables the has command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-has"
+ ]
+ },
+ {
+ "description": "store:allow-keys -> Enables the keys command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-keys"
+ ]
+ },
+ {
+ "description": "store:allow-length -> Enables the length command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-length"
+ ]
+ },
+ {
+ "description": "store:allow-load -> Enables the load command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-load"
+ ]
+ },
+ {
+ "description": "store:allow-reset -> Enables the reset command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-reset"
+ ]
+ },
+ {
+ "description": "store:allow-save -> Enables the save command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-save"
+ ]
+ },
+ {
+ "description": "store:allow-set -> Enables the set command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-set"
+ ]
+ },
+ {
+ "description": "store:allow-values -> Enables the values command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:allow-values"
+ ]
+ },
+ {
+ "description": "store:deny-clear -> Denies the clear command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-clear"
+ ]
+ },
+ {
+ "description": "store:deny-delete -> Denies the delete command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-delete"
+ ]
+ },
+ {
+ "description": "store:deny-entries -> Denies the entries command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-entries"
+ ]
+ },
+ {
+ "description": "store:deny-get -> Denies the get command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-get"
+ ]
+ },
+ {
+ "description": "store:deny-has -> Denies the has command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-has"
+ ]
+ },
+ {
+ "description": "store:deny-keys -> Denies the keys command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-keys"
+ ]
+ },
+ {
+ "description": "store:deny-length -> Denies the length command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-length"
+ ]
+ },
+ {
+ "description": "store:deny-load -> Denies the load command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-load"
+ ]
+ },
+ {
+ "description": "store:deny-reset -> Denies the reset command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-reset"
+ ]
+ },
+ {
+ "description": "store:deny-save -> Denies the save command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-save"
+ ]
+ },
+ {
+ "description": "store:deny-set -> Denies the set command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-set"
+ ]
+ },
+ {
+ "description": "store:deny-values -> Denies the values command without any pre-configured scope.",
+ "type": "string",
+ "enum": [
+ "store:deny-values"
+ ]
}
]
},
diff --git a/examples/api/src-tauri/src/lib.rs b/examples/api/src-tauri/src/lib.rs
index 65c6ab68..429054eb 100644
--- a/examples/api/src-tauri/src/lib.rs
+++ b/examples/api/src-tauri/src/lib.rs
@@ -37,6 +37,7 @@ pub fn run() {
.plugin(tauri_plugin_os::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_shell::init())
+ .plugin(tauri_plugin_store::Builder::default().build())
.setup(move |app| {
#[cfg(desktop)]
{
diff --git a/examples/api/src/App.svelte b/examples/api/src/App.svelte
index 8ffcb104..a4b6334d 100644
--- a/examples/api/src/App.svelte
+++ b/examples/api/src/App.svelte
@@ -14,6 +14,7 @@
import Notifications from "./views/Notifications.svelte";
import Shortcuts from "./views/Shortcuts.svelte";
import Shell from "./views/Shell.svelte";
+ import Store from "./views/Store.svelte";
import Updater from "./views/Updater.svelte";
import Clipboard from "./views/Clipboard.svelte";
import WebRTC from "./views/WebRTC.svelte";
@@ -90,6 +91,11 @@
component: Shell,
icon: "i-codicon-terminal-bash",
},
+ {
+ label: "Store",
+ component: Store,
+ icon: "i-codicon-file-code",
+ },
!isMobile && {
label: "Updater",
component: Updater,
diff --git a/examples/api/src/views/Shell.svelte b/examples/api/src/views/Shell.svelte
index 5f5e2d25..faaa8c4c 100644
--- a/examples/api/src/views/Shell.svelte
+++ b/examples/api/src/views/Shell.svelte
@@ -59,7 +59,7 @@
}
function writeToStdin() {
- child.write(stdin).catch(onMessage);
+ child.write(`${stdin}\n`).catch(onMessage);
}
diff --git a/examples/api/src/views/Store.svelte b/examples/api/src/views/Store.svelte
new file mode 100644
index 00000000..d8e6653b
--- /dev/null
+++ b/examples/api/src/views/Store.svelte
@@ -0,0 +1,55 @@
+
+
+
+
+
+ Key:
+
+
+
+
+ Value:
+
+
+
+
+
+
+
+ {#each Object.entries(cache) as [k, v]}
+
{k} = {v}
+ {/each}
+
+
diff --git a/plugins/store/android/.gitignore b/plugins/store/android/.gitignore
deleted file mode 100644
index c0f21ec2..00000000
--- a/plugins/store/android/.gitignore
+++ /dev/null
@@ -1,2 +0,0 @@
-/build
-/.tauri
diff --git a/plugins/store/android/build.gradle.kts b/plugins/store/android/build.gradle.kts
deleted file mode 100644
index b548022b..00000000
--- a/plugins/store/android/build.gradle.kts
+++ /dev/null
@@ -1,39 +0,0 @@
-plugins {
- id("com.android.library")
- id("org.jetbrains.kotlin.android")
-}
-
-android {
- namespace = "app.tauri.store"
- compileSdk = 34
-
- defaultConfig {
- minSdk = 24
-
- testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- consumerProguardFiles("consumer-rules.pro")
- }
-
- buildTypes {
- release {
- isMinifyEnabled = false
- proguardFiles(
- getDefaultProguardFile("proguard-android-optimize.txt"),
- "proguard-rules.pro"
- )
- }
- }
- compileOptions {
- sourceCompatibility = JavaVersion.VERSION_1_8
- targetCompatibility = JavaVersion.VERSION_1_8
- }
- kotlinOptions {
- jvmTarget = "1.8"
- }
-}
-
-dependencies {
- implementation("androidx.core:core-ktx:1.9.0")
- implementation("com.fasterxml.jackson.core:jackson-databind:2.15.3")
- implementation(project(":tauri-android"))
-}
diff --git a/plugins/store/android/proguard-rules.pro b/plugins/store/android/proguard-rules.pro
deleted file mode 100644
index 481bb434..00000000
--- a/plugins/store/android/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/plugins/store/android/settings.gradle b/plugins/store/android/settings.gradle
deleted file mode 100644
index 14a752e4..00000000
--- a/plugins/store/android/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-include ':tauri-android'
-project(':tauri-android').projectDir = new File('./.tauri/tauri-api')
diff --git a/plugins/store/android/src/main/AndroidManifest.xml b/plugins/store/android/src/main/AndroidManifest.xml
deleted file mode 100644
index 9a40236b..00000000
--- a/plugins/store/android/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
diff --git a/plugins/store/android/src/main/java/StorePlugin.kt b/plugins/store/android/src/main/java/StorePlugin.kt
deleted file mode 100644
index 8389661f..00000000
--- a/plugins/store/android/src/main/java/StorePlugin.kt
+++ /dev/null
@@ -1,50 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-package app.tauri.store
-
-import android.app.Activity
-import app.tauri.annotation.Command
-import app.tauri.annotation.TauriPlugin
-import app.tauri.plugin.Invoke
-import app.tauri.plugin.Plugin
-import com.fasterxml.jackson.databind.JsonNode
-import com.fasterxml.jackson.databind.ObjectMapper
-import java.io.File
-
-@TauriPlugin
-class StorePlugin(private val activity: Activity) : Plugin(activity) {
- @Command
- fun load(invoke: Invoke) {
- try {
- val path = invoke.parseArgs(String::class.java)
- val file = File(activity.applicationContext.getExternalFilesDir(null), path)
-
- invoke.resolveObject(ObjectMapper().readTree(file))
- } catch (ex: Exception) {
- invoke.reject(ex.message)
- }
- }
-
- @Command
- fun save(invoke: Invoke) {
- try {
- val args = invoke.parseArgs(JsonNode::class.java)
- val path = args.get("store").asText()
- val cache = args.get("cache")
- val file = File(activity.applicationContext.getExternalFilesDir(null), path)
-
- if (!file.exists()) {
- file.parentFile?.mkdirs()
- file.createNewFile()
- }
-
- file.writeText(cache.toString())
-
- invoke.resolve()
- } catch (ex: Exception) {
- invoke.reject(ex.message)
- }
- }
-}
\ No newline at end of file
diff --git a/plugins/store/build.rs b/plugins/store/build.rs
index d9bac3bf..7b54fe42 100644
--- a/plugins/store/build.rs
+++ b/plugins/store/build.rs
@@ -10,7 +10,5 @@ const COMMANDS: &[&str] = &[
fn main() {
tauri_plugin::Builder::new(COMMANDS)
.global_api_script_path("./api-iife.js")
- .android_path("android")
- .ios_path("ios")
.build();
}
diff --git a/plugins/store/ios/Package.resolved b/plugins/store/ios/Package.resolved
deleted file mode 100644
index 5f998e0e..00000000
--- a/plugins/store/ios/Package.resolved
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "object": {
- "pins": [
- {
- "package": "SwiftRs",
- "repositoryURL": "https://github.com/Brendonovich/swift-rs",
- "state": {
- "branch": null,
- "revision": "b5ed223fcdab165bc21219c1925dc1e77e2bef5e",
- "version": "1.0.6"
- }
- }
- ]
- },
- "version": 1
-}
diff --git a/plugins/store/ios/Package.swift b/plugins/store/ios/Package.swift
deleted file mode 100644
index 51ba6bf6..00000000
--- a/plugins/store/ios/Package.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-// swift-tools-version:5.3
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-import PackageDescription
-
-let package = Package(
- name: "tauri-plugin-store",
- platforms: [
- .macOS(.v10_13),
- .iOS(.v13),
- ],
- products: [
- // Products define the executables and libraries a package produces, and make them visible to other packages.
- .library(
- name: "tauri-plugin-store",
- type: .static,
- targets: ["tauri-plugin-store"])
- ],
- dependencies: [
- .package(name: "Tauri", path: "../.tauri/tauri-api")
- ],
- targets: [
- // Targets are the basic building blocks of a package. A target can define a module or a test suite.
- // Targets can depend on other targets in this package, and on products in packages this package depends on.
- .target(
- name: "tauri-plugin-store",
- dependencies: [
- .byName(name: "Tauri")
- ],
- path: "Sources")
- ]
-)
diff --git a/plugins/store/ios/Sources/StorePlugin.swift b/plugins/store/ios/Sources/StorePlugin.swift
deleted file mode 100644
index 4f651a5e..00000000
--- a/plugins/store/ios/Sources/StorePlugin.swift
+++ /dev/null
@@ -1,217 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-import Foundation
-
-import SwiftRs
-import Tauri
-import UIKit
-import WebKit
-
-
-struct SaveStore: Codable {
- let store: String
- let cache: [String: JSON]
-}
-
-class StorePlugin: Plugin {
- @objc public func save(_ invoke: Invoke) throws {
- do {
- let args = try invoke.parseArgs(SaveStore.self)
- let store = args.store
- let cache = args.cache
- let fileURL = getUrlFromPath(path: store, createDirs: true)
-
- try JSONEncoder().encode(cache).write(to: fileURL)
- invoke.resolve()
- } catch {
- invoke.reject(error.localizedDescription)
- }
- }
-
- @objc public func load(_ invoke: Invoke) throws {
- do {
- let path = try invoke.parseArgs(String.self)
- let fileURL = getUrlFromPath(path: path, createDirs: false)
- let data = try String(contentsOf: fileURL)
- let passData = dictionary(text: data)
-
- invoke.resolve(passData)
- } catch {
- invoke.reject(error.localizedDescription)
- }
- }
-
- func dictionary(text: String) -> [String: Any?] {
- if let data = text.data(using: .utf8) {
- do {
- return try JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
- } catch {
- fatalError(error.localizedDescription)
- }
- }
-
- return [:]
- }
-
- func getUrlFromPath(path: String, createDirs: Bool) -> URL {
- do {
- var url = try FileManager.default
- .url(
- for: .applicationSupportDirectory,
- in: .userDomainMask,
- appropriateFor: nil,
- create: true
- )
- let components = path.split(separator: "/").map { element in String(element) }
-
- if components.count == 1 {
- return url.appendPath(path: path, isDirectory: false)
- }
-
- for i in 0.. 1 && createDirs {
- try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true)
- }
-
- url = url.appendPath(path: components.last!, isDirectory: false)
-
- return url
- } catch {
- fatalError(error.localizedDescription)
- }
- }
-}
-
-
-@_cdecl("init_plugin_store")
-func initPlugin() -> Plugin {
- return StorePlugin()
-}
-
-private extension URL {
- func appendPath(path: String, isDirectory: Bool) -> URL {
- if #available(iOS 16.0, *) {
- return self.appending(path: path, directoryHint: isDirectory ? .isDirectory : .notDirectory)
- } else {
- return self.appendingPathComponent(path, isDirectory: isDirectory)
- }
- }
-}
-
-public enum JSON : Codable {
- case null
- case number(NSNumber)
- case string(String)
- case array([JSON])
- case bool(Bool)
- case dictionary([String : JSON])
-
- public var value: Any? {
- switch self {
- case .null: return nil
- case .number(let number): return number
- case .string(let string): return string
- case .bool(let bool): return bool
- case .array(let array): return array.map { $0.value }
- case .dictionary(let dictionary): return dictionary.mapValues { $0.value }
- }
- }
-
- public init?(_ value: Any?) {
- guard let value = value else {
- self = .null
- return
- }
-
- if let bool = value as? Bool {
- self = .bool(bool)
- } else if let int = value as? Int {
- self = .number(NSNumber(value: int))
- } else if let double = value as? Double {
- self = .number(NSNumber(value: double))
- } else if let string = value as? String {
- self = .string(string)
- } else if let array = value as? [Any] {
- var mapped = [JSON]()
- for inner in array {
- guard let inner = JSON(inner) else {
- return nil
- }
-
- mapped.append(inner)
- }
-
- self = .array(mapped)
- } else if let dictionary = value as? [String : Any] {
- var mapped = [String : JSON]()
- for (key, inner) in dictionary {
- guard let inner = JSON(inner) else {
- return nil
- }
-
- mapped[key] = inner
- }
-
- self = .dictionary(mapped)
- } else {
- return nil
- }
- }
-
- public init(from decoder: Decoder) throws {
- let container = try decoder.singleValueContainer()
- guard !container.decodeNil() else {
- self = .null
- return
- }
-
- if let bool = try container.decodeIfMatched(Bool.self) {
- self = .bool(bool)
- } else if let int = try container.decodeIfMatched(Int.self) {
- self = .number(NSNumber(value: int))
- } else if let double = try container.decodeIfMatched(Double.self) {
- self = .number(NSNumber(value: double))
- } else if let string = try container.decodeIfMatched(String.self) {
- self = .string(string)
- } else if let array = try container.decodeIfMatched([JSON].self) {
- self = .array(array)
- } else if let dictionary = try container.decodeIfMatched([String : JSON].self) {
- self = .dictionary(dictionary)
- } else {
- throw DecodingError.typeMismatch(JSON.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Unable to decode JSON as any of the possible types."))
- }
- }
-
- public func encode(to encoder: Encoder) throws {
- var container = encoder.singleValueContainer()
-
- switch self {
- case .null: try container.encodeNil()
- case .bool(let bool): try container.encode(bool)
- case .number(let number):
- if number.objCType.pointee == 0x64 /* 'd' */ {
- try container.encode(number.doubleValue)
- } else {
- try container.encode(number.intValue)
- }
- case .string(let string): try container.encode(string)
- case .array(let array): try container.encode(array)
- case .dictionary(let dictionary): try container.encode(dictionary)
- }
- }
-}
-
-fileprivate extension SingleValueDecodingContainer {
- func decodeIfMatched(_ type: T.Type) throws -> T? {
- do {
- return try self.decode(T.self)
- } catch DecodingError.typeMismatch {
- return nil
- }
- }
-}
diff --git a/plugins/store/src/desktop.rs b/plugins/store/src/desktop.rs
deleted file mode 100644
index 3e98080e..00000000
--- a/plugins/store/src/desktop.rs
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use crate::Error;
-use crate::Runtime;
-use crate::Store;
-use std::fs::create_dir_all;
-use std::fs::read;
-use std::fs::File;
-use std::io::Write;
-use tauri::Manager;
-
-#[cfg(desktop)]
-impl Store {
- pub fn save(&self) -> Result<(), Error> {
- let app_dir = self
- .app
- .path()
- .app_data_dir()
- .expect("failed to resolve app dir");
- let store_path = app_dir.join(&self.path);
-
- create_dir_all(store_path.parent().expect("invalid store path"))?;
-
- let bytes = (self.serialize)(&self.cache).map_err(Error::Serialize)?;
- let mut f = File::create(&store_path)?;
- f.write_all(&bytes)?;
-
- Ok(())
- }
-
- /// Update the store from the on-disk state
- pub fn load(&mut self) -> Result<(), Error> {
- let app_dir = self
- .app
- .path()
- .app_data_dir()
- .expect("failed to resolve app dir");
- let store_path = app_dir.join(&self.path);
-
- let bytes = read(store_path)?;
-
- self.cache
- .extend((self.deserialize)(&bytes).map_err(Error::Deserialize)?);
-
- Ok(())
- }
-}
diff --git a/plugins/store/src/error.rs b/plugins/store/src/error.rs
index d8ce9bb5..afd43add 100644
--- a/plugins/store/src/error.rs
+++ b/plugins/store/src/error.rs
@@ -11,13 +11,6 @@ pub type Result = std::result::Result;
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Error {
- #[cfg(mobile)]
- #[error(transparent)]
- PluginInvoke(#[from] tauri::plugin::mobile::PluginInvokeError),
- /// Mobile plugin handled is not initialized, Probably [`StoreBuilder::mobile_plugin_handle`] was not called.
- #[cfg(mobile)]
- #[error("Mobile plugin handled is not initialized, Perhaps you forgot to call StoreBuilder::mobile_plugin_handle")]
- MobilePluginHandleUnInitialized,
#[error("Failed to serialize store. {0}")]
Serialize(Box),
#[error("Failed to deserialize store. {0}")]
diff --git a/plugins/store/src/lib.rs b/plugins/store/src/lib.rs
index f7c9eb0d..fa331eb5 100644
--- a/plugins/store/src/lib.rs
+++ b/plugins/store/src/lib.rs
@@ -29,18 +29,6 @@ use tauri::{
mod error;
mod store;
-#[cfg(mobile)]
-mod mobile;
-#[cfg(mobile)]
-use crate::plugin::PluginHandle;
-#[cfg(target_os = "android")]
-const PLUGIN_IDENTIFIER: &str = "app.tauri.store";
-#[cfg(target_os = "ios")]
-tauri::ios_plugin_binding!(init_plugin_store);
-
-#[cfg(desktop)]
-mod desktop;
-
#[derive(Serialize, Clone)]
struct ChangePayload<'a> {
path: &'a Path,
@@ -51,9 +39,6 @@ struct ChangePayload<'a> {
pub struct StoreCollection {
stores: Mutex>>,
frozen: bool,
-
- #[cfg(mobile)]
- mobile_plugin_handle: PluginHandle,
}
pub fn with_store) -> Result>(
@@ -73,11 +58,6 @@ pub fn with_store) -> Result>(
#[allow(unused_mut)]
let mut builder = StoreBuilder::new(path);
- #[cfg(mobile)]
- {
- builder = builder.mobile_plugin_handle(collection.mobile_plugin_handle.clone());
- }
-
let mut store = builder.build(app);
// ignore loading errors, just use the default
@@ -329,17 +309,9 @@ impl Builder {
}
}
- #[cfg(target_os = "android")]
- let handle = _api.register_android_plugin(PLUGIN_IDENTIFIER, "StorePlugin")?;
- #[cfg(target_os = "ios")]
- let handle = _api.register_ios_plugin(init_plugin_store)?;
-
app_handle.manage(StoreCollection {
stores: Mutex::new(self.stores),
frozen: self.frozen,
-
- #[cfg(mobile)]
- mobile_plugin_handle: handle,
});
Ok(())
diff --git a/plugins/store/src/mobile.rs b/plugins/store/src/mobile.rs
deleted file mode 100644
index 7d999fb4..00000000
--- a/plugins/store/src/mobile.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
-// SPDX-License-Identifier: Apache-2.0
-// SPDX-License-Identifier: MIT
-
-use tauri::Runtime;
-
-use crate::error::Result;
-use crate::Store;
-use serde_json::Value;
-use std::collections::HashMap;
-
-#[derive(Debug, serde::Serialize, serde::Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct LoadStore {
- pub cache: HashMap,
-}
-
-#[derive(Debug, serde::Serialize, serde::Deserialize)]
-#[serde(rename_all = "camelCase")]
-pub struct SaveStore {
- pub store: String,
- pub cache: HashMap,
-}
-
-#[cfg(mobile)]
-impl Store {
- pub fn save(&self) -> Result<()> {
- self.mobile_plugin_handle
- .as_ref()
- .ok_or_else(|| crate::error::Error::MobilePluginHandleUnInitialized)?
- .run_mobile_plugin(
- "save",
- SaveStore {
- store: self.path.to_string_lossy().to_string(),
- cache: self.cache.clone(),
- },
- )
- .map_err(Into::into)
- }
-
- pub fn load(&mut self) -> Result<()> {
- let result: Value = self
- .mobile_plugin_handle
- .as_ref()
- .ok_or_else(|| crate::error::Error::MobilePluginHandleUnInitialized)?
- .run_mobile_plugin("load", self.path.to_string_lossy().to_string())?;
-
- let map = serde_json::from_value::>(result)?;
- self.cache.extend(map);
-
- Ok(())
- }
-}
diff --git a/plugins/store/src/store.rs b/plugins/store/src/store.rs
index 503cae76..08184439 100644
--- a/plugins/store/src/store.rs
+++ b/plugins/store/src/store.rs
@@ -2,15 +2,15 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
-#[cfg(mobile)]
-use crate::plugin::PluginHandle;
use crate::{ChangePayload, Error};
use serde_json::Value as JsonValue;
use std::{
collections::HashMap,
+ fs::{create_dir_all, read, File},
+ io::Write,
path::{Path, PathBuf},
};
-use tauri::{AppHandle, Emitter, Runtime};
+use tauri::{AppHandle, Emitter, Manager, Runtime};
type SerializeFn =
fn(&HashMap) -> Result, Box>;
@@ -30,20 +30,15 @@ fn default_deserialize(
}
/// Builds a [`Store`]
-pub struct StoreBuilder {
+pub struct StoreBuilder {
path: PathBuf,
defaults: Option>,
cache: HashMap,
serialize: SerializeFn,
deserialize: DeserializeFn,
-
- #[cfg(mobile)]
- mobile_plugin_handle: Option>,
- #[cfg(not(mobile))]
- _marker: std::marker::PhantomData,
}
-impl StoreBuilder {
+impl StoreBuilder {
/// Creates a new [`StoreBuilder`].
///
/// # Examples
@@ -64,19 +59,9 @@ impl StoreBuilder {
cache: Default::default(),
serialize: default_serialize,
deserialize: default_deserialize,
- #[cfg(mobile)]
- mobile_plugin_handle: None,
- #[cfg(not(mobile))]
- _marker: std::marker::PhantomData,
}
}
- #[cfg(mobile)]
- pub fn mobile_plugin_handle(mut self, handle: PluginHandle) -> Self {
- self.mobile_plugin_handle = Some(handle);
- self
- }
-
/// Inserts a default key-value pair.
///
/// # Examples
@@ -164,7 +149,7 @@ impl StoreBuilder {
/// Ok(())
/// });
/// ```
- pub fn build(self, app: AppHandle) -> Store {
+ pub fn build(self, app: AppHandle) -> Store {
Store {
app,
path: self.path,
@@ -172,9 +157,6 @@ impl StoreBuilder {
cache: self.cache,
serialize: self.serialize,
deserialize: self.deserialize,
-
- #[cfg(mobile)]
- mobile_plugin_handle: self.mobile_plugin_handle,
}
}
}
@@ -187,12 +169,43 @@ pub struct Store {
pub(crate) cache: HashMap,
pub(crate) serialize: SerializeFn,
pub(crate) deserialize: DeserializeFn,
-
- #[cfg(mobile)]
- pub(crate) mobile_plugin_handle: Option>,
}
impl Store {
+ pub fn save(&self) -> Result<(), Error> {
+ let app_dir = self
+ .app
+ .path()
+ .app_data_dir()
+ .expect("failed to resolve app dir");
+ let store_path = app_dir.join(&self.path);
+
+ create_dir_all(store_path.parent().expect("invalid store path"))?;
+
+ let bytes = (self.serialize)(&self.cache).map_err(Error::Serialize)?;
+ let mut f = File::create(&store_path)?;
+ f.write_all(&bytes)?;
+
+ Ok(())
+ }
+
+ /// Update the store from the on-disk state
+ pub fn load(&mut self) -> Result<(), Error> {
+ let app_dir = self
+ .app
+ .path()
+ .app_data_dir()
+ .expect("failed to resolve app dir");
+ let store_path = app_dir.join(&self.path);
+
+ let bytes = read(store_path)?;
+
+ self.cache
+ .extend((self.deserialize)(&bytes).map_err(Error::Deserialize)?);
+
+ Ok(())
+ }
+
pub fn insert(&mut self, key: String, value: JsonValue) -> Result<(), Error> {
self.cache.insert(key.clone(), value.clone());
self.app.emit(
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7e83b058..3fde875a 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -99,6 +99,9 @@ importers:
'@tauri-apps/plugin-shell':
specifier: 2.0.0-rc.0
version: link:../../plugins/shell
+ '@tauri-apps/plugin-store':
+ specifier: 2.0.0-rc.0
+ version: link:../../plugins/store
'@tauri-apps/plugin-updater':
specifier: 2.0.0-rc.0
version: link:../../plugins/updater