Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
a5a71cc
feat(provider): add Wave 1 scaffolding - provider-host + providers + …
rghamilton3 Mar 13, 2026
037120c
feat(provider): add Wave 1 scaffolding
rghamilton3 Mar 13, 2026
c92819f
feat(provider): integrate SensorStore for sample injection
rghamilton3 Mar 13, 2026
3bd1de2
feat(provider): add panic containment for provider calls
rghamilton3 Mar 13, 2026
d7e17fe
feat(provider): implement graceful shutdown with CancellationToken
rghamilton3 Mar 13, 2026
6a03587
feat(provider): implement MockProvider
rghamilton3 Mar 13, 2026
2d2cc02
test(provider): add ProviderHost unit tests
rghamilton3 Mar 14, 2026
28802c0
feat(provider): add LinuxProvider scaffolding with cfg guard
rghamilton3 Mar 14, 2026
f125af9
feat(provider): implement LinuxProvider sensor discovery
rghamilton3 Mar 14, 2026
cb8beb6
feat(provider): implement LinuxProvider polling
rghamilton3 Mar 14, 2026
ffe0052
test(provider): add LinuxProvider tests
rghamilton3 Mar 14, 2026
acc98b9
feat(tauri): add get_providers_status command
rghamilton3 Mar 14, 2026
4ca1f77
feat(tauri): add list_available_sensors command
rghamilton3 Mar 14, 2026
cd28d8c
test(tauri): add Tauri command tests
rghamilton3 Mar 14, 2026
5694b83
fix(tauri): register MockProvider in run() function
rghamilton3 Mar 14, 2026
4305242
fix(provider): start ProviderHost after registration, track sensor count
rghamilton3 Mar 14, 2026
9166080
merge: resolve conflict with main - include all workspace crates
rghamilton3 Mar 14, 2026
39adc49
fix(tauri): start providers in setup hook after runtime initialized
rghamilton3 Mar 14, 2026
9f99435
fix(tauri): spawn providers via async_runtime in setup hook
rghamilton3 Mar 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[workspace]
resolver = "3"
members = ["apps/desktop/src-tauri", "crates/domain", "crates/sensor-store", "crates/binding-engine"]
members = ["apps/desktop/src-tauri", "crates/domain", "crates/sensor-store", "crates/provider-host", "crates/providers", "crates/binding-engine"]
9 changes: 9 additions & 0 deletions apps/desktop/src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,13 @@ tauri = { version = "2", features = [] }
tauri-plugin-opener = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tokio = { version = "1", features = ["rt-multi-thread"] }
astragauge-provider-host = { path = "../../../crates/provider-host" }
astragauge-sensor-store = { path = "../../../crates/sensor-store" }
astragauge-domain = { path = "../../../crates/domain" }
astragauge-providers = { path = "../../../crates/providers" }

