gitent_server/
api.rs

1use axum::{
2    extract::{Path, State},
3    http::StatusCode,
4    response::{IntoResponse, Json},
5    routing::{get, post},
6    Router,
7};
8use gitent_core::{Change, ChangeType, Commit, CommitInfo, Session, Storage};
9use serde::Deserialize;
10use std::sync::{Arc, Mutex};
11use uuid::Uuid;
12
13#[derive(Clone)]
14pub struct AppState {
15    pub storage: Arc<Mutex<Storage>>,
16}
17
18pub fn create_router(state: AppState) -> Router {
19    Router::new()
20        .route("/health", get(health_check))
21        .route("/session", get(get_active_session))
22        .route("/changes", get(get_uncommitted_changes))
23        .route("/changes", post(create_change))
24        .route("/commits", get(get_commits))
25        .route("/commits", post(create_commit))
26        .route("/commits/:id", get(get_commit))
27        .with_state(state)
28}
29
30async fn health_check() -> impl IntoResponse {
31    Json(serde_json::json!({"status": "ok"}))
32}
33
34async fn get_active_session(
35    State(state): State<AppState>,
36) -> Result<Json<Session>, (StatusCode, String)> {
37    let storage = state.storage.lock().unwrap();
38    storage
39        .get_active_session()
40        .map(Json)
41        .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))
42}
43
44async fn get_uncommitted_changes(
45    State(state): State<AppState>,
46) -> Result<Json<Vec<Change>>, (StatusCode, String)> {
47    let storage = state.storage.lock().unwrap();
48    let session = storage
49        .get_active_session()
50        .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
51
52    storage
53        .get_uncommitted_changes(&session.id)
54        .map(Json)
55        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
56}
57
58#[derive(Deserialize)]
59struct CreateChangeRequest {
60    change_type: String,
61    path: String,
62    content_before: Option<String>,
63    content_after: Option<String>,
64    agent_id: Option<String>,
65}
66
67async fn create_change(
68    State(state): State<AppState>,
69    Json(req): Json<CreateChangeRequest>,
70) -> Result<Json<Change>, (StatusCode, String)> {
71    let storage = state.storage.lock().unwrap();
72    let session = storage
73        .get_active_session()
74        .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
75
76    let change_type = ChangeType::parse(&req.change_type)
77        .ok_or_else(|| (StatusCode::BAD_REQUEST, "Invalid change type".to_string()))?;
78
79    let mut change = Change::new(change_type, std::path::PathBuf::from(req.path), session.id);
80
81    if let Some(content) = req.content_before {
82        change = change.with_content_before(content.into_bytes());
83    }
84
85    if let Some(content) = req.content_after {
86        change = change.with_content_after(content.into_bytes());
87    }
88
89    if let Some(agent_id) = req.agent_id {
90        change = change.with_agent_id(agent_id);
91    }
92
93    storage
94        .create_change(&change)
95        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
96
97    Ok(Json(change))
98}
99
100async fn get_commits(
101    State(state): State<AppState>,
102) -> Result<Json<Vec<CommitInfo>>, (StatusCode, String)> {
103    let storage = state.storage.lock().unwrap();
104    let session = storage
105        .get_active_session()
106        .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
107
108    storage
109        .get_commits_for_session(&session.id)
110        .map(Json)
111        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))
112}
113
114#[derive(Deserialize)]
115struct CreateCommitRequest {
116    message: String,
117    agent_id: String,
118    change_ids: Vec<String>,
119}
120
121async fn create_commit(
122    State(state): State<AppState>,
123    Json(req): Json<CreateCommitRequest>,
124) -> Result<Json<Commit>, (StatusCode, String)> {
125    let storage = state.storage.lock().unwrap();
126    let session = storage
127        .get_active_session()
128        .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))?;
129
130    let change_ids: Vec<Uuid> = req
131        .change_ids
132        .iter()
133        .filter_map(|id| Uuid::parse_str(id).ok())
134        .collect();
135
136    let commit = Commit::new(req.message, req.agent_id, change_ids, session.id);
137
138    storage
139        .create_commit(&commit)
140        .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
141
142    Ok(Json(commit))
143}
144
145async fn get_commit(
146    State(state): State<AppState>,
147    Path(id): Path<String>,
148) -> Result<Json<Commit>, (StatusCode, String)> {
149    let commit_id =
150        Uuid::parse_str(&id).map_err(|_| (StatusCode::BAD_REQUEST, "Invalid UUID".to_string()))?;
151
152    let storage = state.storage.lock().unwrap();
153    storage
154        .get_commit(&commit_id)
155        .map(Json)
156        .map_err(|e| (StatusCode::NOT_FOUND, e.to_string()))
157}