added database models and models for continous use. Also changed get all paths according to that. Nothing is testet yet!
This commit is contained in:
12
devenv.lock
12
devenv.lock
@@ -3,10 +3,10 @@
|
|||||||
"devenv": {
|
"devenv": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"dir": "src/modules",
|
"dir": "src/modules",
|
||||||
"lastModified": 1761343822,
|
"lastModified": 1761922975,
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "devenv",
|
"repo": "devenv",
|
||||||
"rev": "679d2951cee2d09da3c732d00b320ce752d21ee0",
|
"rev": "c9f0b47815a4895fadac87812de8a4de27e0ace1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -19,10 +19,10 @@
|
|||||||
"flake-compat": {
|
"flake-compat": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1747046372,
|
"lastModified": 1761588595,
|
||||||
"owner": "edolstra",
|
"owner": "edolstra",
|
||||||
"repo": "flake-compat",
|
"repo": "flake-compat",
|
||||||
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
|
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -74,10 +74,10 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1758532697,
|
"lastModified": 1761313199,
|
||||||
"owner": "cachix",
|
"owner": "cachix",
|
||||||
"repo": "devenv-nixpkgs",
|
"repo": "devenv-nixpkgs",
|
||||||
"rev": "207a4cb0e1253c7658c6736becc6eb9cace1f25f",
|
"rev": "d1c30452ebecfc55185ae6d1c983c09da0c274ff",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
DROP TABLE pathVersions;
|
DROP TABLE pathMetadata;
|
||||||
DROP TABLE path;
|
DROP TABLE path;
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
CREATE TABLE path (
|
CREATE TABLE path (
|
||||||
id integer primary key,
|
id text primary key,
|
||||||
title text,
|
title text,
|
||||||
description text,
|
description text,
|
||||||
versions text
|
versions text
|
||||||
);
|
);
|
||||||
|
|
||||||
CREATE TABLE pathVersions (
|
CREATE TABLE pathMetadata (
|
||||||
pathId integer references path(id),
|
pathId text references path(id),
|
||||||
versionNumber string,
|
versionNumber text,
|
||||||
|
createdAt text,
|
||||||
|
updatedAt text,
|
||||||
primary key (pathId, versionNumber)
|
primary key (pathId, versionNumber)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// Learn more about Tauri commands at https://tauri.app/develop/calling-rust/
|
|
||||||
use sqlx::{migrate::MigrateDatabase, sqlite::SqlitePoolOptions, Pool, Sqlite};
|
use sqlx::{migrate::MigrateDatabase, sqlite::SqlitePoolOptions, Pool, Sqlite};
|
||||||
use tauri::{App, Manager};
|
use tauri::{App, Manager};
|
||||||
|
|
||||||
|
|||||||
7
src-tauri/src/models/db_models/exercise_db.rs
Normal file
7
src-tauri/src/models/db_models/exercise_db.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#[derive(Debug, sqlx::FromRow)]
|
||||||
|
pub struct ExerciseDb {
|
||||||
|
pub id: u16,
|
||||||
|
pub node_id: u32,
|
||||||
|
pub ex_type: String,
|
||||||
|
pub content: String,
|
||||||
|
}
|
||||||
3
src-tauri/src/models/db_models/mod.rs
Normal file
3
src-tauri/src/models/db_models/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod path_db;
|
||||||
|
pub mod node_db;
|
||||||
|
pub mod exercise_db;
|
||||||
7
src-tauri/src/models/db_models/node_db.rs
Normal file
7
src-tauri/src/models/db_models/node_db.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
#[derive(Debug, sqlx::FromRow)]
|
||||||
|
pub struct NodeDb {
|
||||||
|
pub id: u32,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub path_id: String
|
||||||
|
}
|
||||||
17
src-tauri/src/models/db_models/path_db.rs
Normal file
17
src-tauri/src/models/db_models/path_db.rs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
use chrono::{DateTime, Utc};
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(sqlx::FromRow, Debug)]
|
||||||
|
pub struct PathDb {
|
||||||
|
pub id: String,
|
||||||
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, sqlx::FromRow)]
|
||||||
|
pub struct MetadataDb {
|
||||||
|
pub path_id : String,
|
||||||
|
pub version: String,
|
||||||
|
pub created_at: String,
|
||||||
|
pub updated_at: String,
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
|
#[derive(Debug, Clone)]
|
||||||
pub struct Exercise {
|
pub struct Exercise {
|
||||||
id: u16,
|
pub id: u16,
|
||||||
ex_type: String,
|
pub ex_type: String,
|
||||||
content: String,
|
pub content: String,
|
||||||
node_id: u32
|
pub node_id: u32,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
pub mod path;
|
pub mod path;
|
||||||
pub mod node;
|
pub mod node;
|
||||||
pub mod exercise;
|
pub mod exercise;
|
||||||
|
|
||||||
|
pub mod db_models;
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
#[derive(Debug, sqlx::FromRow)]
|
use crate::models::exercise::Exercise;
|
||||||
pub struct Node {
|
|
||||||
id: u32,
|
#[derive(Debug)]
|
||||||
title: String,
|
pub struct Node{
|
||||||
description: String,
|
pub id: u32,
|
||||||
path_id: String
|
pub title: String,
|
||||||
|
pub description: String,
|
||||||
|
pub path_id: String,
|
||||||
|
pub exercises: Vec<Exercise>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use sqlx::sqlite::SqliteRow;
|
|
||||||
use sqlx::Row;
|
|
||||||
|
|
||||||
use crate::models::node::Node;
|
use crate::models::node::Node;
|
||||||
|
|
||||||
#[derive(sqlx::FromRow, Debug)]
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct Path {
|
pub struct Path {
|
||||||
id: String,
|
pub id: String,
|
||||||
title: String,
|
pub title: String,
|
||||||
description: String,
|
pub description: String,
|
||||||
nodes: Vec<Node>,
|
pub metadata: Vec<Metadata>,
|
||||||
metadata: Metadata,
|
pub nodes: Vec<Node>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, sqlx::FromRow)]
|
#[derive(Debug)]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
versions: Vec<String>,
|
pub path_id : String,
|
||||||
created_at: DateTime<Utc>,
|
pub version: String,
|
||||||
updated_at: DateTime<Utc>,
|
pub created_at: DateTime<Utc>,
|
||||||
|
pub updated_at: DateTime<Utc>,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,19 @@
|
|||||||
use sqlx::{sqlite::SqlitePool, FromRow};
|
use sqlx::{
|
||||||
|
sqlite::{SqlitePool, SqliteRow},
|
||||||
|
FromRow,
|
||||||
|
};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use crate::models::path::Path;
|
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 struct PathRepository<'a> {
|
||||||
pub pool: &'a SqlitePool,
|
pub pool: &'a SqlitePool,
|
||||||
@@ -8,36 +21,211 @@ pub struct PathRepository<'a> {
|
|||||||
|
|
||||||
impl<'a> PathRepository<'a> {
|
impl<'a> PathRepository<'a> {
|
||||||
pub async fn get_path_by_id(&self, id: i32) -> Result<Path, String> {
|
pub async fn get_path_by_id(&self, id: i32) -> Result<Path, String> {
|
||||||
let result = sqlx::query("SELECT * FROM path WHERE id = ?")
|
// Get Path
|
||||||
|
let path_result = sqlx::query("SELECT * FROM path WHERE id = ?")
|
||||||
.bind(id)
|
.bind(id)
|
||||||
.fetch_all(self.pool)
|
.fetch_all(self.pool)
|
||||||
.await
|
.await;
|
||||||
.map_err(|e| format!("ERROR: Failed to query Path db: {} ", e))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
if result.len() > 1 {
|
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));
|
return Err(format!("ERROR: Multiple paths for ID {} found", id));
|
||||||
} else if result.is_empty(){
|
} else if path_result.is_empty() {
|
||||||
return Err(format!("ERROR: No Path with ID {} found", id));
|
return Err(format!("ERROR: No Path with ID {} found", id));
|
||||||
}
|
}
|
||||||
|
|
||||||
let path = match result.first() {
|
let path_result = match path_result.first() {
|
||||||
Some(p) => match Path::from_row(p) {
|
Some(p) => match PathDb::from_row(p) {
|
||||||
Ok(p) => {p},
|
Ok(p) => p,
|
||||||
Err(e) => {return Err(format!("ERROR: Could not parse Path: {}", e));},
|
Err(e) => {
|
||||||
},
|
return Err(format!("ERROR: Could not parse Path: {}", e));
|
||||||
|
}
|
||||||
|
},
|
||||||
None => return Err(format!("ERROR: No path for ID {} found", id)),
|
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)
|
Ok(path)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_all_paths(&self) -> Result<Vec<Path>, String> {
|
pub async fn get_all_paths(&self) -> Result<Vec<Path>, String> {
|
||||||
let rows = sqlx::query_as::<_, Path>("SELECT * FROM paths")
|
let rows = sqlx::query_as::<_, PathDb>("SELECT * FROM path")
|
||||||
.fetch_all(self.pool)
|
.fetch_all(self.pool)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
Ok(rows)
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user