use sqlx::{ sqlite::{SqlitePool, SqliteRow}, FromRow, }; use std::collections::HashMap; use crate::models::{ db_models::{ exercise_db::ExerciseDb, node_db::NodeDb, path_db::{MetadataDb, PathDb}, }, exercise::Exercise, node::Node, path::{Metadata, Path}, }; pub struct PathRepository<'a> { pub pool: &'a SqlitePool, } impl<'a> PathRepository<'a> { pub async fn get_path_by_id(&self, id: i32) -> Result { // Get Path let path_result = sqlx::query("SELECT * FROM path WHERE id = ?") .bind(id) .fetch_all(self.pool) .await; let path_result: Vec = match path_result { Ok(r) => r, Err(e) => { return Err(format!("ERROR: Failed to query Path db: {} ", e)); } }; if path_result.len() > 1 { return Err(format!("ERROR: Multiple paths for ID {} found", id)); } else if path_result.is_empty() { return Err(format!("ERROR: No Path with ID {} found", id)); } let path_result = match path_result.first() { Some(p) => match PathDb::from_row(p) { Ok(p) => p, Err(e) => { return Err(format!("ERROR: Could not parse Path: {}", e)); } }, None => return Err(format!("ERROR: No path for ID {} found", id)), }; // Get Metadata for path let metadata_result = sqlx::query("SELECT * From pathMetadata where pathId = ?") .bind(path_result.id.clone()) .fetch_all(self.pool) .await; let metadata_result = match metadata_result { Ok(r) => r, Err(e) => { return Err(format!("ERROR: Failed to query Metadata db: {}", e)); } }; if metadata_result.is_empty() { return Err(format!( "ERROR: No metadata for path [{:?}] found", path_result )); } let metadata_result: Result, String> = metadata_result .iter() .map(|row| { MetadataDb::from_row(row) .map_err(|e| format!("ERROR: Could not parse Metadata struct: {}", e)) }) .collect(); let metadata_result = match metadata_result { Ok(r) => r, Err(e) => return Err(e), }; // Get nodes for path let node_result = sqlx::query("SELECT * From node where pathId = ?") .bind(path_result.id.clone()) .fetch_all(self.pool) .await; let node_result = match node_result { Ok(r) => r, Err(e) => { return Err(format!("ERROR: Failed to query Node db: {}", e)); } }; if node_result.is_empty() { return Err(format!( "ERROR: No Nodes for path [{:?}] found", path_result )); } let node_result: Result, String> = node_result .iter() .map(|row| { NodeDb::from_row(row) .map_err(|e| format!("ERROR: Could not parse Node struct: {}", e)) }) .collect(); let node_result = match node_result { Ok(r) => r, Err(e) => return Err(e), }; // Get exercises for path let exercise_result = sqlx::query("SELECT * From exercise where pathId = ?") .bind(path_result.id.clone()) .fetch_all(self.pool) .await; let exercise_result = match exercise_result { Ok(r) => r, Err(e) => { return Err(format!("ERROR: Failed to query Exercise db: {}", e)); } }; if exercise_result.is_empty() { return Err(format!( "ERROR: No Exercise for path [{:?}] found", path_result )); } let exercise_result: Result, String> = exercise_result .iter() .map(|row| { ExerciseDb::from_row(row) .map_err(|e| format!("ERROR: Could not parse Exercise struct: {}", e)) }) .collect(); let exercise_result = match exercise_result { Ok(r) => r, Err(e) => return Err(e), }; // Convert metadata let metadata: Vec = metadata_result .iter() .map(|m| Metadata { path_id: m.path_id.clone(), version: m.version.clone(), created_at: m.created_at.parse().unwrap(), updated_at: m.updated_at.parse().unwrap(), }) .collect(); // Group exercises by node_id let mut exercises_by_node: HashMap> = HashMap::new(); for exercise_db in exercise_result { let exercise = Exercise { id: exercise_db.id, ex_type: exercise_db.ex_type, content: exercise_db.content, node_id: exercise_db.node_id, }; exercises_by_node .entry(exercise_db.node_id) .or_insert_with(Vec::new) .push(exercise); } // Create nodes with their respective exercises let nodes: Vec = node_result .iter() .map(|node_db| Node { id: node_db.id, title: node_db.title.clone(), description: node_db.description.clone(), path_id: node_db.path_id.clone(), exercises: exercises_by_node .get(&node_db.id) .cloned() .unwrap_or_else(Vec::new), }) .collect(); let path = Path { id: path_result.id, title: path_result.title, description: path_result.description, metadata, nodes, }; Ok(path) } pub async fn get_all_paths(&self) -> Result, String> { let rows = sqlx::query_as::<_, PathDb>("SELECT * FROM path") .fetch_all(self.pool) .await .map_err(|e| e.to_string())?; let mut paths = Vec::new(); for path_db in rows { match self.get_path_by_id(path_db.id.parse().unwrap_or(0)).await { Ok(path) => paths.push(path), Err(e) => { eprintln!("Warning: Failed to load path {}: {}", path_db.id, e); // Continue with other paths instead of failing completely continue; } } } Ok(paths) } pub async fn save_path(&self, _path: Path) -> Result<(), String> { // TODO: Implement path saving logic todo!("Implement save_path functionality") } }