Skip to main content

romm_api/endpoints/
sync.rs

1//! Save sync session endpoints (`/api/sync/*`).
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use super::Endpoint;
7
8/// One operation returned by `POST /api/sync/negotiate`.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SyncOperation {
11    pub action: String,
12    pub rom_id: u64,
13    pub save_id: Option<u64>,
14    pub file_name: String,
15    pub slot: Option<String>,
16    pub emulator: Option<String>,
17    pub reason: String,
18    pub server_updated_at: Option<String>,
19    pub server_content_hash: Option<String>,
20}
21
22/// `POST /api/sync/negotiate` response.
23#[derive(Debug, Clone, Serialize, Deserialize)]
24pub struct SyncNegotiateResponse {
25    pub session_id: u64,
26    pub operations: Vec<SyncOperation>,
27    pub total_upload: u64,
28    pub total_download: u64,
29    pub total_conflict: u64,
30    pub total_no_op: u64,
31}
32
33/// `GET /api/sync/sessions*` response row.
34#[derive(Debug, Clone, Serialize, Deserialize)]
35pub struct SyncSessionSchema {
36    pub id: u64,
37    pub device_id: String,
38    pub user_id: u64,
39    pub status: String,
40    pub initiated_at: String,
41    pub completed_at: Option<String>,
42    pub operations_planned: u64,
43    pub operations_completed: u64,
44    pub operations_failed: u64,
45    pub error_message: Option<String>,
46    pub created_at: String,
47    pub updated_at: String,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct SyncCompleteResponse {
52    pub session: SyncSessionSchema,
53    pub play_session_ingest: Option<Value>,
54}
55
56/// `POST /api/sync/negotiate`.
57#[derive(Debug, Clone)]
58pub struct NegotiateSync {
59    pub body: Value,
60}
61
62impl Endpoint for NegotiateSync {
63    type Output = SyncNegotiateResponse;
64
65    fn method(&self) -> &'static str {
66        "POST"
67    }
68
69    fn path(&self) -> String {
70        "/api/sync/negotiate".into()
71    }
72
73    fn body(&self) -> Option<Value> {
74        Some(self.body.clone())
75    }
76}
77
78/// `POST /api/sync/sessions/{session_id}/complete`.
79#[derive(Debug, Clone)]
80pub struct CompleteSyncSession {
81    pub session_id: u64,
82    pub body: Value,
83}
84
85impl Endpoint for CompleteSyncSession {
86    type Output = SyncCompleteResponse;
87
88    fn method(&self) -> &'static str {
89        "POST"
90    }
91
92    fn path(&self) -> String {
93        format!("/api/sync/sessions/{}/complete", self.session_id)
94    }
95
96    fn body(&self) -> Option<Value> {
97        Some(self.body.clone())
98    }
99}
100
101/// `GET /api/sync/sessions`.
102#[derive(Debug, Clone, Default)]
103pub struct ListSyncSessions {
104    pub device_id: Option<String>,
105    pub limit: Option<u32>,
106}
107
108impl Endpoint for ListSyncSessions {
109    type Output = Vec<SyncSessionSchema>;
110
111    fn method(&self) -> &'static str {
112        "GET"
113    }
114
115    fn path(&self) -> String {
116        "/api/sync/sessions".into()
117    }
118
119    fn query(&self) -> Vec<(String, String)> {
120        let mut out = Vec::new();
121        if let Some(ref device_id) = self.device_id {
122            if !device_id.is_empty() {
123                out.push(("device_id".into(), device_id.clone()));
124            }
125        }
126        if let Some(limit) = self.limit {
127            out.push(("limit".into(), limit.to_string()));
128        }
129        out
130    }
131}
132
133/// `GET /api/sync/sessions/{session_id}`.
134#[derive(Debug, Clone)]
135pub struct GetSyncSession {
136    pub session_id: u64,
137}
138
139impl Endpoint for GetSyncSession {
140    type Output = SyncSessionSchema;
141
142    fn method(&self) -> &'static str {
143        "GET"
144    }
145
146    fn path(&self) -> String {
147        format!("/api/sync/sessions/{}", self.session_id)
148    }
149}
150
151/// `POST /api/sync/devices/{device_id}/push-pull`.
152#[derive(Debug, Clone)]
153pub struct TriggerPushPull {
154    pub device_id: String,
155}
156
157impl Endpoint for TriggerPushPull {
158    type Output = SyncSessionSchema;
159
160    fn method(&self) -> &'static str {
161        "POST"
162    }
163
164    fn path(&self) -> String {
165        format!("/api/sync/devices/{}/push-pull", self.device_id)
166    }
167}