mod app_data_files;
mod token_storage;
use std::sync::Arc;
use anyhow::{Context, Result};
use arora_bridge::Bridge;
use arora_hal::FakeHal;
use arora_simple_data_store::SimpleDataStore;
use arora_studio_bridge_client::firestore_support::options::{
FirebaseEmulatorOptions, FirebaseOptions,
};
use arora_studio_bridge_client::zenoh::ZenohDeviceClient;
use log::{info, warn};
use app_data_files::ensure_app_data_dir;
pub fn launch() -> Result<()> {
launch_with_hal(Arc::new(FakeHal::new()))
}
pub fn launch_with_hal(hal: Arc<dyn arora_hal::Hal>) -> Result<()> {
env_logger::init();
let firebase_options = FirebaseOptions::from_env();
let firebase_emulator_options = FirebaseEmulatorOptions::from_env();
let endpoints: Vec<String> = std::env::var("ZENOH_ENDPOINTS")
.ok()
.map(|v| v.split(',').map(|s| s.trim().to_string()).collect())
.unwrap_or_default();
let app_data_dir = ensure_app_data_dir().context("could not create app data directory")?;
let key_path = app_data_dir.join("key");
let token_path = match std::env::var("IDENTITY_FILE") {
Ok(identity_file) => std::path::PathBuf::from(identity_file),
Err(_) => app_data_dir.join("refresh_token"),
};
if rustls::crypto::CryptoProvider::get_default().is_none() {
let _ = rustls::crypto::ring::default_provider().install_default();
}
let refresh_token = token_storage::load_token(&key_path, &token_path)
.ok()
.flatten();
if refresh_token.is_some() {
info!("Refresh token found");
} else {
warn!("No refresh token found");
}
let save_cb: Box<dyn FnMut(String) + Send + Sync> = {
let key_path = key_path.clone();
let token_path = token_path.clone();
Box::new(move |token: String| {
if let Err(e) = token_storage::save_token(&key_path, &token_path, &token) {
warn!("Failed to save refresh token to {:?}: {:?}", token_path, e);
}
})
};
let device_info = device_info_from_env();
info!("Connecting via Zenoh (endpoints: {:?})", endpoints);
crate::launch_with(hal, SimpleDataStore::new(), move || async move {
let client = ZenohDeviceClient::new(
&firebase_options,
Some(&firebase_emulator_options),
refresh_token,
Some(save_cb),
endpoints,
)
.await
.map_err(|e| anyhow::anyhow!("failed to connect to Semio Studio via Zenoh: {e:?}"))?;
let client: Arc<dyn Bridge> = Arc::new(client);
if let Some(info) = device_info {
client
.update_device_info(Some(info))
.await
.context("failed to register device info with Studio")?;
info!("Registered device info with Studio");
}
Ok(client)
})
}
fn device_info_from_env() -> Option<arora_bridge::DeviceInfo> {
let owners: Vec<String> = std::env::var("DEVICE_OWNERS")
.ok()
.map(|s| {
s.split(',')
.map(|o| o.trim().to_string())
.filter(|o| !o.is_empty())
.collect()
})
.unwrap_or_default();
let info = arora_bridge::DeviceInfo {
name: std::env::var("DEVICE_NAME").ok(),
description: std::env::var("DEVICE_DESCRIPTION").ok(),
model_family: std::env::var("MODEL_FAMILY").ok(),
hardware_version: std::env::var("HARDWARE_VERSION").ok(),
software_version: std::env::var("SOFTWARE_VERSION").ok(),
owners,
};
let configured = info.name.is_some()
|| info.description.is_some()
|| info.model_family.is_some()
|| info.hardware_version.is_some()
|| info.software_version.is_some()
|| !info.owners.is_empty();
configured.then_some(info)
}