use crate::platform::{CameraSystem, PlatformInfo, SystemTestResult};
use crate::types::{CameraDeviceInfo, CameraFormat, Platform};
use tauri::command;
use crate::registry::{FeatureManifest, SystemRegistry};
#[command]
pub async fn get_system_manifest() -> Vec<FeatureManifest> {
SystemRegistry::get_manifest()
}
#[command]
pub async fn initialize_camera_system() -> Result<String, String> {
match CameraSystem::initialize() {
Ok(message) => {
log::info!("Camera system initialized: {message}");
Ok(message)
}
Err(e) => {
log::error!("Failed to initialize camera system: {e}");
Err(format!("Failed to initialize camera system: {e}"))
}
}
}
#[command]
pub async fn get_available_cameras() -> Result<Vec<CameraDeviceInfo>, String> {
match CameraSystem::list_cameras() {
Ok(cameras) => {
log::info!("Found {} cameras", cameras.len());
for camera in &cameras {
log::debug!(
"Camera: {} - {} (Available: {})",
camera.id,
camera.name,
camera.is_available
);
}
Ok(cameras)
}
Err(e) => {
log::error!("Failed to list cameras: {e}");
Err(format!("Failed to list cameras: {e}"))
}
}
}
#[command]
pub async fn get_platform_info() -> Result<PlatformInfo, String> {
match CameraSystem::get_platform_info() {
Ok(info) => {
log::info!(
"Platform: {} using {}",
info.platform.as_str(),
info.backend
);
Ok(info)
}
Err(e) => {
log::error!("Failed to get platform info: {e}");
Err(format!("Failed to get platform info: {e}"))
}
}
}
#[command]
pub async fn test_camera_system() -> Result<SystemTestResult, String> {
log::info!("Running camera system test...");
match CameraSystem::test_system() {
Ok(result) => {
log::info!(
"Camera system test completed: {} cameras found on {}",
result.cameras_found,
result.platform.as_str()
);
for (camera_id, test_result) in &result.test_results {
match test_result {
crate::platform::CameraTestResult::Success => {
log::info!("Camera {camera_id} test: SUCCESS");
}
crate::platform::CameraTestResult::InitError(err) => {
log::warn!("Camera {camera_id} init error: {err}");
}
crate::platform::CameraTestResult::CaptureError(err) => {
log::warn!("Camera {camera_id} capture error: {err}");
}
crate::platform::CameraTestResult::NotAvailable => {
log::info!("Camera {camera_id} not available");
}
}
}
Ok(result)
}
Err(e) => {
log::error!("Camera system test failed: {e}");
Err(format!("Camera system test failed: {e}"))
}
}
}
#[command]
pub async fn get_current_platform() -> Result<String, String> {
let platform = Platform::current();
Ok(platform.as_str().to_string())
}
#[command]
pub async fn check_camera_availability(device_id: String) -> Result<bool, String> {
match CameraSystem::list_cameras() {
Ok(cameras) => {
let is_available = cameras
.iter()
.find(|camera| camera.id == device_id)
.is_some_and(|camera| camera.is_available);
log::debug!("Camera {device_id} availability: {is_available}");
Ok(is_available)
}
Err(e) => {
log::error!("Failed to check camera availability: {e}");
Err(format!("Failed to check camera availability: {e}"))
}
}
}
#[command]
pub async fn get_camera_formats(device_id: String) -> Result<Vec<CameraFormat>, String> {
match CameraSystem::list_cameras() {
Ok(cameras) => {
if let Some(camera) = cameras.iter().find(|c| c.id == device_id) {
log::debug!(
"Camera {} supports {} formats",
device_id,
camera.supports_formats.len()
);
Ok(camera.supports_formats.clone())
} else {
let msg = format!("Camera with ID '{device_id}' not found");
log::warn!("{msg}");
Err(msg)
}
}
Err(e) => {
log::error!("Failed to get camera formats: {e}");
Err(format!("Failed to get camera formats: {e}"))
}
}
}
#[command]
pub async fn get_recommended_format() -> Result<CameraFormat, String> {
let format = crate::platform::optimizations::get_photography_format();
log::info!(
"Recommended photography format: {}x{} @ {}fps ({})",
format.width,
format.height,
format.fps,
format.format_type
);
Ok(format)
}
#[command]
pub async fn get_optimal_settings() -> Result<crate::types::CameraInitParams, String> {
let params = crate::platform::optimizations::get_optimal_settings();
log::info!(
"Optimal settings: Device {} with {}x{} @ {}fps",
params.device_id,
params.format.width,
params.format.height,
params.format.fps
);
Ok(params)
}
#[command]
pub async fn get_system_diagnostics() -> Result<SystemDiagnostics, String> {
log::info!("Running system diagnostics...");
let platform = Platform::current();
let crate_version = crate::VERSION.to_string();
let (platform_info, platform_info_error) = match CameraSystem::get_platform_info() {
Ok(info) => (Some(info), None),
Err(e) => {
log::warn!("Platform info unavailable: {e}");
(None, Some(e.to_string()))
}
};
let (_cameras, _camera_enumeration_error) = match CameraSystem::list_cameras() {
Ok(cams) => (cams, None),
Err(e) => {
log::warn!("Camera enumeration failed: {e}");
(vec![], Some(e.to_string()))
}
};
let (cameras, camera_enumeration_error) = match CameraSystem::list_cameras() {
Ok(cams) => (cams, None),
Err(e) => {
let msg = e.to_string();
log::warn!("Camera enumeration failed: {msg}");
(vec![], Some(msg))
}
};
let camera_count = cameras.len();
let camera_summaries: Vec<CameraSummary> = cameras
.iter()
.map(|c| CameraSummary {
id: c.id.clone(),
name: c.name.clone(),
is_available: c.is_available,
format_count: c.supports_formats.len(),
max_resolution: c
.supports_formats
.iter()
.map(|f| (f.width, f.height))
.max_by_key(|(w, h)| w * h),
})
.collect();
let (permission_status, permission_error) =
match crate::commands::permissions::check_camera_permission_status().await {
Ok(p) => (p.status.to_string(), None),
Err(e) => ("unknown".to_string(), Some(e)),
};
let diagnostics = SystemDiagnostics {
crate_version,
platform: platform.as_str().to_string(),
backend: platform_info
.as_ref()
.map_or_else(|| "unknown".to_string(), |p| p.backend.clone()),
camera_count,
cameras: camera_summaries,
permission_status,
features_enabled: get_enabled_features(),
timestamp: chrono::Utc::now().to_rfc3339(),
platform_info_error,
camera_enumeration_error,
permission_error,
};
log::info!(
"Diagnostics complete: {} cameras on {} ({})",
diagnostics.camera_count,
diagnostics.platform,
diagnostics.backend
);
Ok(diagnostics)
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SystemDiagnostics {
pub crate_version: String,
pub platform: String,
pub backend: String,
pub camera_count: usize,
pub cameras: Vec<CameraSummary>,
pub permission_status: String,
pub features_enabled: Vec<String>,
pub timestamp: String,
pub platform_info_error: Option<String>,
pub camera_enumeration_error: Option<String>,
pub permission_error: Option<String>,
}
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct CameraSummary {
pub id: String,
pub name: String,
pub is_available: bool,
pub format_count: usize,
pub max_resolution: Option<(u32, u32)>,
}
fn get_enabled_features() -> Vec<String> {
[
("recording", cfg!(feature = "recording")),
("audio", cfg!(feature = "audio")),
("headless", cfg!(feature = "headless")),
("contextlite", cfg!(feature = "contextlite")),
]
.into_iter()
.filter(|(_, enabled)| *enabled)
.map(|(name, _)| name.to_string())
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_get_current_platform_returns_known_value() {
let platform = get_current_platform()
.await
.expect("platform query should succeed");
assert!(matches!(
platform.as_str(),
"windows" | "macos" | "linux" | "unknown"
));
}
#[tokio::test]
async fn test_get_recommended_format_has_valid_shape() {
let format = get_recommended_format()
.await
.expect("recommended format should be available");
assert!(format.width > 0);
assert!(format.height > 0);
assert!(format.fps > 0.0);
assert!(!format.format_type.is_empty());
}
#[tokio::test]
async fn test_get_optimal_settings_has_valid_shape() {
let params = get_optimal_settings()
.await
.expect("optimal settings should be available");
assert!(!params.device_id.is_empty());
assert!(params.format.width > 0);
assert!(params.format.height > 0);
assert!(params.format.fps > 0.0);
}
#[test]
fn test_get_enabled_features_contains_recording_when_enabled() {
let features = get_enabled_features();
#[cfg(feature = "recording")]
assert!(features.iter().any(|f| f == "recording"));
#[cfg(not(feature = "recording"))]
assert!(!features.iter().any(|f| f == "recording"));
}
#[tokio::test]
async fn test_system_diagnostics_shape() {
let diagnostics = get_system_diagnostics()
.await
.expect("diagnostics should always return a report");
assert!(!diagnostics.crate_version.is_empty());
assert!(matches!(
diagnostics.platform.as_str(),
"windows" | "macos" | "linux" | "unknown"
));
assert!(!diagnostics.backend.is_empty());
assert!(!diagnostics.permission_status.is_empty());
assert!(!diagnostics.timestamp.is_empty());
for cam in diagnostics.cameras {
assert!(!cam.id.is_empty());
assert!(!cam.name.is_empty());
}
}
}