split route code

This commit is contained in:
Philipp Hofer 2025-04-08 14:00:14 +02:00
parent e7913736e5
commit 272b5eb61c
5 changed files with 193 additions and 168 deletions

View File

@ -1,5 +1,5 @@
use axum::{Router, body::Body, response::Response, routing::get};
use maud::{Markup, html};
use axum::{body::Body, response::Response, routing::get, Router};
use maud::{html, Markup};
use partials::page;
use sqlx::SqlitePool;
use std::sync::Arc;
@ -79,12 +79,25 @@ async fn serve_marker_png() -> Response<Body> {
async fn index(session: Session) -> Markup {
let content = html! {
h1 { "Stationslauf-App" }
nav {
ul {
li {
a role="button" href="/station" {
"Stationen"
}
}
li {
a role="button" href="/route" {
"Routen"
}
}
li {
a role="button" href="/group" {
"Gruppen"
}
}
}
}
};
page(content, session, false).await
}

167
src/route/mod.rs Normal file
View File

@ -0,0 +1,167 @@
use crate::station::Station;
use axum::Router;
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use std::sync::Arc;
mod web;
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub(crate) struct Route {
pub(crate) id: i64,
name: String,
}
impl Route {
async fn all(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as::<_, Self>("SELECT id, name FROM route;")
.fetch_all(db)
.await
.unwrap()
}
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!(Self, "SELECT id, name FROM route WHERE id = ?", id)
.fetch_one(db)
.await
.ok()
}
async fn create(db: &SqlitePool, name: &str) -> Result<(), String> {
sqlx::query!("INSERT INTO route(name) VALUES (?)", name)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
async fn update_name(&self, db: &SqlitePool, name: &str) {
sqlx::query!("UPDATE route SET name = ? WHERE id = ?", name, self.id)
.execute(db)
.await
.unwrap();
}
async fn add_station(&self, db: &SqlitePool, station: &Station) -> Result<(), String> {
sqlx::query!(
r#"
INSERT INTO route_station (route_id, station_id, pos)
VALUES (?, ?, (
SELECT COALESCE(MAX(pos), 0) + 2
FROM route_station
WHERE route_id = ?
))
"#,
self.id,
station.id,
self.id
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
async fn delete_station(&self, db: &SqlitePool, station: &Station) -> bool {
let result = sqlx::query!(
"DELETE FROM route_station WHERE route_id = ? AND station_id = ?",
self.id,
station.id
)
.execute(db)
.await
.unwrap();
result.rows_affected() > 0
}
async fn move_station_higher(&self, db: &SqlitePool, station: &Station) -> bool {
let result = sqlx::query!(
"UPDATE route_station SET pos = pos-3 WHERE route_id = ? AND station_id = ?",
self.id,
station.id
)
.execute(db)
.await
.unwrap();
if result.rows_affected() == 0 {
return false;
}
sqlx::query(
&format!(
"
-- Step 1: Create a temporary table with new rank values
CREATE TEMP TABLE IF NOT EXISTS temp_pos AS
SELECT
route_id,
station_id,
ROW_NUMBER() OVER (ORDER BY pos ASC) AS new_rank
FROM
route_station WHERE route_id = {};
-- Step 2: Update the original table
UPDATE route_station
SET pos = (SELECT 2*new_rank FROM temp_pos WHERE temp_pos.route_id = route_station.route_id AND temp_pos.station_id = route_station.station_id) WHERE route_id = {};
-- Step 3: Drop the temporary table (no longer needed)
DROP TABLE temp_pos;",
self.id,
self.id,
))
.execute(db)
.await
.unwrap();
true
}
async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
sqlx::query!("DELETE FROM route WHERE id = ?", self.id)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
async fn stations(&self, db: &SqlitePool) -> Vec<Station> {
sqlx::query_as::<_, Station>(
"
SELECT s.id, s.name, s.notes, s.amount_people, s.last_login, s.pw, s.lat, s.lng
FROM station s
JOIN route_station rs ON s.id = rs.station_id
WHERE rs.route_id = ?
ORDER BY rs.pos
",
)
.bind(self.id)
.fetch_all(db)
.await
.unwrap()
}
async fn stations_not_in_route(&self, db: &SqlitePool) -> Vec<Station> {
sqlx::query_as::<_, Station>(
"
SELECT id, name, notes, amount_people, last_login, pw, lat, lng
FROM station
WHERE id NOT IN (
SELECT station_id
FROM route_station
WHERE route_id = ?
)
ORDER BY name
",
)
.bind(self.id)
.fetch_all(db)
.await
.unwrap()
}
}
pub(super) fn routes() -> Router<Arc<SqlitePool>> {
web::routes()
}

View File

@ -1,172 +1,17 @@
use super::Route;
use crate::{err, page, station::Station, succ};
use axum::{
Form, Router,
extract::State,
response::{IntoResponse, Redirect},
routing::{get, post},
Form, Router,
};
use maud::{Markup, PreEscaped, html};
use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use maud::{html, Markup, PreEscaped};
use serde::Deserialize;
use sqlx::SqlitePool;
use std::sync::Arc;
use tower_sessions::Session;
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub(crate) struct Route {
pub(crate) id: i64,
name: String,
}
impl Route {
async fn all(db: &SqlitePool) -> Vec<Self> {
sqlx::query_as::<_, Self>("SELECT id, name FROM route;")
.fetch_all(db)
.await
.unwrap()
}
pub async fn find_by_id(db: &SqlitePool, id: i64) -> Option<Self> {
sqlx::query_as!(Self, "SELECT id, name FROM route WHERE id = ?", id)
.fetch_one(db)
.await
.ok()
}
async fn create(db: &SqlitePool, name: &str) -> Result<(), String> {
sqlx::query!("INSERT INTO route(name) VALUES (?)", name)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
async fn update_name(&self, db: &SqlitePool, name: &str) {
sqlx::query!("UPDATE route SET name = ? WHERE id = ?", name, self.id)
.execute(db)
.await
.unwrap();
}
async fn add_station(&self, db: &SqlitePool, station: &Station) -> Result<(), String> {
sqlx::query!(
r#"
INSERT INTO route_station (route_id, station_id, pos)
VALUES (?, ?, (
SELECT COALESCE(MAX(pos), 0) + 2
FROM route_station
WHERE route_id = ?
))
"#,
self.id,
station.id,
self.id
)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
async fn delete_station(&self, db: &SqlitePool, station: &Station) -> bool {
let result = sqlx::query!(
"DELETE FROM route_station WHERE route_id = ? AND station_id = ?",
self.id,
station.id
)
.execute(db)
.await
.unwrap();
result.rows_affected() > 0
}
async fn move_station_higher(&self, db: &SqlitePool, station: &Station) -> bool {
let result = sqlx::query!(
"UPDATE route_station SET pos = pos-3 WHERE route_id = ? AND station_id = ?",
self.id,
station.id
)
.execute(db)
.await
.unwrap();
if result.rows_affected() == 0 {
return false;
}
sqlx::query(
&format!(
"
-- Step 1: Create a temporary table with new rank values
CREATE TEMP TABLE IF NOT EXISTS temp_pos AS
SELECT
route_id,
station_id,
ROW_NUMBER() OVER (ORDER BY pos ASC) AS new_rank
FROM
route_station WHERE route_id = {};
-- Step 2: Update the original table
UPDATE route_station
SET pos = (SELECT 2*new_rank FROM temp_pos WHERE temp_pos.route_id = route_station.route_id AND temp_pos.station_id = route_station.station_id) WHERE route_id = {};
-- Step 3: Drop the temporary table (no longer needed)
DROP TABLE temp_pos;",
self.id,
self.id,
))
.execute(db)
.await
.unwrap();
true
}
async fn delete(&self, db: &SqlitePool) -> Result<(), String> {
sqlx::query!("DELETE FROM route WHERE id = ?", self.id)
.execute(db)
.await
.map_err(|e| e.to_string())?;
Ok(())
}
async fn stations(&self, db: &SqlitePool) -> Vec<Station> {
sqlx::query_as::<_, Station>(
"
SELECT s.id, s.name, s.notes, s.amount_people, s.last_login, s.pw, s.lat, s.lng
FROM station s
JOIN route_station rs ON s.id = rs.station_id
WHERE rs.route_id = ?
ORDER BY rs.pos
",
)
.bind(self.id)
.fetch_all(db)
.await
.unwrap()
}
async fn stations_not_in_route(&self, db: &SqlitePool) -> Vec<Station> {
sqlx::query_as::<_, Station>(
"
SELECT id, name, notes, amount_people, last_login, pw, lat, lng
FROM station
WHERE id NOT IN (
SELECT station_id
FROM route_station
WHERE route_id = ?
)
ORDER BY name
",
)
.bind(self.id)
.fetch_all(db)
.await
.unwrap()
}
}
async fn index(State(db): State<Arc<SqlitePool>>, session: Session) -> Markup {
let routes = Route::all(&db).await;

View File

@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize};
use sqlx::{FromRow, SqlitePool};
use std::sync::Arc;
mod routes;
mod web;
#[derive(FromRow, Debug, Serialize, Deserialize)]
pub(crate) struct Station {
@ -130,5 +130,5 @@ impl Station {
}
pub(super) fn routes() -> Router<Arc<SqlitePool>> {
routes::routes()
web::routes()
}