use serde::{Deserialize, Serialize};
pub const ALPN: &[u8] = b"PAI_SHO/1";
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Serialize, Deserialize)]
pub enum Request {
Invite {
key: Option<String>,
name: Option<String>,
expose: Vec<u16>,
},
Accept {
handle: String,
name: Option<String>,
},
Forget {
peer: String,
},
Expose {
port: u16,
to: Vec<String>,
all: bool,
},
Unexpose {
port: u16,
to: Option<String>,
},
List,
Key,
Project {
peer: String,
ip: Option<String>,
name: Option<String>,
},
Unproject {
peer: String,
},
}
#[derive(Debug, Serialize, Deserialize)]
pub enum Response {
Ok,
Key(String),
List(ListInfo),
Invite(String),
Error(String),
}
#[derive(Debug, Serialize, Deserialize)]
pub struct ListInfo {
pub me: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub cli: String,
#[serde(default)]
pub daemon: String,
pub peers: Vec<PeerInfo>,
pub i_expose: Vec<u16>,
pub grants: Vec<GrantInfo>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct GrantInfo {
pub port: u16,
pub to: String,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PeerInfo {
pub key: String,
pub name: Option<String>,
#[serde(default)]
pub version: Option<String>,
pub online: bool,
pub admission: String,
pub they_expose: Vec<u16>,
pub ip: Option<String>,
pub bound: Vec<u16>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum PeerMessage {
ExposedPorts(Vec<u16>),
Connect { port: u16 },
Enroll {
token: String,
#[serde(default)]
version: Option<String>,
},
Error(String),
}
impl PeerMessage {
pub fn enroll(token: impl Into<String>) -> Self {
Self::Enroll {
token: token.into(),
version: Some(VERSION.to_string()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn an_old_enroll_still_parses() {
let msg: PeerMessage = serde_json::from_str(r#"{"Enroll":{"token":"abc"}}"#).unwrap();
match msg {
PeerMessage::Enroll { token, version } => {
assert_eq!(token, "abc");
assert!(version.is_none());
}
other => panic!("expected Enroll, got {other:?}"),
}
}
#[test]
fn an_old_peer_still_parses_an_enroll_that_carries_version() {
#[derive(Deserialize)]
struct OldEnroll {
token: String,
}
#[derive(Deserialize)]
enum OldMessage {
Enroll(OldEnroll),
}
let json = serde_json::to_string(&PeerMessage::enroll("abc")).unwrap();
let old: OldMessage = serde_json::from_str(&json).unwrap();
match old {
OldMessage::Enroll(e) => assert_eq!(e.token, "abc"),
}
}
#[test]
fn an_old_list_has_an_empty_daemon_version() {
let info: ListInfo =
serde_json::from_str(r#"{"me":"abc","peers":[],"i_expose":[],"grants":[]}"#).unwrap();
assert!(info.cli.is_empty());
assert!(info.daemon.is_empty());
}
}