Skip to main content

tauri_plugin_nostr_sync/
models.rs

1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(rename_all = "camelCase")]
7pub struct PublishRequest {
8    pub category: String,
9    pub payload: Value,
10    /// NIP-40 expiration as a Unix timestamp (seconds). Relay support is the caller's responsibility.
11    pub expiration: Option<u64>,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15#[serde(rename_all = "camelCase")]
16pub struct FetchRequest {
17    pub category: String,
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21#[serde(rename_all = "camelCase")]
22pub struct FetchResult {
23    pub category: String,
24    pub payload: Value,
25    pub updated_at: DateTime<Utc>,
26    pub device_id: String,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
30#[serde(rename_all = "camelCase")]
31pub struct SyncAllRequest {
32    pub categories: Vec<String>,
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize)]
36#[serde(rename_all = "camelCase")]
37pub struct PollRequest {
38    pub categories: Vec<String>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42#[serde(rename_all = "camelCase")]
43pub struct SyncStatus {
44    pub ready: bool,
45    pub relay_count: usize,
46    pub connected_relay_count: usize,
47    pub device_id: String,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize)]
51#[serde(rename_all = "camelCase")]
52pub struct RelayInfo {
53    pub url: String,
54    pub connected: bool,
55    pub last_seen: Option<DateTime<Utc>>,
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61    use chrono::TimeZone;
62
63    #[test]
64    fn fetch_result_serializes_camel_case() {
65        let result = FetchResult {
66            category: "ui-settings".to_string(),
67            payload: serde_json::json!({ "x": 1 }),
68            updated_at: Utc.timestamp_opt(1_700_000_000, 0).unwrap(),
69            device_id: "abc".to_string(),
70        };
71        let json = serde_json::to_value(&result).unwrap();
72        assert!(json.get("category").is_some(), "expected category field");
73        assert!(
74            json.get("updatedAt").is_some(),
75            "expected camelCase updatedAt"
76        );
77        assert!(
78            json.get("deviceId").is_some(),
79            "expected camelCase deviceId"
80        );
81        assert!(json.get("updated_at").is_none());
82        assert!(json.get("device_id").is_none());
83    }
84
85    #[test]
86    fn sync_status_serializes_camel_case() {
87        let status = SyncStatus {
88            ready: false,
89            relay_count: 2,
90            connected_relay_count: 1,
91            device_id: "test-device".to_string(),
92        };
93        let json = serde_json::to_value(&status).unwrap();
94        assert!(json.get("relayCount").is_some());
95        assert!(json.get("connectedRelayCount").is_some());
96        assert!(json.get("relay_count").is_none());
97    }
98
99    #[test]
100    fn relay_info_serializes_camel_case() {
101        let info = RelayInfo {
102            url: "wss://relay.example".to_string(),
103            connected: true,
104            last_seen: None,
105        };
106        let json = serde_json::to_value(&info).unwrap();
107        assert!(json.get("lastSeen").is_some());
108        assert!(json.get("last_seen").is_none());
109    }
110
111    #[test]
112    fn sync_all_request_deserializes_categories() {
113        let json = r#"{"categories":["ui-settings","wallet"]}"#;
114        let req: SyncAllRequest = serde_json::from_str(json).unwrap();
115        assert_eq!(req.categories, vec!["ui-settings", "wallet"]);
116    }
117
118    #[test]
119    fn poll_request_deserializes_categories() {
120        let json = r#"{"categories":["ui-settings","wallet"]}"#;
121        let req: PollRequest = serde_json::from_str(json).unwrap();
122        assert_eq!(req.categories, vec!["ui-settings", "wallet"]);
123    }
124}