Files
flalingo/src-tauri/src/repositories/path_repository.rs

232 lines
6.9 KiB
Rust

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<Path, String> {
// Get Path
let path_result = sqlx::query("SELECT * FROM path WHERE id = ?")
.bind(id)
.fetch_all(self.pool)
.await;
let path_result: Vec<SqliteRow> = 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<Vec<MetadataDb>, 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<Vec<NodeDb>, 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<Vec<ExerciseDb>, 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> = 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<u32, Vec<Exercise>> = 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> = 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<Vec<Path>, 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")
}
}