use serde::{Deserialize, Serialize};
pub const PROTOCOL_VERSION: u32 = 1;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum DeviceMessage {
Enroll {
enroll_token: String,
version: u32,
#[serde(default, skip_serializing_if = "Option::is_none")]
label: Option<String>,
},
Attach {
device_id: String,
enroll_token: String,
},
Heartbeat,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum RelayMessage {
Enrolled {
device_id: String,
public_url: String,
},
Rejected {
code: String,
message: String,
},
HeartbeatAck,
OpenData {
count: usize,
},
}
pub mod reject {
pub const BAD_TOKEN: &str = "bad-token";
pub const UNSUPPORTED_VERSION: &str = "unsupported-version";
pub const BAD_HANDSHAKE: &str = "bad-handshake";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn enroll_roundtrips() {
let msg = DeviceMessage::Enroll {
enroll_token: "secret".into(),
version: PROTOCOL_VERSION,
label: Some("build-box".into()),
};
let json = serde_json::to_string(&msg).unwrap();
assert!(json.contains("\"type\":\"enroll\""));
assert_eq!(serde_json::from_str::<DeviceMessage>(&json).unwrap(), msg);
}
#[test]
fn label_is_optional() {
let json = r#"{"type":"enroll","enroll_token":"s","version":1}"#;
let msg: DeviceMessage = serde_json::from_str(json).unwrap();
assert_eq!(
msg,
DeviceMessage::Enroll {
enroll_token: "s".into(),
version: 1,
label: None
}
);
}
#[test]
fn relay_messages_roundtrip() {
for msg in [
RelayMessage::Enrolled {
device_id: "d-1".into(),
public_url: "https://relay.example/d/d-1".into(),
},
RelayMessage::Rejected {
code: reject::BAD_TOKEN.into(),
message: "no".into(),
},
RelayMessage::HeartbeatAck,
RelayMessage::OpenData { count: 4 },
] {
let json = serde_json::to_string(&msg).unwrap();
assert_eq!(serde_json::from_str::<RelayMessage>(&json).unwrap(), msg);
}
}
#[test]
fn attach_roundtrips() {
let msg = DeviceMessage::Attach {
device_id: "dev-1".into(),
enroll_token: "secret".into(),
};
let json = serde_json::to_string(&msg).unwrap();
assert!(json.contains("\"type\":\"attach\""));
assert_eq!(serde_json::from_str::<DeviceMessage>(&json).unwrap(), msg);
}
#[test]
fn unknown_message_types_are_rejected() {
assert!(serde_json::from_str::<DeviceMessage>(r#"{"type":"teleport"}"#).is_err());
}
}