use serde::Deserialize;
use serde::Serialize;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotTrackRequest {
pub files: Vec<String>,
#[serde(flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotTrackResponse {
#[serde(default)]
pub tracked: u32,
#[serde(flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotPatchRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message_id: Option<String>,
#[serde(default, flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FilePatch {
pub path: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub patch: Option<String>,
#[serde(default)]
pub added: bool,
#[serde(default)]
pub deleted: bool,
#[serde(default)]
pub modified: bool,
#[serde(flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotPatchResponse {
#[serde(default)]
pub patches: Vec<FilePatch>,
#[serde(flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotDiffRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message_id: Option<String>,
#[serde(default, flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileDiff {
pub path: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub original: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub modified: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<String>,
#[serde(flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotDiffResponse {
#[serde(default)]
pub diffs: Vec<FileDiff>,
#[serde(flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotRestoreRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message_id: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub files: Vec<String>,
#[serde(default, flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotRestoreResponse {
#[serde(default)]
pub restored: u32,
#[serde(flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotRevertRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub session_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message_id: Option<String>,
#[serde(default, flatten)]
pub extra: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SnapshotRevertResponse {
#[serde(default)]
pub success: bool,
#[serde(default)]
pub reverted: u32,
#[serde(flatten)]
pub extra: serde_json::Value,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_snapshot_track_request() {
let req = SnapshotTrackRequest {
files: vec!["src/main.rs".to_string(), "Cargo.toml".to_string()],
extra: serde_json::Value::Null,
};
let json = serde_json::to_string(&req).unwrap();
assert!(json.contains("src/main.rs"));
}
#[test]
fn test_file_patch() {
let json = r#"{
"path": "src/lib.rs",
"patch": "@@ -1,3 +1,4 @@\n+use serde;",
"modified": true
}"#;
let patch: FilePatch = serde_json::from_str(json).unwrap();
assert_eq!(patch.path, "src/lib.rs");
assert!(patch.modified);
assert!(!patch.added);
assert!(!patch.deleted);
}
#[test]
fn test_file_diff() {
let json = r##"{
"path": "README.md",
"original": "# Old Title",
"modified": "# New Title",
"status": "modified"
}"##;
let diff: FileDiff = serde_json::from_str(json).unwrap();
assert_eq!(diff.path, "README.md");
assert_eq!(diff.original, Some("# Old Title".to_string()));
assert_eq!(diff.modified, Some("# New Title".to_string()));
assert_eq!(diff.status, Some("modified".to_string()));
}
#[test]
fn test_snapshot_restore_response() {
let json = r#"{"restored": 5}"#;
let resp: SnapshotRestoreResponse = serde_json::from_str(json).unwrap();
assert_eq!(resp.restored, 5);
}
#[test]
fn test_snapshot_revert_response() {
let json = r#"{"success": true, "reverted": 3}"#;
let resp: SnapshotRevertResponse = serde_json::from_str(json).unwrap();
assert!(resp.success);
assert_eq!(resp.reverted, 3);
}
}