use super::extended::{extended_message, ExtendedMessage, ExtendedMessageSubtype};
const SUBTYPE_CONSOLE_VERSION: ExtendedMessageSubtype = 0xff30;
extended_message!(SUBTYPE_CONSOLE_VERSION,
pub struct ConsoleVersionRequest {
}
pub struct ConsoleVersionResponse {
pub update_available: bool,
pub versions: Vec<String>,
}
{
fn impl_frame_data_len(&self) -> usize {
0
}
fn impl_frame_data<W: std::io::Write>(&self, _dst: &mut W) -> Result<(), super::MessageError> {
Ok(())
}
fn from_frame_data(message_id: u8, data: Vec<u8>) -> Result<Self, super::MessageError>
where
Self: Sized,
{
if !data.is_empty() {
Err(MessageError::InvalidData)
} else {
Ok(Self { message_id })
}
}
}
{
fn impl_frame_data_len(&self) -> usize {
if self.versions.is_empty() {
2
} else {
self.versions.iter().fold(1, |a, s| a + s.len() + 1)
}
}
fn impl_frame_data<W: std::io::Write>(&self, dst: &mut W) -> Result<(), super::MessageError> {
let mut pre = [0u8; 2];
pre[0] = self.update_available.into();
pre[1] = (self.impl_frame_data_len() - 2).try_into()?;
dst.write_all(&pre)?;
let mut first = true;
for s in &self.versions {
if !first {
dst.write_all(",".as_bytes())?;
} else {
first = false;
}
dst.write_all(s.as_bytes())?;
}
Ok(())
}
fn from_frame_data(message_id: u8, data: Vec<u8>) -> Result<Self, super::MessageError>
{
if data.len() < 2 || data.len() != 2 + data[1] as usize {
return Err(MessageError::InvalidData);
}
let update_available = data[0] != 0;
let s = core::str::from_utf8(&data[2..])?;
let versions = s.split(',').map(|s| s.to_string()).collect();
Ok(Self {
message_id,
update_available,
versions,
})
}
});
impl ConsoleVersionRequest {
pub fn new() -> Self {
Self {
message_id: super::next_msg_id(),
}
}
}
impl ConsoleVersionResponse {
pub fn new<T: IntoIterator<Item: Into<String>>>(versions: T, update_available: bool) -> Self {
Self::with_message_id(super::next_msg_id(), versions, update_available)
}
pub fn with_message_id<T: IntoIterator<Item: Into<String>>>(
message_id: u8,
versions: T,
update_available: bool,
) -> Self {
Self {
message_id,
update_available,
versions: versions.into_iter().map(T::Item::into).collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::conn::tests::data::*;
#[test]
fn test_console_version_req() {
let orig = ConsoleVersionRequest::new();
let frame: Frame = orig.clone().try_into().expect("into frame failed");
let req: ConsoleVersionRequest = frame.try_into().expect("from frame failed");
assert_eq!(req, orig);
}
#[test]
fn test_console_version_req_from_data() {
let req: ConsoleVersionRequest = frame(MSG_REQ_CON_VERS)
.try_into()
.expect("from frame failed");
let f: Frame = req.try_into().expect("into frame failed");
assert_eq!(f, frame(MSG_REQ_CON_VERS));
}
#[test]
fn test_console_version_resp() {
let orig = ConsoleVersionResponse::new(vec!["1.0.3", "1.0.1"], true);
let frame: Frame = orig.clone().try_into().expect("into frame failed");
let resp: ConsoleVersionResponse = frame.try_into().expect("from frame failed");
assert_eq!(resp, orig);
}
#[test]
fn test_console_version_resp_from_data() {
let resp: ConsoleVersionResponse = frame(MSG_RESP_CON_VERS)
.try_into()
.expect("from frame failed");
assert!(!resp.update_available);
assert_eq!(resp.versions.len(), 2);
assert!(resp.versions.iter().all(|v| v == "1.0.3"));
let f: Frame = resp.try_into().expect("into frame failed");
assert_eq!(f, frame(MSG_RESP_CON_VERS));
}
}