mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-10-10 18:04:06 +02:00
Replace the Go backend with the Rust workspace and route Android/iOS through UniFFI bindings. Include the migrated extension runtime, network, providers, media metadata, downloads and library operations, with version 5.0.0+147. Remove Go sources, adapters, native build selection and CI dependencies. Build Rust unconditionally and lock the iOS Rust pod using a relative path. Retain legacy source and migration evidence in a local ignored archive. Validation: Android Kotlin compile and 53 native tests; Swift Rust-branch equivalence and syntax; CocoaPods install; workflow, shell, Ruby, plist and diff checks. Reuse the preceding 100 Rust tests, fmt/Clippy and five-ABI build checkpoint; no new APK or iOS application build for this cleanup. Known follow-up: URL/URLSearchParams globals are missing from the Rust JS runtime. A controlled extension replay confirms a URL-resolution regression; this commit does not fix that runtime gap.
415 lines
12 KiB
Rust
415 lines
12 KiB
Rust
use crate::cancellation::RequestLease;
|
|
use crate::extensions::ExtensionEnvironment;
|
|
use spotiflac_extensions::RuntimeLimits;
|
|
use spotiflac_extensions::backend::Backend as CoreManager;
|
|
use spotiflac_extensions::manager::ManagerError;
|
|
use std::path::Path;
|
|
use std::sync::Arc;
|
|
|
|
#[derive(Debug, thiserror::Error, uniffi::Error)]
|
|
#[uniffi(flat_error)]
|
|
pub enum ExtensionManagerError {
|
|
#[error("{0}")]
|
|
Operation(String),
|
|
}
|
|
|
|
impl From<ManagerError> for ExtensionManagerError {
|
|
fn from(error: ManagerError) -> Self {
|
|
Self::Operation(error.to_string())
|
|
}
|
|
}
|
|
|
|
/// Owns installed source, data, and JavaScript workers. Use a background native
|
|
/// thread for operations that read packages or run extension code.
|
|
#[derive(uniffi::Object)]
|
|
pub struct ExtensionManager {
|
|
pub(crate) inner: CoreManager,
|
|
}
|
|
|
|
#[uniffi::export]
|
|
impl ExtensionManager {
|
|
#[uniffi::constructor]
|
|
pub fn new(
|
|
source_directory: String,
|
|
data_directory: String,
|
|
master_key: String,
|
|
app_version: String,
|
|
timeout_ms: u64,
|
|
) -> Result<Self, ExtensionManagerError> {
|
|
Self::with_lyrics_settings(
|
|
source_directory,
|
|
data_directory,
|
|
master_key,
|
|
app_version,
|
|
timeout_ms,
|
|
"[]".into(),
|
|
"{}".into(),
|
|
)
|
|
}
|
|
|
|
#[uniffi::constructor]
|
|
pub fn with_lyrics_settings(
|
|
source_directory: String,
|
|
data_directory: String,
|
|
master_key: String,
|
|
app_version: String,
|
|
timeout_ms: u64,
|
|
providers_json: String,
|
|
options_json: String,
|
|
) -> Result<Self, ExtensionManagerError> {
|
|
let key = zeroize::Zeroizing::new(master_key);
|
|
let limits = RuntimeLimits {
|
|
timeout_ms: if timeout_ms == 0 { 30_000 } else { timeout_ms },
|
|
..RuntimeLimits::default()
|
|
};
|
|
Ok(Self {
|
|
inner: CoreManager::with_lyrics_settings(
|
|
Path::new(&source_directory),
|
|
Path::new(&data_directory),
|
|
&key,
|
|
&app_version,
|
|
limits,
|
|
&providers_json,
|
|
&options_json,
|
|
)?,
|
|
})
|
|
}
|
|
|
|
pub fn environment(&self) -> Arc<ExtensionEnvironment> {
|
|
Arc::new(ExtensionEnvironment {
|
|
inner: self.inner.environment(),
|
|
})
|
|
}
|
|
|
|
pub fn get_app_version(&self) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.get_app_version()
|
|
.map_err(ExtensionManagerError::Operation)
|
|
}
|
|
|
|
pub fn set_app_version(&self, version: String) -> Result<(), ExtensionManagerError> {
|
|
self.inner
|
|
.set_app_version(&version)
|
|
.map_err(ExtensionManagerError::Operation)
|
|
}
|
|
|
|
pub fn install(&self, package_path: String) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.install(Path::new(&package_path))
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn upgrade(&self, package_path: String) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.upgrade(Path::new(&package_path))
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn check_upgrade(&self, package_path: String) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.check_upgrade(Path::new(&package_path))
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn installed(&self) -> Result<String, ExtensionManagerError> {
|
|
self.inner.installed().map_err(Into::into)
|
|
}
|
|
|
|
pub fn load_all(&self) -> Result<String, ExtensionManagerError> {
|
|
self.inner.load_all().map_err(Into::into)
|
|
}
|
|
|
|
pub fn set_enabled(
|
|
&self,
|
|
extension_id: String,
|
|
enabled: bool,
|
|
) -> Result<(), ExtensionManagerError> {
|
|
self.inner
|
|
.set_enabled(&extension_id, enabled)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn initialize(
|
|
&self,
|
|
extension_id: String,
|
|
settings_json: String,
|
|
) -> Result<(), ExtensionManagerError> {
|
|
let settings = serde_json::from_str(&settings_json).map_err(|error| {
|
|
ExtensionManagerError::Operation(format!("invalid settings: {error}"))
|
|
})?;
|
|
self.inner
|
|
.initialize(&extension_id, settings)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn cleanup(&self, extension_id: String) -> Result<(), ExtensionManagerError> {
|
|
self.inner.cleanup(&extension_id).map_err(Into::into)
|
|
}
|
|
|
|
pub fn update_settings(
|
|
&self,
|
|
extension_id: String,
|
|
settings_json: String,
|
|
) -> Result<(), ExtensionManagerError> {
|
|
let settings = serde_json::from_str(&settings_json).map_err(|error| {
|
|
ExtensionManagerError::Operation(format!("invalid settings: {error}"))
|
|
})?;
|
|
self.inner
|
|
.update_settings(&extension_id, settings)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn download_by_strategy(
|
|
&self,
|
|
request_json: String,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.download_by_strategy(&request_json, &|| Ok(()))
|
|
.map_err(ExtensionManagerError::Operation)
|
|
}
|
|
|
|
pub fn download_with_extensions_json(
|
|
&self,
|
|
request_json: String,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.download_with_extensions_json(&request_json, &|| Ok(()))
|
|
.map_err(ExtensionManagerError::Operation)
|
|
}
|
|
|
|
pub fn check_extension_health_json(
|
|
&self,
|
|
extension_id: String,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.check_extension_health_json(&extension_id, &|| Ok(()))
|
|
.map_err(ExtensionManagerError::Operation)
|
|
}
|
|
|
|
pub fn download(
|
|
&self,
|
|
extension_id: String,
|
|
request_json: String,
|
|
resolution_timeout_ms: u64,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
let request = serde_json::from_str(&request_json).map_err(|error| {
|
|
ExtensionManagerError::Operation(format!("invalid download request: {error}"))
|
|
})?;
|
|
self.inner
|
|
.download(&extension_id, request, resolution_timeout_ms)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn release_idle_download_runtimes(&self) -> Result<u64, ExtensionManagerError> {
|
|
self.inner
|
|
.release_idle_download_runtimes()
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn release_memory(&self, under_pressure: bool) -> Result<(), ExtensionManagerError> {
|
|
self.inner
|
|
.release_memory(under_pressure)
|
|
.map_err(ExtensionManagerError::Operation)
|
|
}
|
|
|
|
pub fn post_process(
|
|
&self,
|
|
extension_id: String,
|
|
input_json: String,
|
|
metadata_json: String,
|
|
hook_id: String,
|
|
timeout_ms: u64,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
let input = serde_json::from_str(&input_json).map_err(|error| {
|
|
ExtensionManagerError::Operation(format!("invalid post-process input: {error}"))
|
|
})?;
|
|
let metadata = serde_json::from_str(&metadata_json).map_err(|error| {
|
|
ExtensionManagerError::Operation(format!("invalid post-process metadata: {error}"))
|
|
})?;
|
|
self.inner
|
|
.post_process(&extension_id, input, metadata, &hook_id, timeout_ms)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn run_post_processing(
|
|
&self,
|
|
input_json: String,
|
|
metadata_json: String,
|
|
timeout_ms: u64,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
// Preserve the permissive Go JSON wrapper used by PlatformBridge.
|
|
self.inner
|
|
.run_post_processing(
|
|
serde_json::from_str(&input_json).unwrap_or_default(),
|
|
serde_json::from_str(&metadata_json).unwrap_or_default(),
|
|
timeout_ms,
|
|
)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn check_availability(
|
|
&self,
|
|
extension_id: String,
|
|
request_json: String,
|
|
timeout_ms: u64,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
let request = serde_json::from_str(&request_json).map_err(|error| {
|
|
ExtensionManagerError::Operation(format!("invalid availability request: {error}"))
|
|
})?;
|
|
self.inner
|
|
.check_availability(&extension_id, request, timeout_ms)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn enrich_track(
|
|
&self,
|
|
extension_id: String,
|
|
track_json: String,
|
|
item_id: String,
|
|
timeout_ms: u64,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.enrich_track(&extension_id, &track_json, &item_id, timeout_ms)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn search_metadata_provider(
|
|
&self,
|
|
extension_id: String,
|
|
query: String,
|
|
limit: i64,
|
|
timeout_ms: u64,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.search_metadata_provider(&extension_id, &query, limit as isize, timeout_ms)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn search_metadata_providers(
|
|
&self,
|
|
query: String,
|
|
limit: i64,
|
|
include_extensions: bool,
|
|
item_id: String,
|
|
timeout_ms: u64,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.search_metadata_providers(
|
|
&query,
|
|
limit as isize,
|
|
include_extensions,
|
|
&item_id,
|
|
timeout_ms,
|
|
)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn invoke_action(
|
|
&self,
|
|
extension_id: String,
|
|
action: String,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.invoke_action(&extension_id, &action)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn call(
|
|
&self,
|
|
extension_id: String,
|
|
method: String,
|
|
arguments_json: String,
|
|
lease: Option<Arc<RequestLease>>,
|
|
timeout_ms: u64,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.call(
|
|
&extension_id,
|
|
&method,
|
|
&arguments_json,
|
|
lease.map(|lease| Arc::clone(&lease.inner)),
|
|
timeout_ms,
|
|
)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn provider_ids(&self, kind: String) -> Result<Vec<String>, ExtensionManagerError> {
|
|
self.inner.provider_ids(&kind).map_err(Into::into)
|
|
}
|
|
|
|
pub fn provider_call(
|
|
&self,
|
|
extension_id: String,
|
|
method: String,
|
|
arguments_json: String,
|
|
lease: Option<Arc<RequestLease>>,
|
|
timeout_ms: u64,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.provider_call(
|
|
&extension_id,
|
|
&method,
|
|
&arguments_json,
|
|
lease.map(|lease| Arc::clone(&lease.inner)),
|
|
timeout_ms,
|
|
)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn set_provider_priority(
|
|
&self,
|
|
kind: String,
|
|
ids: Vec<String>,
|
|
) -> Result<(), ExtensionManagerError> {
|
|
self.inner
|
|
.set_provider_priority(&kind, ids)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn set_fallback_providers(
|
|
&self,
|
|
ids: Option<Vec<String>>,
|
|
) -> Result<(), ExtensionManagerError> {
|
|
self.inner.set_fallback_providers(ids).map_err(Into::into)
|
|
}
|
|
|
|
pub fn provider_priorities(&self) -> Result<String, ExtensionManagerError> {
|
|
self.inner.provider_priorities().map_err(Into::into)
|
|
}
|
|
|
|
pub fn fallback_allowed(&self, extension_id: String) -> Result<bool, ExtensionManagerError> {
|
|
self.inner
|
|
.fallback_allowed(&extension_id)
|
|
.map_err(Into::into)
|
|
}
|
|
|
|
pub fn find_url_handler(&self, url: String) -> Result<Option<String>, ExtensionManagerError> {
|
|
self.inner.find_url_handler(&url).map_err(Into::into)
|
|
}
|
|
|
|
pub fn get_extension_pending_auth_json(
|
|
&self,
|
|
extension_id: String,
|
|
) -> Result<String, ExtensionManagerError> {
|
|
self.inner
|
|
.get_extension_pending_auth_json(&extension_id)
|
|
.map_err(ExtensionManagerError::Operation)
|
|
}
|
|
|
|
pub fn unload(&self, extension_id: String) -> Result<(), ExtensionManagerError> {
|
|
self.inner.unload(&extension_id).map_err(Into::into)
|
|
}
|
|
|
|
pub fn unload_all(&self) -> Result<(), ExtensionManagerError> {
|
|
self.inner.unload_all().map_err(Into::into)
|
|
}
|
|
|
|
pub fn remove(&self, extension_id: String) -> Result<(), ExtensionManagerError> {
|
|
self.inner.remove(&extension_id).map_err(Into::into)
|
|
}
|
|
|
|
pub fn shutdown(&self) {
|
|
self.inner.shutdown();
|
|
}
|
|
}
|