[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
async-trait = "0.1"

158 changes: 157 additions & 1 deletion apps/desktop/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,170 @@
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
use serde::{Deserialize, Serialize};

use astragauge_provider_host::{HostConfig, ProviderHost};
use astragauge_providers::MockProvider;
use astragauge_sensor_store::SensorStore;
use std::sync::Arc;
use tauri::State;

#[tauri::command]
fn greet(name: &str) -> String {
format!("Hello, {}! You've been greeted from Rust!", name)
}

#[tauri::command]
fn get_providers_status(
host: State<Arc<std::sync::RwLock<ProviderHost>>>,
) -> Result<Vec<astragauge_provider_host::ProviderStatus>, String> {
host
.read()
.map(|h| h.get_providers_status())
.map_err(|e| format!("Failed to acquire provider host lock: {}", e))
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SensorInfo {
pub id: String,
pub name: String,
pub category: String,
pub unit: String,
pub provider_id: String,
}

#[tauri::command]
async fn list_available_sensors(
store: State<'_, Arc<SensorStore>>,
) -> Result<Vec<SensorInfo>, String> {
let sensor_ids = store.list_sensors().await;

let mut sensors = Vec::new();
for sensor_id in sensor_ids {
if let Some(descriptor) = store.get_descriptor(&sensor_id).await {
let provider_id = sensor_id
.split('.')
.next()
.unwrap_or(&sensor_id)
.to_string();

sensors.push(SensorInfo {
id: descriptor.id.to_string(),
name: descriptor.name,
category: descriptor.category,
unit: descriptor.unit,
provider_id,
});
}
}

Ok(sensors)
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
let store = Arc::new(SensorStore::new());
let config = HostConfig::default();
let host = Arc::new(std::sync::RwLock::new(ProviderHost::new(config, store)));

let mock_provider = MockProvider::new_test();
let mock_provider_arc: Arc<Box<dyn astragauge_provider_host::Provider>> =
Arc::new(Box::new(mock_provider));
host
.write()
.unwrap()
.register_provider(mock_provider_arc)
.expect("Failed to register MockProvider");

let host_clone = Arc::clone(&host);

tauri::Builder::default()
.plugin(tauri_plugin_opener::init())
.invoke_handler(tauri::generate_handler![greet])
.manage(host)
.invoke_handler(tauri::generate_handler![
greet,
get_providers_status,
list_available_sensors
])
.setup(move |_app| {
tauri::async_runtime::spawn(async move {
host_clone.write().unwrap().start();
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

#[cfg(test)]
mod tests {
use super::*;
use astragauge_provider_host::{ProviderHealth, ProviderStatus};

#[test]
fn test_provider_status_serialization() {
let status = ProviderStatus {
id: "test-provider".to_string(),
name: "Test Provider".to_string(),
health: ProviderHealth::Ok,
sensor_count: 5,
};

let json = serde_json::to_string(&status).expect("Should serialize to JSON");
assert!(json.contains("\"id\":\"test-provider\""));
assert!(json.contains("\"name\":\"Test Provider\""));
assert!(json.contains("\"health\":\"ok\""));
assert!(json.contains("\"sensor_count\":5"));
}

#[test]
fn test_empty_provider_status_list() {
let statuses: Vec<ProviderStatus> = vec![];
let json = serde_json::to_string(&statuses).expect("Should serialize empty list");
assert_eq!(json, "[]");
}

#[test]
fn test_provider_health_serialization() {
let health = ProviderHealth::Ok;
let json = serde_json::to_string(&health).expect("Should serialize to JSON");
assert_eq!(json, "\"ok\"");

let degraded = ProviderHealth::Degraded {
message: "Test degradation".to_string(),
};
let json = serde_json::to_string(&degraded).expect("Should serialize to JSON");
assert!(json.contains("degraded"));
assert!(json.contains("Test degradation"));

let error = ProviderHealth::Error {
message: "Test error".to_string(),
};
let json = serde_json::to_string(&error).expect("Should serialize to JSON");
assert!(json.contains("error"));
assert!(json.contains("Test error"));
}

#[test]
fn test_sensor_info_serialization() {
let sensor = SensorInfo {
id: "cpu.temperature".to_string(),
name: "CPU Temperature".to_string(),
category: "temperature".to_string(),
unit: "celsius".to_string(),
provider_id: "cpu".to_string(),
};

let json = serde_json::to_string(&sensor).expect("Should serialize to JSON");
assert!(json.contains("\"id\":\"cpu.temperature\""));
assert!(json.contains("\"name\":\"CPU Temperature\""));
assert!(json.contains("\"category\":\"temperature\""));
assert!(json.contains("\"unit\":\"celsius\""));
assert!(json.contains("\"provider_id\":\"cpu\""));
}

#[test]
fn test_empty_sensor_list() {
let sensors: Vec<SensorInfo> = vec![];
let json = serde_json::to_string(&sensors).expect("Should serialize empty list");
assert_eq!(json, "[]");
}
}
Loading
Loading