wsb 0.134.3832

Workspace - A tool suite for file operations, version management, and development workflow automation
Documentation
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    Json,
};
use serde::Deserialize;

use super::sessions::ErrorResponse;
use crate::web::state::WebState;

fn internal_err(e: anyhow::Error) -> (StatusCode, Json<ErrorResponse>) {
    (
        StatusCode::INTERNAL_SERVER_ERROR,
        Json(ErrorResponse { error: e.to_string() }),
    )
}

fn not_found(msg: &str) -> (StatusCode, Json<ErrorResponse>) {
    (
        StatusCode::NOT_FOUND,
        Json(ErrorResponse { error: msg.to_string() }),
    )
}

#[derive(Deserialize)]
pub struct PathQuery {
    pub path: String,
}

pub async fn list_path_notes(
    State(state): State<WebState>,
    Query(q): Query<PathQuery>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<ErrorResponse>)> {
    let notes = state.db.list_path_notes(&q.path).await.map_err(internal_err)?;
    Ok(Json(serde_json::json!({ "notes": notes })))
}

#[derive(Deserialize)]
pub struct CreateNoteBody {
    pub path: String,
    pub content: String,
}

pub async fn create_path_note(
    State(state): State<WebState>,
    Json(body): Json<CreateNoteBody>,
) -> Result<(StatusCode, Json<serde_json::Value>), (StatusCode, Json<ErrorResponse>)> {
    if body.content.trim().is_empty() {
        return Err((
            StatusCode::UNPROCESSABLE_ENTITY,
            Json(ErrorResponse { error: "content must not be empty".to_string() }),
        ));
    }
    let note = state
        .db
        .create_path_note(&body.path, &body.content)
        .await
        .map_err(internal_err)?;
    Ok((StatusCode::CREATED, Json(serde_json::json!({ "note": note }))))
}

#[derive(Deserialize)]
pub struct UpdateNoteBody {
    pub content: String,
}

pub async fn update_path_note(
    State(state): State<WebState>,
    Path(id): Path<String>,
    Json(body): Json<UpdateNoteBody>,
) -> Result<Json<serde_json::Value>, (StatusCode, Json<ErrorResponse>)> {
    if body.content.trim().is_empty() {
        return Err((
            StatusCode::UNPROCESSABLE_ENTITY,
            Json(ErrorResponse { error: "content must not be empty".to_string() }),
        ));
    }
    let note = state
        .db
        .update_path_note(&id, &body.content)
        .await
        .map_err(internal_err)?
        .ok_or_else(|| not_found("note not found"))?;
    Ok(Json(serde_json::json!({ "note": note })))
}

pub async fn delete_path_note(
    State(state): State<WebState>,
    Path(id): Path<String>,
) -> Result<StatusCode, (StatusCode, Json<ErrorResponse>)> {
    let deleted = state
        .db
        .delete_path_note(&id)
        .await
        .map_err(internal_err)?;
    if deleted {
        Ok(StatusCode::NO_CONTENT)
    } else {
        Err(not_found("note not found"))
    }
}