refactor: breaking refactor of domo_proto and domo_node
This commit is contained in:
parent
be4425aff9
commit
15971aa4fa
31 changed files with 902 additions and 393 deletions
2
domo_proto/Cargo.lock
generated
2
domo_proto/Cargo.lock
generated
|
@ -19,7 +19,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "domo_proto"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"rand",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "domo_proto"
|
||||
version = "0.1.0"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
|
|
@ -1,2 +1,8 @@
|
|||
use crate::packet::packet_data::PacketData;
|
||||
|
||||
pub mod node_management;
|
||||
pub mod property_control;
|
||||
|
||||
impl_into_enum_variant!(PacketData::NodeManagement, node_management::NodeManagementCommand);
|
||||
impl_into_enum_variant!(PacketData::PropertyControl, property_control::PropertyControlCommand);
|
||||
impl_into_enum_variant!(PacketData::Raw, Vec<u8>);
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
use crate::packet::identifier::Identifier;
|
||||
use crate::packet::{Packet, ToPacket};
|
||||
use crate::packet::packet_data::PacketData;
|
||||
|
||||
pub enum NodeManagement {
|
||||
Ping,
|
||||
RegisterNode { device_id: Identifier },
|
||||
RemoveNode,
|
||||
RegisterProperty {
|
||||
property_name: String,
|
||||
data_type: u8,
|
||||
read_only: bool,
|
||||
},
|
||||
RemoveProperty {
|
||||
property_name: String,
|
||||
},
|
||||
Error {
|
||||
error_code: u8,
|
||||
metadata: [u8; 0xFFFF],
|
||||
},
|
||||
}
|
||||
|
||||
impl NodeManagement {
|
||||
fn get_command(&self) -> u8 {
|
||||
match self {
|
||||
NodeManagement::Ping => 0x00,
|
||||
NodeManagement::RegisterNode { .. } => 0x01,
|
||||
NodeManagement::RemoveNode => 0x02,
|
||||
NodeManagement::RegisterProperty { .. } => 0x03,
|
||||
NodeManagement::RemoveProperty { .. } => 0x04,
|
||||
NodeManagement::Error { .. } => 0x0E
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<NodeManagement> for Packet {
|
||||
fn into(self) -> NodeManagement {
|
||||
match self.command {
|
||||
0x00 => NodeManagement::Ping,
|
||||
0x01 => NodeManagement::RegisterNode { device_id: self.src },
|
||||
0x02 => NodeManagement::RemoveNode,
|
||||
0x03 => NodeManagement::RegisterProperty {
|
||||
property_name: String::from_utf8(self.data.data[..32].to_vec()).unwrap(),
|
||||
data_type: self.data.data[33],
|
||||
read_only: self.data.data[34] != 0,
|
||||
},
|
||||
0x04 => NodeManagement::RemoveProperty {
|
||||
property_name: String::from_utf8(self.data.data[..32].to_vec()).unwrap()
|
||||
},
|
||||
0x0E => NodeManagement::Error {
|
||||
error_code: self.data.data[0],
|
||||
metadata: {
|
||||
let mut metadata = [0_u8; 0xFFFF];
|
||||
let data_slice = self.data.data;
|
||||
let metadata_length = u16::from_be_bytes([data_slice[1], data_slice[2]]) as usize;
|
||||
if metadata_length <= 0xFFFF {
|
||||
metadata[..metadata_length].copy_from_slice(&data_slice[3..(3 + metadata_length)]);
|
||||
}
|
||||
metadata
|
||||
},
|
||||
},
|
||||
_ => unreachable!("Invalid command in Packet::V1"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl Into<PacketData> for NodeManagement {
|
||||
fn into(self) -> PacketData {
|
||||
match self {
|
||||
NodeManagement::Ping => PacketData::default(),
|
||||
NodeManagement::RegisterNode { device_id } => PacketData::new(Vec::from(device_id.bytes)),
|
||||
NodeManagement::RemoveNode => PacketData::default(),
|
||||
NodeManagement::RegisterProperty { property_name, data_type, read_only } => PacketData::new({
|
||||
let mut vec = vec![];
|
||||
vec.extend(property_name.into_bytes());
|
||||
vec.push(data_type);
|
||||
vec.push(read_only as u8);
|
||||
vec
|
||||
}),
|
||||
NodeManagement::RemoveProperty { property_name } => PacketData::new(property_name.into_bytes()),
|
||||
NodeManagement::Error { error_code, metadata } => PacketData::new({
|
||||
let mut vec = vec![];
|
||||
vec.push(error_code);
|
||||
vec.extend_from_slice(metadata.as_ref());
|
||||
vec
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToPacket for NodeManagement {
|
||||
fn to_packet(self, src: Identifier, dest: Identifier, packet_id: Identifier, reply_to: Identifier) -> Packet {
|
||||
Packet {
|
||||
src,
|
||||
dest,
|
||||
packet_id,
|
||||
reply_to,
|
||||
command: self.get_command(),
|
||||
data: self.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum NodeManagementError {
|
||||
net_duplicate_packet,
|
||||
net_broken_packet,
|
||||
net_invalid_packet,
|
||||
|
||||
errc_not_registered,
|
||||
|
||||
errs_not_delivered,
|
||||
}
|
||||
|
||||
impl NodeManagementError {
|
||||
pub fn error_code(&self) -> u8 {
|
||||
match self {
|
||||
NodeManagementError::net_duplicate_packet => 0x00,
|
||||
NodeManagementError::net_broken_packet => 0x01,
|
||||
NodeManagementError::net_invalid_packet => 0x02,
|
||||
NodeManagementError::errc_not_registered => 0x10,
|
||||
NodeManagementError::errs_not_delivered => 0x20
|
||||
}
|
||||
}
|
||||
}
|
37
domo_proto/src/commands/node_management/errors.rs
Normal file
37
domo_proto/src/commands/node_management/errors.rs
Normal file
|
@ -0,0 +1,37 @@
|
|||
use std::io;
|
||||
|
||||
#[derive(Debug, PartialOrd, PartialEq)]
|
||||
#[allow(non_camel_case_types)]
|
||||
pub enum NodeManagementError {
|
||||
net_duplicate_packet = 0x00,
|
||||
net_broken_packet = 0x01,
|
||||
net_invalid_packet = 0x02,
|
||||
net_addr_in_use = 0x03,
|
||||
net_dest_unreachable = 0x04,
|
||||
|
||||
errc_not_registered = 0x10,
|
||||
|
||||
errs_not_delivered = 0x20,
|
||||
}
|
||||
|
||||
impl Into<u8> for NodeManagementError {
|
||||
fn into(self) -> u8 {
|
||||
self as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<u8> for NodeManagementError {
|
||||
type Error = io::Error;
|
||||
fn try_from(value: u8) -> io::Result<Self> {
|
||||
match value {
|
||||
0x00 => Ok(NodeManagementError::net_duplicate_packet),
|
||||
0x01 => Ok(NodeManagementError::net_broken_packet),
|
||||
0x02 => Ok(NodeManagementError::net_invalid_packet),
|
||||
0x03 => Ok(NodeManagementError::net_addr_in_use),
|
||||
0x04 => Ok(NodeManagementError::net_dest_unreachable),
|
||||
0x10 => Ok(NodeManagementError::errc_not_registered),
|
||||
0x20 => Ok(NodeManagementError::errs_not_delivered),
|
||||
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "non supported error"))
|
||||
}
|
||||
}
|
||||
}
|
100
domo_proto/src/commands/node_management/mod.rs
Normal file
100
domo_proto/src/commands/node_management/mod.rs
Normal file
|
@ -0,0 +1,100 @@
|
|||
use std::io;
|
||||
use crate::identifier::Identifier;
|
||||
use crate::packet::raw_packet::RawPacket;
|
||||
use crate::packet::{Packet, ToPacket};
|
||||
use crate::packet::packet_data::PacketData;
|
||||
|
||||
pub mod vec;
|
||||
pub mod errors;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum NodeManagementCommand {
|
||||
Ping,
|
||||
RegisterNode { device_id: Identifier },
|
||||
RemoveNode,
|
||||
RegisterProperty {
|
||||
property_name: String,
|
||||
data_type: u8,
|
||||
read_only: bool,
|
||||
},
|
||||
RemoveProperty {
|
||||
property_name: String,
|
||||
},
|
||||
Error {
|
||||
error_code: u8,
|
||||
metadata: Vec<u8>,
|
||||
},
|
||||
}
|
||||
|
||||
impl NodeManagementCommand {
|
||||
pub fn command(&self) -> u8 {
|
||||
match self {
|
||||
NodeManagementCommand::Ping => 0x00,
|
||||
NodeManagementCommand::RegisterNode { .. } => 0x01,
|
||||
NodeManagementCommand::RemoveNode => 0x02,
|
||||
NodeManagementCommand::RegisterProperty { .. } => 0x03,
|
||||
NodeManagementCommand::RemoveProperty { .. } => 0x04,
|
||||
NodeManagementCommand::Error { .. } => 0x0E
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToPacket for NodeManagementCommand {
|
||||
fn to_packet(self, src: Identifier, dest: Identifier, packet_id: Identifier, reply_to: Identifier) -> Packet {
|
||||
Packet {
|
||||
src, dest, packet_id, reply_to,
|
||||
command: self.command(),
|
||||
data: PacketData::NodeManagement(self.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<RawPacket> for NodeManagementCommand {
|
||||
type Error = io::Error;
|
||||
fn try_from(raw_packet: RawPacket) -> io::Result<Self> {
|
||||
let build_invalid_input_err = |r: &str| {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, r))
|
||||
};
|
||||
match raw_packet.command {
|
||||
0x00 => Ok(NodeManagementCommand::Ping),
|
||||
0x01 => if raw_packet.data_length == 4 {
|
||||
Ok(NodeManagementCommand::RegisterNode {
|
||||
device_id: Identifier::from(raw_packet.data)
|
||||
})
|
||||
} else {
|
||||
build_invalid_input_err("expected 4 bytes")
|
||||
},
|
||||
0x02 => Ok(NodeManagementCommand::RemoveNode),
|
||||
0x03 => if raw_packet.data_length == 34 {
|
||||
Ok(NodeManagementCommand::RegisterProperty {
|
||||
property_name: String::from_utf8(raw_packet.data[..32].to_vec())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?,
|
||||
data_type: raw_packet.data[33],
|
||||
read_only: raw_packet.data[34] != 0,
|
||||
})
|
||||
} else {
|
||||
build_invalid_input_err("expected 34 bytes")
|
||||
},
|
||||
0x04 => if raw_packet.data_length == 32 {
|
||||
Ok(NodeManagementCommand::RemoveProperty {
|
||||
property_name: String::from_utf8(raw_packet.data.to_vec())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?
|
||||
})
|
||||
} else {
|
||||
build_invalid_input_err("expected 32 bytes")
|
||||
},
|
||||
0x0E => if raw_packet.data_length == 3 {
|
||||
Ok(NodeManagementCommand::Error {
|
||||
error_code: raw_packet.data[0],
|
||||
metadata: {
|
||||
let metadata_length = u16::from_be_bytes([raw_packet.data[1], raw_packet.data[2]]) as usize;
|
||||
raw_packet.data[3..metadata_length].to_vec()
|
||||
},
|
||||
})
|
||||
} else {
|
||||
build_invalid_input_err("expected 3 bytes")
|
||||
},
|
||||
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "command group is unsupported"))
|
||||
}
|
||||
}
|
||||
}
|
26
domo_proto/src/commands/node_management/vec.rs
Normal file
26
domo_proto/src/commands/node_management/vec.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use crate::commands::node_management::NodeManagementCommand;
|
||||
|
||||
/// Returns a NodeManagement command.
|
||||
impl Into<Vec<u8>> for NodeManagementCommand {
|
||||
fn into(self) -> Vec<u8> {
|
||||
match self {
|
||||
NodeManagementCommand::Ping => vec![],
|
||||
NodeManagementCommand::RegisterNode { device_id } => device_id.bytes.to_vec(),
|
||||
NodeManagementCommand::RemoveNode => vec![],
|
||||
NodeManagementCommand::RegisterProperty { property_name, data_type, read_only } => {
|
||||
let mut vec = vec![];
|
||||
vec.extend(property_name.into_bytes());
|
||||
vec.push(data_type);
|
||||
vec.push(read_only as u8);
|
||||
vec
|
||||
},
|
||||
NodeManagementCommand::RemoveProperty { property_name } => property_name.into_bytes(),
|
||||
NodeManagementCommand::Error { error_code, metadata } => {
|
||||
let mut vec = vec![];
|
||||
vec.push(error_code);
|
||||
vec.extend(metadata);
|
||||
vec
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
use crate::packet::data_types::DataType;
|
||||
|
||||
pub enum PropertyControl {
|
||||
Get {
|
||||
property_name: String,
|
||||
data: DataType
|
||||
},
|
||||
Set {
|
||||
property_name: String,
|
||||
data: DataType
|
||||
},
|
||||
Reset {
|
||||
property_name: String,
|
||||
}
|
||||
}
|
118
domo_proto/src/commands/property_control/mod.rs
Normal file
118
domo_proto/src/commands/property_control/mod.rs
Normal file
|
@ -0,0 +1,118 @@
|
|||
use std::io;
|
||||
use crate::data_types::DataType;
|
||||
use crate::identifier::Identifier;
|
||||
use crate::packet::raw_packet::RawPacket;
|
||||
use crate::packet::{Packet, ToPacket};
|
||||
use crate::packet::packet_data::PacketData;
|
||||
use crate::prelude::as_u32_be;
|
||||
|
||||
pub mod vec;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PropertyControlCommand {
|
||||
Get {
|
||||
property_name: String,
|
||||
data: DataType,
|
||||
},
|
||||
Set {
|
||||
property_name: String,
|
||||
data: DataType,
|
||||
},
|
||||
Reset {
|
||||
property_name: String,
|
||||
},
|
||||
Subscribe {
|
||||
device_id: Identifier,
|
||||
property_name: String,
|
||||
},
|
||||
Unsubscribe {
|
||||
device_id: Identifier,
|
||||
property_name: String,
|
||||
},
|
||||
}
|
||||
|
||||
impl PropertyControlCommand {
|
||||
pub fn command(&self) -> u8 {
|
||||
match self {
|
||||
PropertyControlCommand::Get { .. } => 0x10,
|
||||
PropertyControlCommand::Set { .. } => 0x11,
|
||||
PropertyControlCommand::Reset { .. } => 0x12,
|
||||
PropertyControlCommand::Subscribe { .. } => 0x1A,
|
||||
PropertyControlCommand::Unsubscribe { .. } => 0x1B
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToPacket for PropertyControlCommand {
|
||||
fn to_packet(self, src: Identifier, dest: Identifier, packet_id: Identifier, reply_to: Identifier) -> Packet {
|
||||
Packet {
|
||||
src, dest, packet_id, reply_to,
|
||||
command: self.command(),
|
||||
data: PacketData::PropertyControl(self.clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<RawPacket> for PropertyControlCommand {
|
||||
type Error = io::Error;
|
||||
fn try_from(raw_packet: RawPacket) -> io::Result<Self> {
|
||||
let build_invalid_input_err = |r: &str| {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidInput, r))
|
||||
};
|
||||
match raw_packet.command {
|
||||
0x10 => if raw_packet.data_length == 33 {
|
||||
Ok(PropertyControlCommand::Get {
|
||||
property_name: String::from_utf8(raw_packet.data[..0x34].to_vec())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?,
|
||||
data: DataType::try_from(raw_packet.data[0x34..].to_vec())?,
|
||||
})
|
||||
} else {
|
||||
build_invalid_input_err("expected 33 bytes")
|
||||
},
|
||||
0x11 => {
|
||||
if raw_packet.data_length == 33 {
|
||||
Ok(PropertyControlCommand::Set {
|
||||
property_name: String::from_utf8(raw_packet.data[..0x34].to_vec())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?,
|
||||
data: DataType::try_from(raw_packet.data[0x34..].to_vec())?,
|
||||
})
|
||||
} else {
|
||||
build_invalid_input_err("expected 33 bytes")
|
||||
}
|
||||
}
|
||||
0x12 => {
|
||||
if raw_packet.data_length == 33 {
|
||||
Ok(PropertyControlCommand::Reset {
|
||||
property_name: String::from_utf8(raw_packet.data[..0x34].to_vec())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?,
|
||||
})
|
||||
} else {
|
||||
build_invalid_input_err("expected 33 bytes")
|
||||
}
|
||||
}
|
||||
0x1A => {
|
||||
if raw_packet.data_length == 36 {
|
||||
Ok(PropertyControlCommand::Subscribe {
|
||||
device_id: Identifier::from(as_u32_be(raw_packet.data[0x14..0x18].as_ref())),
|
||||
property_name: String::from_utf8(raw_packet.data[0x18..0x4C].to_vec())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?,
|
||||
})
|
||||
} else {
|
||||
build_invalid_input_err("expected 36 bytes")
|
||||
}
|
||||
}
|
||||
0x1B => {
|
||||
if raw_packet.data_length == 36 {
|
||||
Ok(PropertyControlCommand::Unsubscribe {
|
||||
device_id: Identifier::from(as_u32_be(raw_packet.data[0x14..0x18].as_ref())),
|
||||
property_name: String::from_utf8(raw_packet.data[0x18..0x4C].to_vec())
|
||||
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "String is not UTF-8"))?,
|
||||
})
|
||||
} else {
|
||||
build_invalid_input_err("expected 36 bytes")
|
||||
}
|
||||
}
|
||||
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "command group is unsupported"))
|
||||
}
|
||||
}
|
||||
}
|
34
domo_proto/src/commands/property_control/vec.rs
Normal file
34
domo_proto/src/commands/property_control/vec.rs
Normal file
|
@ -0,0 +1,34 @@
|
|||
use crate::commands::property_control::PropertyControlCommand;
|
||||
|
||||
/// Returns a PropertyControl command.
|
||||
impl Into<Vec<u8>> for PropertyControlCommand {
|
||||
fn into(self) -> Vec<u8> {
|
||||
match self {
|
||||
PropertyControlCommand::Get { property_name, data } => {
|
||||
let mut v = vec![];
|
||||
v.extend(property_name.into_bytes());
|
||||
v.extend(Into::<Vec<u8>>::into(data));
|
||||
v
|
||||
},
|
||||
PropertyControlCommand::Set { property_name, data } => {
|
||||
let mut v = vec![];
|
||||
v.extend(property_name.into_bytes());
|
||||
v.extend(Into::<Vec<u8>>::into(data));
|
||||
v
|
||||
},
|
||||
PropertyControlCommand::Reset { property_name } => property_name.into_bytes(),
|
||||
PropertyControlCommand::Subscribe { device_id, property_name } => {
|
||||
let mut v = vec![];
|
||||
v.extend(device_id.bytes);
|
||||
v.extend(property_name.into_bytes());
|
||||
v
|
||||
},
|
||||
PropertyControlCommand::Unsubscribe { device_id, property_name } => {
|
||||
let mut v = vec![];
|
||||
v.extend(device_id.bytes);
|
||||
v.extend(property_name.into_bytes());
|
||||
v
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
84
domo_proto/src/data_types/mod.rs
Normal file
84
domo_proto/src/data_types/mod.rs
Normal file
|
@ -0,0 +1,84 @@
|
|||
use crate::identifier::Identifier;
|
||||
|
||||
mod vec;
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
pub enum DataType {
|
||||
// Meta
|
||||
/// Meta type: Nothing (`0x00`)
|
||||
Nothing,
|
||||
/// Meta type: Array (`0x01`)
|
||||
Array(Vec<DataType>),
|
||||
|
||||
// Basic types
|
||||
/// Basic Type: Switch (`0x10`]
|
||||
Switch(bool),
|
||||
/// Basic Type: Slider (`0x11`)
|
||||
Slider(u8),
|
||||
/// Basic Type: Text (`0x12`)
|
||||
Text(String),
|
||||
|
||||
// Cosmetic
|
||||
/// Cosmetic Type: RGB (`0x20`)
|
||||
RGB(u8, u8, u8),
|
||||
|
||||
// Time & Space
|
||||
/// Time & Space Type: Seconds (`0x90`)
|
||||
Seconds(u64),
|
||||
|
||||
// Domo Types
|
||||
/// Domo type: Node Ref (`0xF0`)
|
||||
NodeRef(Identifier),
|
||||
}
|
||||
|
||||
impl DataType {
|
||||
pub fn get_data_size(data_type: u8) -> usize {
|
||||
match data_type {
|
||||
0x00 => 0,
|
||||
0x01 => {
|
||||
2
|
||||
},
|
||||
0x10 => 1,
|
||||
0x11 => 1,
|
||||
0x12 => 256,
|
||||
0x20 => 3,
|
||||
0x90 => 8,
|
||||
0xF0 => 4,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a vector of DataTypes.
|
||||
/// Do **not** put a raw packet in here, **only** a raw datatype vector.
|
||||
/// Unparsable data automatically gets turned into DataType::Nothing
|
||||
pub fn get_data_types(raw: Vec<u8>) -> Vec<DataType> {
|
||||
let mut data = vec![];
|
||||
let mut pieces = vec![];
|
||||
let mut byte = raw.iter().peekable();
|
||||
|
||||
while let Some(&c) = byte.next() {
|
||||
let mut piece = vec![c];
|
||||
let mut len = DataType::get_data_size(c);
|
||||
while len > 0 {
|
||||
if let Some(b) = byte.next() {
|
||||
piece.push(*b);
|
||||
}
|
||||
len -= 1;
|
||||
}
|
||||
if piece.len() == DataType::get_data_size(c) + 1 {
|
||||
pieces.push(piece);
|
||||
} else {
|
||||
pieces.push(vec![0x00]);
|
||||
}
|
||||
}
|
||||
|
||||
for piece in pieces {
|
||||
data.push(
|
||||
DataType::try_from(piece)
|
||||
.unwrap_or(DataType::Nothing)
|
||||
)
|
||||
}
|
||||
|
||||
data
|
||||
}
|
81
domo_proto/src/data_types/vec.rs
Normal file
81
domo_proto/src/data_types/vec.rs
Normal file
|
@ -0,0 +1,81 @@
|
|||
use std::io;
|
||||
use crate::data_types::{DataType, get_data_types};
|
||||
use crate::identifier::Identifier;
|
||||
use crate::prelude::as_u64_be;
|
||||
|
||||
impl Into<Vec<u8>> for DataType {
|
||||
fn into(self) -> Vec<u8> {
|
||||
match self {
|
||||
DataType::Nothing => vec![0x00],
|
||||
DataType::Array(vec) => {
|
||||
let mut res = vec![0x01];
|
||||
for item in vec {
|
||||
res.extend(Into::<Vec<u8>>::into(item).iter());
|
||||
}
|
||||
res
|
||||
},
|
||||
DataType::Switch(b) => vec![0x10, b as u8],
|
||||
DataType::Slider(v) => vec![0x11, v],
|
||||
DataType::Text(s) => {
|
||||
let mut bytes = vec![0x12];
|
||||
bytes.extend(s.into_bytes());
|
||||
bytes
|
||||
},
|
||||
DataType::RGB(r, g, b) => vec![0x20, r, g, b],
|
||||
DataType::Seconds(s) => {
|
||||
let mut bytes = vec![0x90];
|
||||
bytes.extend_from_slice(s.to_be_bytes().as_ref());
|
||||
bytes
|
||||
},
|
||||
DataType::NodeRef(id) => {
|
||||
let mut bytes = vec![0xF0];
|
||||
bytes.extend_from_slice(id.bytes.as_ref());
|
||||
bytes
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_data_type {
|
||||
($v: ident, $len: literal, $($t:tt)*) => {
|
||||
if $v.len() - 1 == $len {
|
||||
Ok($($t)*)
|
||||
} else {
|
||||
Err(io::Error::new(io::ErrorKind::InvalidData, format!("Expected data length {}, actual {}", $len, $v.len())))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Turn a `Vec<u8>` into a single data type.
|
||||
/// Returns Datatype::Nothing if it's nothing or can find nothing.
|
||||
impl TryFrom<Vec<u8>> for DataType {
|
||||
type Error = io::Error;
|
||||
fn try_from(value: Vec<u8>) -> io::Result<Self> {
|
||||
if value.len() == 0 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "data length == 0"))
|
||||
};
|
||||
match value[0] {
|
||||
0x01 => {
|
||||
if value.len() - 1 < 2 { return Err(io::Error::new(io::ErrorKind::InvalidInput, "The array DataType requires a 2 byte header.")) }
|
||||
let size = ((value[1] as u16) << 8) | value[2] as u16;
|
||||
if value.len() - 1 < size as usize { return Err(io::Error::new(io::ErrorKind::InvalidData, "Array specified data length != array data length")) }
|
||||
Ok(DataType::Array(get_data_types(value[3..(size as usize)].to_vec())))
|
||||
},
|
||||
0x10 => impl_data_type!(value, 1, DataType::Switch(value[1] != 0)),
|
||||
0x11 => impl_data_type!(value, 1, DataType::Slider(value[1])),
|
||||
0x12 => impl_data_type!(
|
||||
value,
|
||||
256,
|
||||
if let Ok(s) = String::from_utf8(value[1..257].to_vec()) {
|
||||
DataType::Text(s)
|
||||
} else {
|
||||
DataType::Nothing
|
||||
}
|
||||
),
|
||||
0x20 => impl_data_type!(value, 3, DataType::RGB(value[1], value[2], value[3])),
|
||||
0x90 => impl_data_type!(value, 8, DataType::Seconds(as_u64_be(&value[1..9]))),
|
||||
0xF0 => impl_data_type!(value, 4, DataType::NodeRef(Identifier::from(&value[1..5]))),
|
||||
_ => Err(io::Error::new(io::ErrorKind::InvalidData, "Could not match data type header to a data type.")),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
use std::fmt::{Display, Formatter, LowerHex};
|
||||
use rand::random;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
|
||||
/// Helper struct for the identifiers used in `dest`, `src`, `packet_id` and `reply_to`.
|
||||
#[derive(Debug, Eq, PartialEq, Copy, Clone, Hash)]
|
||||
pub struct Identifier {
|
||||
pub bytes: [u8; 4],
|
||||
}
|
||||
|
@ -66,3 +67,9 @@ impl From<u32> for Identifier {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<u32> for Identifier {
|
||||
fn into(self) -> u32 {
|
||||
u32::from_be_bytes(self.bytes)
|
||||
}
|
||||
}
|
|
@ -1,19 +1,39 @@
|
|||
pub mod commands;
|
||||
pub mod packet;
|
||||
//! Welcome to the Domo protocol rust implementation.
|
||||
//!
|
||||
//! This implementation is up-to-date with DomoProto v1.
|
||||
//!
|
||||
//! Most code that should/is actually used (usually) has documentation with it.
|
||||
|
||||
#![forbid(clippy::unwrap_used, clippy::expect_used)]
|
||||
|
||||
#[macro_use]
|
||||
#[doc(hidden)]
|
||||
pub mod prelude;
|
||||
|
||||
/// Domo protocol packet abstraction.
|
||||
pub mod packet;
|
||||
|
||||
/// Identifier helper code.
|
||||
pub mod identifier;
|
||||
|
||||
/// Data Types as stated in the Domo protocol spec.
|
||||
pub mod data_types;
|
||||
|
||||
/// Commands are according to Domo protocol spec.
|
||||
pub mod commands;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::packet;
|
||||
use crate::packet::data_types::DataType;
|
||||
use crate::packet::identifier::Identifier;
|
||||
use crate::data_types::{DataType, get_data_types};
|
||||
use crate::identifier::Identifier;
|
||||
use crate::packet::packet_data::PacketData;
|
||||
|
||||
#[test]
|
||||
pub fn data_types() {
|
||||
// test Vec<u8> -> Vec<DataType>
|
||||
assert_eq!(
|
||||
DataType::get_data(vec![0x00, 0x10, 0x00]),
|
||||
get_data_types(vec![0x00, 0x10, 0x00]),
|
||||
vec![
|
||||
DataType::Nothing,
|
||||
DataType::Switch(false),
|
||||
|
|
|
@ -1,153 +0,0 @@
|
|||
use crate::packet::identifier::Identifier;
|
||||
use crate::prelude::as_u64_be;
|
||||
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub enum DataType {
|
||||
// Meta
|
||||
/// Meta type: Nothing [0x00]
|
||||
Nothing,
|
||||
/// Meta type: Array [0x01]
|
||||
Array(Vec<DataType>),
|
||||
|
||||
// Basic types
|
||||
/// Basic Type: Switch [0x10]
|
||||
Switch(bool),
|
||||
/// Basic Type: Slider [0x11]
|
||||
Slider(u8),
|
||||
/// Basic Type: Text [0x12]
|
||||
Text(String),
|
||||
|
||||
// Cosmetic
|
||||
/// Cosmetic Type: RGB [0x20]
|
||||
RGB(u8, u8, u8),
|
||||
|
||||
// Time & Space
|
||||
/// Time & Space Type: Seconds [0x90]
|
||||
Seconds(u64),
|
||||
|
||||
// Domo Types
|
||||
/// Domo type: Node Ref [0xF0]
|
||||
NodeRef(Identifier),
|
||||
}
|
||||
|
||||
impl DataType {
|
||||
pub fn get_data_size(data_type: u8) -> usize {
|
||||
match data_type {
|
||||
0x00 => 0,
|
||||
0x01 => {
|
||||
2
|
||||
},
|
||||
0x10 => 1,
|
||||
0x11 => 1,
|
||||
0x12 => 256,
|
||||
0x20 => 3,
|
||||
0x90 => 8,
|
||||
0xF0 => 4,
|
||||
_ => 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data(buf: Vec<u8>) -> Vec<DataType> {
|
||||
let mut data = vec![];
|
||||
let mut pieces = vec![];
|
||||
let mut byte = buf.iter().peekable();
|
||||
|
||||
while let Some(&c) = byte.next() {
|
||||
let mut piece = vec![c];
|
||||
let mut len = DataType::get_data_size(c);
|
||||
while len > 0 {
|
||||
if let Some(_) = byte.peek() {
|
||||
piece.push(*byte.next().unwrap());
|
||||
}
|
||||
len -= 1;
|
||||
}
|
||||
if piece.len() == DataType::get_data_size(c) + 1 {
|
||||
pieces.push(piece);
|
||||
} else {
|
||||
pieces.push(vec![0x00]);
|
||||
}
|
||||
}
|
||||
|
||||
for piece in pieces {
|
||||
data.push(DataType::from(piece))
|
||||
}
|
||||
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_data_type {
|
||||
($v: ident, $len: literal, $($t:tt)*) => {
|
||||
if $v.len() - 1 == $len {
|
||||
$($t)*
|
||||
} else {
|
||||
DataType::Nothing
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for DataType {
|
||||
/// Turn a Vec<u8> into a single data type.
|
||||
/// Returns Datatype::Nothing if it's nothing or can find nothing.
|
||||
fn from(value: Vec<u8>) -> Self {
|
||||
if value.len() == 0 {
|
||||
return DataType::Nothing;
|
||||
};
|
||||
match value[0] {
|
||||
0x01 => {
|
||||
if value.len() - 1 < 2 { return DataType::Nothing }
|
||||
let size = ((value[1] as u16) << 8) | value[2] as u16;
|
||||
if value.len() - 1 < size as usize { return DataType::Nothing }
|
||||
DataType::Array(DataType::get_data(value[3..(size as usize)].to_vec()))
|
||||
},
|
||||
0x10 => impl_data_type!(value, 1, DataType::Switch(value[1] != 0)),
|
||||
0x11 => impl_data_type!(value, 1, DataType::Slider(value[1])),
|
||||
0x12 => impl_data_type!(
|
||||
value,
|
||||
256,
|
||||
if let Ok(s) = String::from_utf8(value[1..257].to_vec()) {
|
||||
DataType::Text(s)
|
||||
} else {
|
||||
DataType::Nothing
|
||||
}
|
||||
),
|
||||
0x20 => impl_data_type!(value, 3, DataType::RGB(value[1], value[2], value[3])),
|
||||
0x90 => impl_data_type!(value, 8, DataType::Seconds(as_u64_be(&value[1..9]))),
|
||||
0xF0 => impl_data_type!(value, 4, DataType::NodeRef(Identifier::from(&value[1..5]))),
|
||||
_ => DataType::Nothing,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for DataType {
|
||||
fn into(self) -> Vec<u8> {
|
||||
match self {
|
||||
DataType::Nothing => vec![0x00],
|
||||
DataType::Array(vec) => {
|
||||
let mut res = vec![0x01];
|
||||
for item in vec {
|
||||
res.extend(Into::<Vec<u8>>::into(item).iter());
|
||||
}
|
||||
res
|
||||
},
|
||||
DataType::Switch(b) => vec![0x10, b as u8],
|
||||
DataType::Slider(v) => vec![0x11, v],
|
||||
DataType::Text(s) => {
|
||||
let mut bytes = vec![0x12];
|
||||
bytes.extend(s.into_bytes());
|
||||
bytes
|
||||
},
|
||||
DataType::RGB(r, g, b) => vec![0x20, r, g, b],
|
||||
DataType::Seconds(s) => {
|
||||
let mut bytes = vec![0x90];
|
||||
bytes.extend_from_slice(s.to_be_bytes().as_ref());
|
||||
bytes
|
||||
},
|
||||
DataType::NodeRef(id) => {
|
||||
let mut bytes = vec![0xF0];
|
||||
bytes.extend_from_slice(id.bytes.as_ref());
|
||||
bytes
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
pub mod data_types;
|
||||
pub mod identifier;
|
||||
pub mod packet_data;
|
||||
|
||||
use crate::prelude;
|
||||
use identifier::Identifier;
|
||||
use packet_data::PacketData;
|
||||
use std::convert::TryFrom;
|
||||
use crate::identifier::Identifier;
|
||||
use std::fmt::Debug;
|
||||
use std::io;
|
||||
use packet_data::PacketData;
|
||||
use crate::packet::raw_packet::RawPacket;
|
||||
|
||||
/// High level abstraction for packet data.
|
||||
pub mod packet_data;
|
||||
/// Low level abstraction for packets.
|
||||
pub mod raw_packet;
|
||||
|
||||
/// Size of the packet header.
|
||||
pub const PACKET_HEADER_SIZE: usize = 20;
|
||||
|
@ -15,6 +16,7 @@ pub const FULL_PACKET_SIZE: usize = 65559;
|
|||
/// Packet size without CRC32.
|
||||
pub const BASE_PACKET_SIZE: usize = 65555;
|
||||
|
||||
/// The abstraction for all DomoProto packets.
|
||||
#[derive(Debug)]
|
||||
pub struct Packet {
|
||||
pub dest: Identifier,
|
||||
|
@ -38,6 +40,7 @@ impl Packet {
|
|||
data,
|
||||
} => {
|
||||
let mut buf = Vec::new();
|
||||
let data = data.clone().into_bytes();
|
||||
let data_length = data.len();
|
||||
buf.push(0x01);
|
||||
buf.extend_from_slice(&dest.bytes);
|
||||
|
@ -47,15 +50,14 @@ impl Packet {
|
|||
buf.push(command.clone());
|
||||
buf.push((data_length >> 8) as u8);
|
||||
buf.push((data_length & 0xFF) as u8);
|
||||
buf.extend(&data.data);
|
||||
buf.extend(data);
|
||||
buf
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_crc32(&self) -> u32 {
|
||||
let d = self.build_base_packet();
|
||||
crc32fast::hash(d.as_slice())
|
||||
crc32fast::hash(self.build_base_packet().as_slice())
|
||||
}
|
||||
|
||||
pub fn build_full_packet(&self) -> Vec<u8> {
|
||||
|
@ -69,40 +71,14 @@ impl Packet {
|
|||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Vec<u8>> for Packet {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
match value[0] {
|
||||
0x01 => {
|
||||
let header: Vec<u16> = value[..0x15].iter().map(|v| u16::from(*v)).collect();
|
||||
let data_length = (header[0x12] << 8) | header[0x13];
|
||||
let crc32 = prelude::as_u32_be(&value[(0x14 + data_length as usize)..]);
|
||||
let packet = Packet {
|
||||
dest: Identifier::from(&value[0x01..0x05]),
|
||||
src: Identifier::from(&value[0x05..0x09]),
|
||||
packet_id: Identifier::from(&value[0x09..0x0D]),
|
||||
reply_to: Identifier::from(&value[0x0D..0x11]),
|
||||
command: value[0x11],
|
||||
data: PacketData::new(value[0x14..(data_length as usize + 0x14)].to_owned()),
|
||||
};
|
||||
if crc32fast::hash(packet.build_base_packet().as_slice()) != crc32 {
|
||||
Err(())
|
||||
} else {
|
||||
Ok(packet)
|
||||
}
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for Packet {
|
||||
fn into(self) -> Vec<u8> {
|
||||
self.build_full_packet()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToPacket {
|
||||
fn to_packet(self, src: Identifier, dest: Identifier, packet_id: Identifier, reply_to: Identifier) -> Packet;
|
||||
}
|
||||
|
||||
/// Secretly calls RawPacket::try_from().into()
|
||||
impl TryFrom<Vec<u8>> for Packet {
|
||||
type Error = io::Error;
|
||||
fn try_from(data: Vec<u8>) -> io::Result<Self> {
|
||||
Ok(RawPacket::try_from(data)?.into())
|
||||
}
|
||||
}
|
|
@ -1,20 +1,40 @@
|
|||
use std::io;
|
||||
use crate::commands::property_control::PropertyControlCommand;
|
||||
use crate::commands::node_management::NodeManagementCommand;
|
||||
use crate::packet::raw_packet::RawPacket;
|
||||
|
||||
/// Abstraction used for interpreting the data in a packet.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PacketData {
|
||||
pub data: Vec<u8>,
|
||||
pub enum PacketData {
|
||||
NodeManagement(NodeManagementCommand),
|
||||
PropertyControl(PropertyControlCommand),
|
||||
Raw(Vec<u8>)
|
||||
}
|
||||
|
||||
/// Returns an empty `PacketData::Raw`
|
||||
impl Default for PacketData {
|
||||
fn default() -> Self {
|
||||
PacketData::Raw(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl PacketData {
|
||||
pub fn new(data: Vec<u8>) -> PacketData {
|
||||
PacketData { data }
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.data.len()
|
||||
pub fn into_bytes(self) -> Vec<u8> {
|
||||
match self {
|
||||
PacketData::NodeManagement(v) => v.into(),
|
||||
PacketData::PropertyControl(v) => v.into(),
|
||||
PacketData::Raw(v) => v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for PacketData {
|
||||
fn default() -> Self {
|
||||
PacketData { data: vec![] }
|
||||
impl TryFrom<RawPacket> for PacketData {
|
||||
type Error = io::Error;
|
||||
fn try_from(raw_packet: RawPacket) -> io::Result<Self> {
|
||||
match raw_packet.command {
|
||||
0x0 => Ok(PacketData::NodeManagement(NodeManagementCommand::try_from(raw_packet)?)),
|
||||
0x1 => Ok(PacketData::PropertyControl(PropertyControlCommand::try_from(raw_packet)?)),
|
||||
_ => Err(io::Error::new(io::ErrorKind::InvalidInput, "command group is unsupported"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
16
domo_proto/src/packet/raw_packet/mod.rs
Normal file
16
domo_proto/src/packet/raw_packet/mod.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
mod vec;
|
||||
mod packet;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RawPacket {
|
||||
pub version: u8,
|
||||
pub dest: u32,
|
||||
pub src: u32,
|
||||
pub packet_id: u32,
|
||||
pub reply_to: u32,
|
||||
pub command: u8,
|
||||
pub data_length: usize,
|
||||
pub data: Vec<u8>,
|
||||
pub checksum: u32
|
||||
}
|
||||
|
46
domo_proto/src/packet/raw_packet/packet.rs
Normal file
46
domo_proto/src/packet/raw_packet/packet.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
use crate::identifier::Identifier;
|
||||
use crate::packet::Packet;
|
||||
use crate::packet::packet_data::PacketData;
|
||||
use crate::packet::raw_packet::RawPacket;
|
||||
|
||||
impl From<Packet> for RawPacket {
|
||||
fn from(packet: Packet) -> Self {
|
||||
let data = &packet.data.into_bytes();
|
||||
let data_length = data.len();
|
||||
RawPacket {
|
||||
version: 0x01,
|
||||
dest: packet.dest.into(),
|
||||
src: packet.src.into(),
|
||||
packet_id: packet.packet_id.into(),
|
||||
reply_to: packet.reply_to.into(),
|
||||
command: packet.command,
|
||||
data_length,
|
||||
data: data.clone(),
|
||||
checksum: {
|
||||
let mut v = vec![0x01];
|
||||
v.extend(packet.dest.bytes);
|
||||
v.extend(packet.src.bytes);
|
||||
v.extend(packet.packet_id.bytes);
|
||||
v.extend(packet.reply_to.bytes);
|
||||
v.push(packet.command);
|
||||
v.extend(data_length.to_be_bytes());
|
||||
v.extend(data);
|
||||
crc32fast::hash(v.as_ref())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Packet> for RawPacket {
|
||||
fn into(self) -> Packet {
|
||||
Packet {
|
||||
dest: Identifier::from(self.dest),
|
||||
src: Identifier::from(self.src),
|
||||
packet_id: Identifier::from(self.packet_id),
|
||||
reply_to: Identifier::from(self.reply_to),
|
||||
command: self.command,
|
||||
data: PacketData::try_from(self.clone())
|
||||
.unwrap_or(PacketData::Raw(self.data)),
|
||||
}
|
||||
}
|
||||
}
|
47
domo_proto/src/packet/raw_packet/vec.rs
Normal file
47
domo_proto/src/packet/raw_packet/vec.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
use std::io;
|
||||
use crate::packet::PACKET_HEADER_SIZE;
|
||||
use crate::packet::raw_packet::RawPacket;
|
||||
use crate::prelude::as_u32_be;
|
||||
|
||||
impl TryFrom<Vec<u8>> for RawPacket {
|
||||
type Error = io::Error;
|
||||
fn try_from(data: Vec<u8>) -> io::Result<Self> {
|
||||
if data.len() < PACKET_HEADER_SIZE {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "Can't parse data into RawPacket"))
|
||||
}
|
||||
let data_length = u16::from_be_bytes([data[0x12], data[0x13]]) as usize;
|
||||
if data.len() < 0x14 + data_length + 4 {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "Can't parse data into RawPacket"))
|
||||
}
|
||||
let checksum = as_u32_be(data[(data.len() - 5)..].as_ref());
|
||||
if checksum != crc32fast::hash(data[..(data.len() - 4)].as_ref()) {
|
||||
return Err(io::Error::new(io::ErrorKind::InvalidData, "Checksum does not match"))
|
||||
}
|
||||
Ok(RawPacket {
|
||||
version: data[0],
|
||||
dest: as_u32_be(data[0x01..0x05].as_ref()),
|
||||
src: as_u32_be(data[0x05..0x09].as_ref()),
|
||||
packet_id: as_u32_be(data[0x09..0x0D].as_ref()),
|
||||
reply_to: as_u32_be(data[0x0D..0x11].as_ref()),
|
||||
command: data[0x11],
|
||||
data_length,
|
||||
data: data[0x14..data_length].to_vec(),
|
||||
checksum,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Vec<u8>> for RawPacket {
|
||||
fn into(self) -> Vec<u8> {
|
||||
let mut v = vec![self.version];
|
||||
v.extend(self.dest.to_be_bytes());
|
||||
v.extend(self.src.to_be_bytes());
|
||||
v.extend(self.packet_id.to_be_bytes());
|
||||
v.extend(self.reply_to.to_be_bytes());
|
||||
v.push(self.command);
|
||||
v.extend(self.data_length.to_be_bytes());
|
||||
v.extend(self.data);
|
||||
v.extend(self.checksum.to_be_bytes());
|
||||
v
|
||||
}
|
||||
}
|
|
@ -15,3 +15,13 @@ pub fn as_u64_be(array: &[u8]) -> u64 {
|
|||
+ ((array[6] as u64) << 8)
|
||||
+ ((array[7] as u64) << 0)
|
||||
}
|
||||
|
||||
macro_rules! impl_into_enum_variant {
|
||||
($houser: tt::$variant: tt, $impl_type: ty) => {
|
||||
impl Into<$houser> for $impl_type {
|
||||
fn into(self) -> $houser {
|
||||
$houser::$variant(self)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue