Skip to main content

origin_tauri/
defaults.rs

1//! The standard desktop wiring.
2//!
3//! Products call these from their composition root instead of repeating the same four
4//! adapter constructions — and override any single one where they need something else
5//! (ADR-0004, "convention by default, explicit override when needed").
6
7use crate::{HostConfig, TauriNotificationService, TauriOpener};
8use origin_domain::Result;
9use origin_http::HttpClient;
10use origin_http_reqwest::ReqwestHttpClient;
11use origin_platform::{NotificationService, Opener};
12use origin_secrets::SecretStore;
13use origin_secrets_system::SystemSecretStore;
14use origin_storage::Storage;
15use origin_storage_sqlite::SqliteStorage;
16use std::sync::Arc;
17use tauri::{AppHandle, Runtime};
18
19/// Name of the database file inside the app data directory.
20const DATABASE_FILE: &str = "origin.sqlite3";
21
22/// SQLite storage in the platform's app-data directory.
23///
24/// The directory comes from `origin_platform::paths`, not from Tauri's path resolver:
25/// a headless run of the same product has no `AppHandle` and must reach the same
26/// database. Two independent derivations of one directory is how a headless mode ends
27/// up looking at an empty file.
28///
29/// The file holds cache, read models and settings only — losing it costs a resync,
30/// nothing more (ADR-0008).
31pub fn storage<R: Runtime>(app: &AppHandle<R>, config: &HostConfig) -> Result<Arc<dyn Storage>> {
32    let _ = app;
33
34    let path = origin_platform::paths::data_dir(&config.app_id)?.join(DATABASE_FILE);
35    tracing::debug!(path = %path.display(), "opening application database");
36
37    Ok(Arc::new(SqliteStorage::open(path)?))
38}
39
40/// Credentials in the operating system credential store, scoped to this product.
41pub fn secret_store(config: &HostConfig) -> Arc<dyn SecretStore> {
42    Arc::new(SystemSecretStore::new(config.app_id.clone()))
43}
44
45/// Native notifications.
46pub fn notifications<R: Runtime>(app: &AppHandle<R>) -> Arc<dyn NotificationService> {
47    Arc::new(TauriNotificationService::new(app.clone()))
48}
49
50/// One HTTP client for the whole application.
51///
52/// The user agent identifies the product and version, which several APIs require and
53/// most of them use when they need to contact an integrator about traffic.
54pub fn http_client<R: Runtime>(
55    app: &AppHandle<R>,
56    config: &HostConfig,
57) -> Result<Arc<dyn HttpClient>> {
58    let package = app.package_info();
59    let user_agent = format!("{}/{} ({})", package.name, package.version, config.app_id);
60
61    Ok(Arc::new(ReqwestHttpClient::new(user_agent)?))
62}
63
64/// Opening external http(s) URLs in the user's browser.
65pub fn opener<R: Runtime>(app: &AppHandle<R>) -> Arc<dyn Opener> {
66    Arc::new(TauriOpener::new(app.clone()))
67}