diff --git a/Cargo.lock b/Cargo.lock index 8bffeadb4..d647bf5ed 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5094,7 +5094,6 @@ dependencies = [ "roxmltree", "rstest", "rstest_reuse", - "rusqlite", "rustls", "scraper", "serde", @@ -5291,6 +5290,17 @@ version = "0.1.0" dependencies = [ "uniffi", "wp_api", + "wp_mobile_cache", +] + +[[package]] +name = "wp_mobile_cache" +version = "0.1.0" +dependencies = [ + "rusqlite", + "thiserror 2.0.17", + "uniffi", + "wp_api", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index bf8a01deb..cb7e29151 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ members = [ "wp_localization_parser", "wp_localization_validation", "wp_mobile", + "wp_mobile_cache", "wp_rs_cli", "wp_rs_web", "wp_serde_helper", diff --git a/Makefile b/Makefile index 54315a515..b3e435f64 100644 --- a/Makefile +++ b/Makefile @@ -102,8 +102,8 @@ _build-apple-%-tvos _build-apple-%-tvos-sim _build-apple-%-watchos _build-apple- # Build the library for a specific target _build-apple-%: - cargo $(CARGO_OPTS) $(cargo_config_library) build --target $* --features export-uncancellable-endpoints --package wp_api --profile $(CARGO_PROFILE) --no-default-features - ./scripts/swift-bindings.sh target/$*/$(CARGO_PROFILE_DIRNAME)/libwp_api.a + cargo $(CARGO_OPTS) $(cargo_config_library) build --target $* --features export-uncancellable-endpoints --package wp_mobile --profile $(CARGO_PROFILE) --no-default-features + ./scripts/swift-bindings.sh target/$*/$(CARGO_PROFILE_DIRNAME)/libwp_mobile.a # Build the library for one single platform, including real device and simulator. build-apple-platform-macos := $(addprefix _build-apple-,$(apple-platform-targets-macos)) @@ -141,11 +141,11 @@ docker-image-web: docker build -t wordpress-rs-web -f wp_rs_web/Dockerfile . --progress=plain swift-linux-library: - cargo build --release --features export-uncancellable-endpoints --package wp_api - ./scripts/swift-bindings.sh target/release/libwp_api.a + cargo build --release --features export-uncancellable-endpoints --package wp_mobile + ./scripts/swift-bindings.sh target/release/libwp_mobile.a mkdir -p target/release/libwordpressFFI-linux cp target/release/swift-bindings/Headers/* target/release/libwordpressFFI-linux/ - cp target/release/libwp_api.a target/release/libwordpressFFI-linux/ + cp target/release/libwp_mobile.a target/release/libwordpressFFI-linux/ swift-example-app: swift-example-app-mac swift-example-app-ios @@ -163,7 +163,7 @@ test-swift-linux: docker compose run --rm swift make test-swift-linux-in-docker test-swift-linux-in-docker: swift-linux-library - swift test -Xlinker -Ltarget/release/libwordpressFFI-linux -Xlinker -lwp_api + swift test -Xlinker -Ltarget/release/libwordpressFFI-linux -Xlinker -lwp_mobile test-swift-darwin: xcframework swift test diff --git a/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/cache/kotlin/WordPressApiCache.kt b/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/cache/kotlin/WordPressApiCache.kt index 6b55bbdb1..9f1d9b3e6 100644 --- a/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/cache/kotlin/WordPressApiCache.kt +++ b/native/kotlin/api/kotlin/src/main/kotlin/rs/wordpress/cache/kotlin/WordPressApiCache.kt @@ -2,9 +2,9 @@ package rs.wordpress.cache.kotlin import kotlinx.coroutines.asCoroutineDispatcher import kotlinx.coroutines.withContext -import uniffi.wp_api.DatabaseDelegate -import uniffi.wp_api.UpdateHook -import uniffi.wp_api.WpApiCache +import uniffi.wp_mobile_cache.DatabaseDelegate +import uniffi.wp_mobile_cache.UpdateHook +import uniffi.wp_mobile_cache.WpApiCache import java.nio.file.Path import java.util.concurrent.Executors @@ -13,6 +13,7 @@ class WordPressApiCacheLoggingDelegate : DatabaseDelegate { println("Received update: $updateHook") } } + class WordPressApiCacheDelegate( private val callback: (updateHook: UpdateHook) -> Unit ) : DatabaseDelegate { @@ -31,7 +32,10 @@ class WordPressApiCache { constructor(delegate: WordPressApiCacheDelegate? = null) : this(":memory:", delegate) // Creates a new cache at the specified file system URL - constructor(path: Path, delegate: WordPressApiCacheDelegate? = null) : this(path.toString(), delegate) + constructor(path: Path, delegate: WordPressApiCacheDelegate? = null) : this( + path.toString(), + delegate + ) // Creates a new cache at the specified path constructor(string: String, delegate: WordPressApiCacheDelegate? = null) { @@ -42,6 +46,7 @@ class WordPressApiCache { suspend fun performMigrations(): Int = withContext(internalDispatcher) { cache.performMigrations().toInt() } + fun startListeningForUpdates() { if (this.delegate != null) { this.cache.startListeningForUpdates(this.delegate) diff --git a/native/kotlin/build.gradle.kts b/native/kotlin/build.gradle.kts index 956997e53..eba105968 100644 --- a/native/kotlin/build.gradle.kts +++ b/native/kotlin/build.gradle.kts @@ -33,6 +33,7 @@ allprojects { exclude("**/wp_api.kt") exclude("**/wp_localization.kt") exclude("**/wp_mobile.kt") + exclude("**/wp_mobile_cache.kt") } tasks.withType().configureEach { @@ -42,6 +43,7 @@ allprojects { exclude("**/wp_api.kt") exclude("**/wp_localization.kt") exclude("**/wp_mobile.kt") + exclude("**/wp_mobile_cache.kt") } dependencies { diff --git a/native/swift/Tests/integration-tests/CancellationTests.swift b/native/swift/Tests/integration-tests/CancellationTests.swift index 71a540e01..96420be80 100644 --- a/native/swift/Tests/integration-tests/CancellationTests.swift +++ b/native/swift/Tests/integration-tests/CancellationTests.swift @@ -19,7 +19,12 @@ struct CancellationTests { throws: WpApiError.RequestExecutionFailed(statusCode: nil, redirects: nil, reason: .cancellationError), performing: { let task = Task { - _ = try await api.posts.create(params: .init(title: title, content: content, meta: nil)) + let params = PostCreateParams( + title: title, + content: content, + meta: nil + ) + _ = try await api.posts.create(postEndpointType: .posts, params: params) Issue.record("The creating post function should throw") } diff --git a/native/swift/Tests/wordpress-api/Support/Extensions.swift b/native/swift/Tests/wordpress-api/Support/Extensions.swift index d13760ea5..3b56ca337 100644 --- a/native/swift/Tests/wordpress-api/Support/Extensions.swift +++ b/native/swift/Tests/wordpress-api/Support/Extensions.swift @@ -19,14 +19,6 @@ extension PaginatableResponse { } } -// These `Sendable` conformances are **NOT** safe – they're for the test suite only. -// -// Until or unless `WpNetworkRequest` and `WpNetworkRequest` become `uniffi::Record` (thus Structs) -// we can't guarantee that they're thread-safe - -extension WpNetworkRequest: @unchecked Sendable {} -extension WpNetworkResponse: @unchecked Sendable {} - // This is only for testing – it's not production-ready // extension WordPressLoginClientError: Equatable { // public static func == (lhs: WordPressLoginClientError, rhs: WordPressLoginClientError) -> Bool { diff --git a/wp_api/Cargo.toml b/wp_api/Cargo.toml index 60716662e..314ff5c81 100644 --- a/wp_api/Cargo.toml +++ b/wp_api/Cargo.toml @@ -4,7 +4,6 @@ version = "0.1.0" edition = "2024" [features] -default = ["rusqlite/bundled"] export-uncancellable-endpoints = ["wp_derive_request_builder/export-uncancellable-endpoints"] integration-tests = [] reqwest-request-executor = ["dep:reqwest", "dep:tokio", "dep:hyper-util", "dep:rustls", "dep:hickory-resolver", "dep:hyper", "dep:h2"] @@ -49,7 +48,6 @@ wp_localization = { path = "../wp_localization" } wp_localization_macro = { path = "../wp_localization_macro" } wp_serde_helper = { path = "../wp_serde_helper" } x509-cert = { workspace = true } -rusqlite = { version = "0.37.0", features = ["hooks"] } [dev-dependencies] rstest = { workspace = true } diff --git a/wp_api/src/cache/mod.rs b/wp_api/src/cache/mod.rs deleted file mode 100644 index dd46670a4..000000000 --- a/wp_api/src/cache/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod wp_api_cache; diff --git a/wp_api/src/lib.rs b/wp_api/src/lib.rs index 54c76cc15..76a72146b 100644 --- a/wp_api/src/lib.rs +++ b/wp_api/src/lib.rs @@ -6,7 +6,6 @@ use users::*; use wp_localization::{MessageBundle, WpMessages, WpSupportsLocalization}; use wp_localization_macro::WpDeriveLocalizable; -pub mod cache; pub mod jetpack; pub mod wp_com; diff --git a/wp_mobile/Cargo.toml b/wp_mobile/Cargo.toml index e506cfc19..be359dbd5 100644 --- a/wp_mobile/Cargo.toml +++ b/wp_mobile/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [features] reqwest-request-executor = ["wp_api/reqwest-request-executor"] +export-uncancellable-endpoints = ["wp_api/export-uncancellable-endpoints"] [lib] crate-type = ["lib", "cdylib", "staticlib"] @@ -13,6 +14,7 @@ name = "wp_mobile" [dependencies] uniffi = { workspace = true } wp_api = { path = "../wp_api" } +wp_mobile_cache = { path = "../wp_mobile_cache" } [build-dependencies] uniffi = { workspace = true , features = [ "build", "cli" ] } diff --git a/wp_mobile/src/lib.rs b/wp_mobile/src/lib.rs index c14dd3fea..ff119745b 100644 --- a/wp_mobile/src/lib.rs +++ b/wp_mobile/src/lib.rs @@ -1,5 +1,6 @@ -// Re-export wp_api to ensure its bindings are generated +// Re-export to ensure its bindings are generated pub use wp_api; +pub use wp_mobile_cache; #[uniffi::export] fn wp_mobile_crate_works(input: String) -> String { diff --git a/wp_mobile_cache/Cargo.toml b/wp_mobile_cache/Cargo.toml new file mode 100644 index 000000000..ea547712e --- /dev/null +++ b/wp_mobile_cache/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "wp_mobile_cache" +version = "0.1.0" +edition = "2024" + +[features] +default = ["rusqlite/bundled"] + +[dependencies] +rusqlite = { version = "0.37.0", features = ["hooks"] } +thiserror = { workspace = true } +uniffi = { workspace = true } +wp_api = { path = "../wp_api" } diff --git a/wp_api/src/cache/migrations/0001-create-posts-table.sql b/wp_mobile_cache/migrations/0001-create-posts-table.sql similarity index 100% rename from wp_api/src/cache/migrations/0001-create-posts-table.sql rename to wp_mobile_cache/migrations/0001-create-posts-table.sql diff --git a/wp_api/src/cache/migrations/0002-create-users-table.sql b/wp_mobile_cache/migrations/0002-create-users-table.sql similarity index 100% rename from wp_api/src/cache/migrations/0002-create-users-table.sql rename to wp_mobile_cache/migrations/0002-create-users-table.sql diff --git a/wp_api/src/cache/wp_api_cache.rs b/wp_mobile_cache/src/lib.rs similarity index 97% rename from wp_api/src/cache/wp_api_cache.rs rename to wp_mobile_cache/src/lib.rs index d2097c850..0bb2f884b 100644 --- a/wp_api/src/cache/wp_api_cache.rs +++ b/wp_mobile_cache/src/lib.rs @@ -63,8 +63,8 @@ impl WpApiCache { } static MIGRATION_QUERIES: [&str; 2] = [ - include_str!("migrations/0001-create-posts-table.sql"), - include_str!("migrations/0002-create-users-table.sql"), + include_str!("../migrations/0001-create-posts-table.sql"), + include_str!("../migrations/0002-create-users-table.sql"), ]; pub struct MigrationManager<'a> { @@ -191,6 +191,8 @@ impl DBManager { } } +uniffi::setup_scaffolding!(); + #[cfg(test)] mod tests { use super::*; diff --git a/wp_mobile_cache/uniffi.toml b/wp_mobile_cache/uniffi.toml new file mode 100644 index 000000000..b667e139f --- /dev/null +++ b/wp_mobile_cache/uniffi.toml @@ -0,0 +1,13 @@ +[bindings.kotlin] +android = false +android_cleaner = false +cdylib_name = "wp_mobile" +generate_immutable_records = true + +[bindings.swift] +ffi_module_name = "libwordpressFFI" +ffi_module_filename = "wp_mobile_cache_uniffi" +generate_module_map = false +generate_immutable_records = true +experimental_sendable_value_types = true +omit_localized_error_conformance = true diff --git a/xcframework/src/main.rs b/xcframework/src/main.rs index c5d124453..cfd00137f 100644 --- a/xcframework/src/main.rs +++ b/xcframework/src/main.rs @@ -6,7 +6,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; const XCFRAMEWORK_OUTPUT_PATH: &str = "target/libwordpressFFI.xcframework"; -const LIBRARY_FILENAME: &str = "libwordpress.a"; +const LIBRARY_FILENAME: &str = "libwp_mobile.a"; fn main() -> Result<()> { CreateXCFramework::parse().run() @@ -217,7 +217,7 @@ impl Slice { // If there are more static libraries (a.k.a cargo packages), we'll // need to bundle them together into one static library. - // At the moment, we only have one libwp_api, so we can just copy it. + // At the moment, we only have one libwp_mobile, so we can just copy it. assert!( libs.len() == 1, "Expected exactly one library for each slice" @@ -251,7 +251,7 @@ impl Slice { } fn built_libraries(&self) -> Vec { - vec![self.built_product_dir().join("libwp_api.a")] + vec![self.built_product_dir().join(LIBRARY_FILENAME)] } }