# System Resources ORM Setup - Diesel Implementation This document provides all the code needed to convert the `system_resource` entity from raw SQL queries to Diesel ORM, following the same pattern used for the user entity. ## Current State Analysis The system_resources entity currently has: - ✅ Diesel model defined in `infrastructure/src/system_resources/model.rs` - ❌ No Diesel repository implementation (only SQL-based) - ❌ No Diesel adapter implementation (only SQL-based) - ❌ Missing table definition in `schema.rs` ## Step 1: Add system_resources Table Definition to schema.rs **File**: `infrastructure/src/schema.rs` Add the following table definition after the user table: ```rust // Define the system_resources table structure matching the current SQL schema table! { system_resources (id) { id -> Text, datetime -> Integer, cpu_usage -> Float, total_memory -> Integer, used_memory -> Integer, } } ``` ## Step 2: Create Diesel Repository Implementation **File**: `infrastructure/src/system_resources/diesel_repository.rs` ```rust use diesel::prelude::*; use diesel::sqlite::SqliteConnection; use std::sync::{Arc, Mutex}; use chrono::{DateTime, Utc}; use super::model::SystemResourcesEntity; #[derive(Clone)] pub struct SystemResourcesDieselRepository { pub database_connection: Arc>, } impl SystemResourcesDieselRepository { pub fn new(database_connection: Arc>) -> Self { Self { database_connection, } } fn get_connection(&self) -> std::sync::MutexGuard<'_, SqliteConnection> { self.database_connection.lock().unwrap() } pub fn create_table(&self) -> Result<(), diesel::result::Error> { let mut connection = self.get_connection(); diesel::sql_query( "CREATE TABLE IF NOT EXISTS system_resources ( id TEXT PRIMARY KEY, datetime INTEGER NOT NULL, cpu_usage REAL NOT NULL, total_memory INTEGER NOT NULL, used_memory INTEGER NOT NULL );" ).execute(&mut *connection)?; Ok(()) } pub fn insert(&self, resource: &SystemResourcesEntity) -> Result<(), diesel::result::Error> { let mut connection = self.get_connection(); diesel::insert_into(crate::schema::system_resources::table) .values(resource) .execute(&mut *connection)?; Ok(()) } pub fn get_by_id(&self, id: &str) -> Result, diesel::result::Error> { let mut connection = self.get_connection(); let result: Result = crate::schema::system_resources::table .find(id) .first(&mut *connection); match result { Ok(resource_diesel) => Ok(Some(resource_diesel)), Err(diesel::result::Error::NotFound) => Ok(None), Err(e) => Err(e), } } pub fn get_between(&self, start: i64, end: i64) -> Result, diesel::result::Error> { let mut connection = self.get_connection(); let resources: Vec = crate::schema::system_resources::table .filter(crate::schema::system_resources::datetime.between(start, end)) .order(crate::schema::system_resources::datetime.asc()) .load(&mut *connection)?; Ok(resources) } } ``` ## Step 3: Create Diesel Adapter Implementation **File**: `infrastructure/src/system_resources/diesel_adapter.rs` ```rust use std::io::{Error, ErrorKind}; use chrono::{DateTime, Utc}; use lumiere_domain::system_resources::model::SystemResources; use lumiere_domain::system_resources::port::SystemResourcesPort; use crate::system_resources::diesel_repository::SystemResourcesDieselRepository; use sysinfo::{CpuRefreshKind, MemoryRefreshKind, RefreshKind, System}; #[derive(Clone)] pub struct SystemResourcesDieselAdapter { pub repository: SystemResourcesDieselRepository } impl SystemResourcesDieselAdapter { pub fn new(repository: SystemResourcesDieselRepository) -> Self { Self { repository } } fn get_cpu_usage_from_shell(&self, _: &System) -> f32 { use std::process::Command; use log::error; if cfg!(target_os = "linux") { self.get_cpu_usage_from_command("echo $[100-$(vmstat 1 2|tail -1|awk '{print $15}')]") } else if cfg!(target_os = "macos") { self.get_cpu_usage_from_command("top -l 1 | grep -E \"^CPU\" | cut -d ' ' -f3 | cut -d '%' -f1") } else { error!("Unhandled OS…"); 0f32 } } fn get_cpu_usage_from_command(&self, command: &str) -> f32 { use std::process::Command; let command_output = Command::new("sh") .arg("-c") .arg(command) .output() .expect("Error while retrieving CPU usage from command…"); if command_output.status.success() { let string = String::from_utf8(command_output.stdout).unwrap(); string.trim().parse().unwrap() } else { let stderr = String::from_utf8(command_output.stderr).unwrap(); error!("Command failed with error:\n{}", stderr); 0f32 } } } impl SystemResourcesPort for SystemResourcesDieselAdapter { fn insert_system_resources(&self, system_resources: &SystemResources) -> Result<(), Error> { let resource_entity = crate::system_resources::model::SystemResourcesEntity::from(system_resources); self.repository.insert(&resource_entity) .map_err(|error| Error::new( ErrorKind::Other, format!("Unable to insert the system resources… Cause: {}", error) )) } fn get_system_resources_lines_between(&self, start: &DateTime, end: &DateTime) -> Result, Error> { self.repository.get_between(start.timestamp(), end.timestamp()) .map_err(|error| Error::new( ErrorKind::Other, format!("Unable to retrieve the system resources in the given period… Cause: {}", error) )) .map(|resource_entities| resource_entities.into_iter().map(|resource_entity| resource_entity.to_domain()).collect()) } fn get_system_resources(&self) -> Result { let system = System::new_with_specifics( RefreshKind::nothing().with_cpu(CpuRefreshKind::everything()).with_memory(MemoryRefreshKind::everything()), ); let cpu_usage = self.get_cpu_usage_from_shell(&system); let total_memory = system.total_memory(); let used_memory = system.used_memory(); let result = SystemResources::new( cpu_usage, total_memory, used_memory, ); Ok(result) } fn clone_box(&self) -> Box { Box::new(self.clone()) } } ``` ## Step 4: Update Module Files **File**: `infrastructure/src/system_resources/mod.rs` Add the new Diesel implementations to the module exports: ```rust // Add these lines to expose the new Diesel implementations pub mod diesel_repository; pub mod diesel_adapter; // Re-export the types for convenience pub use diesel_repository::SystemResourcesDieselRepository; pub use diesel_adapter::SystemResourcesDieselAdapter; ``` ## Step 5: Update Cargo.toml Dependencies **File**: `infrastructure/Cargo.toml` Ensure Diesel dependencies are properly configured. The file should include: ```toml diesel = { version = "2.1", features = ["sqlite"] } diesel_migrations = "2.1" ``` ## Step 6: Update Database Initialization **File**: `infrastructure/src/common/database_initialisation.rs` Update the initialization code to use Diesel for creating tables: ```rust // Replace the existing system_resources table creation with Diesel version impl DatabaseInitialisation { pub fn create_tables(&self) -> Result<(), Error> { // User table (already using Diesel) let user_repository = crate::user::diesel_repository::UserDieselRepository::new(self.database_connection.clone()); user_repository.create_table()?; // System resources table (now using Diesel) let system_resources_repository = crate::system_resources::diesel_repository::SystemResourcesDieselRepository::new(self.database_connection.clone()); system_resources_repository.create_table()?; Ok(()) } } ``` ## Step 7: Update Application Context **File**: `launcher/src/application_context.rs` Update the application context to use Diesel adapters: ```rust // Replace system resources adapter initialization let system_resources_repository = SystemResourcesDieselRepository::new(database_connection.clone()); let system_resources_adapter = SystemResourcesDieselAdapter::new(system_resources_repository); ``` ## Migration Strategy ### Phase 1: Parallel Implementation (Current Step) 1. ✅ Create Diesel model (already done) 2. ✅ Add table definition to schema.rs 3. ✅ Implement SystemResourcesDieselRepository 4. ✅ Implement SystemResourcesDieselAdapter 5. ✅ Update module exports ### Phase 2: Integration 1. Update database initialization to use Diesel 2. Update application context to use Diesel adapters 3. Test Diesel implementation alongside existing SQL version ### Phase 3: Validation 1. Verify identical behavior through manual testing 2. Check performance characteristics 3. Ensure all system resources API endpoints work correctly ### Phase 4: Cleanup (Future) 1. Remove old SQL-based adapter implementation 2. Remove old SQL-based repository implementation 3. Update all imports to use Diesel versions ## Key Differences from User Implementation 1. **Model Differences**: SystemResourcesEntity uses different field types: - `datetime` as i64 (Unix timestamp) instead of DateTime - `cpu_usage` as f32 instead of String - Memory fields as i64 instead of other types 2. **Repository Method Differences**: - No equivalent to `get_by_username` (system resources use datetime ranges) - `get_between` method for time-based queries instead of ID/username lookups - Different query patterns for temporal data 3. **Adapter Method Differences**: - Additional `get_system_resources()` method that collects live system data - Time-based range queries instead of single entity lookups ## Success Criteria 1. ✅ SystemResourcesDieselRepository implements all necessary methods 2. ✅ SystemResourcesDieselAdapter provides same functionality as SQL version 3. ✅ All system resources API endpoints work identically with Diesel backend 4. ✅ No data migration required - existing system resources continue to work 5. ✅ Performance characteristics meet or exceed current implementation 6. ✅ Error handling produces same error types and messages