Skip to main content

mur_common/
zfs_protocol.rs

1use serde::{Deserialize, Serialize};
2use std::path::PathBuf;
3
4/// Request sent from mur-core (host) to mur-zfs-agent (inside VM).
5#[derive(Debug, Clone, Serialize, Deserialize)]
6#[serde(tag = "op", rename_all = "snake_case")]
7pub enum ZfsRequest {
8    CreateTrack { base: PathBuf, name: String },
9    Snapshot { track: PathBuf, label: String },
10    DiffFiles { track: PathBuf, since: String },
11    Promote { track: PathBuf, target: PathBuf },
12    Destroy { track: PathBuf },
13}
14
15/// Response sent from mur-zfs-agent back to mur-core.
16#[derive(Debug, Clone, Serialize, Deserialize)]
17#[serde(tag = "status", rename_all = "snake_case")]
18pub enum ZfsResponse {
19    /// CreateTrack succeeded — `path` is the mountpoint of the new clone.
20    Track { path: PathBuf },
21    /// Snapshot succeeded — `snap_id` is the full snapshot name (e.g. `pool/ds@label`).
22    Snap { snap_id: String },
23    /// DiffFiles succeeded — `paths` are repo-relative changed paths.
24    Files { paths: Vec<PathBuf> },
25    /// Operation succeeded with no output (Destroy).
26    Ok,
27    /// Operation failed — `message` is the error string.
28    Error { message: String },
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34
35    #[test]
36    fn create_track_roundtrip() {
37        let req = ZfsRequest::CreateTrack {
38            base: "/pool/data/project".into(),
39            name: "track-a".into(),
40        };
41        let json = serde_json::to_string(&req).unwrap();
42        let back: ZfsRequest = serde_json::from_str(&json).unwrap();
43        assert!(matches!(back, ZfsRequest::CreateTrack { .. }));
44    }
45
46    #[test]
47    fn error_response_roundtrip() {
48        let resp = ZfsResponse::Error {
49            message: "zfs: command not found".into(),
50        };
51        let json = serde_json::to_string(&resp).unwrap();
52        let back: ZfsResponse = serde_json::from_str(&json).unwrap();
53        assert!(matches!(back, ZfsResponse::Error { .. }));
54    }
55
56    #[test]
57    fn all_request_variants_serialize() {
58        let reqs = vec![
59            ZfsRequest::CreateTrack {
60                base: "/p".into(),
61                name: "t".into(),
62            },
63            ZfsRequest::Snapshot {
64                track: "/p/t".into(),
65                label: "base".into(),
66            },
67            ZfsRequest::DiffFiles {
68                track: "/p/t".into(),
69                since: "mur-base".into(),
70            },
71            ZfsRequest::Promote {
72                track: "/p/t".into(),
73                target: "/p/main".into(),
74            },
75            ZfsRequest::Destroy {
76                track: "/p/t".into(),
77            },
78        ];
79        for req in reqs {
80            let json = serde_json::to_string(&req).unwrap();
81            let _back: ZfsRequest = serde_json::from_str(&json).unwrap();
82        }
83    }
84}