Skip to main content

romm_api/endpoints/
saves.rs

1//! Save asset endpoints (`/api/saves*`).
2
3use serde::{Deserialize, Serialize};
4use serde_json::{json, Value};
5
6use super::Endpoint;
7
8/// Minimal save schema used by sync command flows.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct SaveSchema {
11    pub id: u64,
12    pub rom_id: u64,
13    pub file_name: String,
14    pub emulator: Option<String>,
15    pub slot: Option<String>,
16    pub content_hash: Option<String>,
17    pub updated_at: String,
18}
19
20/// `GET /api/saves`.
21#[derive(Debug, Clone, Default)]
22pub struct ListSaves {
23    pub rom_id: Option<u64>,
24    pub device_id: Option<String>,
25    pub slot: Option<String>,
26}
27
28impl Endpoint for ListSaves {
29    type Output = Vec<SaveSchema>;
30
31    fn method(&self) -> &'static str {
32        "GET"
33    }
34
35    fn path(&self) -> String {
36        "/api/saves".into()
37    }
38
39    fn query(&self) -> Vec<(String, String)> {
40        let mut out = Vec::new();
41        if let Some(rom_id) = self.rom_id {
42            out.push(("rom_id".into(), rom_id.to_string()));
43        }
44        if let Some(ref device_id) = self.device_id {
45            if !device_id.is_empty() {
46                out.push(("device_id".into(), device_id.clone()));
47            }
48        }
49        if let Some(ref slot) = self.slot {
50            if !slot.is_empty() {
51                out.push(("slot".into(), slot.clone()));
52            }
53        }
54        out
55    }
56}
57
58/// `GET /api/saves/{id}`.
59#[derive(Debug, Clone)]
60pub struct GetSave {
61    pub id: u64,
62    pub device_id: Option<String>,
63}
64
65impl Endpoint for GetSave {
66    type Output = SaveSchema;
67
68    fn method(&self) -> &'static str {
69        "GET"
70    }
71
72    fn path(&self) -> String {
73        format!("/api/saves/{}", self.id)
74    }
75
76    fn query(&self) -> Vec<(String, String)> {
77        let mut out = Vec::new();
78        if let Some(ref device_id) = self.device_id {
79            if !device_id.is_empty() {
80                out.push(("device_id".into(), device_id.clone()));
81            }
82        }
83        out
84    }
85}
86
87/// `POST /api/saves/{id}/downloaded`.
88#[derive(Debug, Clone)]
89pub struct ConfirmSaveDownloaded {
90    pub id: u64,
91    pub device_id: String,
92}
93
94impl Endpoint for ConfirmSaveDownloaded {
95    type Output = SaveSchema;
96
97    fn method(&self) -> &'static str {
98        "POST"
99    }
100
101    fn path(&self) -> String {
102        format!("/api/saves/{}/downloaded", self.id)
103    }
104
105    fn body(&self) -> Option<Value> {
106        Some(json!({ "device_id": self.device_id }))
107    }
108}
109
110/// `POST /api/saves/{id}/track`.
111#[derive(Debug, Clone)]
112pub struct TrackSave {
113    pub id: u64,
114    pub device_id: String,
115}
116
117impl Endpoint for TrackSave {
118    type Output = SaveSchema;
119
120    fn method(&self) -> &'static str {
121        "POST"
122    }
123
124    fn path(&self) -> String {
125        format!("/api/saves/{}/track", self.id)
126    }
127
128    fn body(&self) -> Option<Value> {
129        Some(json!({ "device_id": self.device_id }))
130    }
131}
132
133/// `POST /api/saves/{id}/untrack`.
134#[derive(Debug, Clone)]
135pub struct UntrackSave {
136    pub id: u64,
137    pub device_id: String,
138}
139
140impl Endpoint for UntrackSave {
141    type Output = SaveSchema;
142
143    fn method(&self) -> &'static str {
144        "POST"
145    }
146
147    fn path(&self) -> String {
148        format!("/api/saves/{}/untrack", self.id)
149    }
150
151    fn body(&self) -> Option<Value> {
152        Some(json!({ "device_id": self.device_id }))
153    }
154}