use crate::keys::{DiscoPublic, NodePublic};
use crate::control::hostinfo::Hostinfo;
use crate::control::json::{JsonError, Writer};
pub const PATH: &str = "/machine/map";
pub const FRAME_HEADER_LEN: usize = 4;
pub struct MapRequest<'a> {
pub version: u16,
pub node_key: &'a NodePublic,
pub disco_key: &'a DiscoPublic,
pub hostinfo: Hostinfo<'a>,
pub stream: bool,
pub omit_peers: bool,
pub read_only: bool,
pub endpoints: &'a [&'a str],
}
impl MapRequest<'_> {
pub fn write<'o>(&self, out: &'o mut [u8]) -> Result<&'o [u8], JsonError> {
let mut node_text = [0u8; 128];
let node_key = self
.node_key
.encode(&mut node_text)
.map_err(|_| JsonError::Overflow)?;
let mut disco_text = [0u8; 128];
let disco_key = self
.disco_key
.encode(&mut disco_text)
.map_err(|_| JsonError::Overflow)?;
let mut writer = Writer::new(out);
writer
.begin_object()
.field_u64("Version", self.version as u64)
.field_str("NodeKey", node_key)
.field_str("DiscoKey", disco_key)
.field_bool("Stream", self.stream)
.field_bool("OmitPeers", self.omit_peers)
.field_bool("ReadOnly", self.read_only);
writer.key("Endpoints").begin_array();
for endpoint in self.endpoints {
writer.str(endpoint);
}
writer.end_array();
writer.key("Hostinfo");
self.hostinfo.write(&mut writer);
writer.end_object();
writer.finish()
}
}
pub fn frame_len(header: &[u8]) -> Option<usize> {
let bytes: [u8; FRAME_HEADER_LEN] = header.get(..FRAME_HEADER_LEN)?.try_into().ok()?;
Some(u32::from_le_bytes(bytes) as usize)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::keys::{DiscoPrivate, NodePrivate};
#[test]
fn the_request_does_not_ask_for_compression() {
let node = NodePrivate::from_bytes([0x11; 32]).public();
let disco = DiscoPrivate::from_bytes([0x22; 32]).public();
let mut buffer = [0u8; 1024];
let request = MapRequest {
version: 131,
node_key: &node,
disco_key: &disco,
hostinfo: Hostinfo::default(),
stream: false,
omit_peers: false,
read_only: false,
endpoints: &[],
};
let text = core::str::from_utf8(request.write(&mut buffer).unwrap()).unwrap();
assert!(!text.contains("Compress"), "{text}");
assert!(text.contains(r#""NodeKey":"nodekey:"#));
assert!(text.contains(r#""DiscoKey":"discokey:"#));
assert!(text.contains(r#""Stream":false"#));
assert!(text.contains(r#""Endpoints":[]"#));
}
#[test]
fn the_frame_length_is_little_endian() {
assert_eq!(frame_len(&[0x2c, 0x01, 0x00, 0x00]), Some(300));
assert_ne!(frame_len(&[0x00, 0x00, 0x01, 0x2c]), Some(300));
assert_eq!(frame_len(&[0x01, 0x02]), None);
}
}