From 5811d290321410b16575e4b6345d78b1c65a5da8 Mon Sep 17 00:00:00 2001
From: Raine <raine@ixvd.net>
Date: Sun, 5 Nov 2023 20:52:29 +0100
Subject: [PATCH] fix: implement new proto spec

---
 domo_proto/src/commands/mod.rs                |  9 ++-
 .../src/commands/raw_data_transmission/mod.rs | 78 +++++++++++++++++++
 .../src/commands/raw_data_transmission/vec.rs | 21 +++++
 domo_proto/src/data_types/mod.rs              | 29 +++----
 domo_proto/src/data_types/vec.rs              | 35 +++++----
 domo_proto/src/lib.rs                         |  7 +-
 domo_proto/src/packet/mod.rs                  | 16 +++-
 domo_proto/src/packet/packet_data.rs          | 10 ++-
 domo_proto/src/packet/raw_packet/packet.rs    |  2 +-
 9 files changed, 161 insertions(+), 46 deletions(-)
 create mode 100644 domo_proto/src/commands/raw_data_transmission/mod.rs
 create mode 100644 domo_proto/src/commands/raw_data_transmission/vec.rs

diff --git a/domo_proto/src/commands/mod.rs b/domo_proto/src/commands/mod.rs
index 167daaf..a82cdfe 100644
--- a/domo_proto/src/commands/mod.rs
+++ b/domo_proto/src/commands/mod.rs
@@ -1,8 +1,15 @@
 use crate::packet::packet_data::PacketData;
 
+/// NM = Node Management
+/// This is the abstraction for managing nodes.
 pub mod node_management;
+/// PC = Property Control
+/// This is the abstraction for controling properties on a node.
 pub mod property_control;
+/// RDT = Raw Data Transmission
+/// This is the abstraction for sending raw data over domo.
+pub mod raw_data_transmission;
 
 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>);
+impl_into_enum_variant!(PacketData::Unknown, Vec<u8>);
diff --git a/domo_proto/src/commands/raw_data_transmission/mod.rs b/domo_proto/src/commands/raw_data_transmission/mod.rs
new file mode 100644
index 0000000..f58afe0
--- /dev/null
+++ b/domo_proto/src/commands/raw_data_transmission/mod.rs
@@ -0,0 +1,78 @@
+use std::io;
+
+use crate::{
+    identifier::Identifier,
+    packet::{packet_data::PacketData, raw_packet::RawPacket, Packet, ToPacket},
+    prelude::as_u32_be,
+};
+
+pub mod vec;
+
+#[derive(Debug, Clone)]
+pub enum RawDataTransmission {
+    SetupTransmission {
+        /// The total size of the data being sent.
+        size: u64,
+        /// This string is the mime type of the data being sent.  
+        /// By default this should be `application/octet-stream`.  
+        /// The maximum length of this string is 128 bytes since the mime rfc states the recommended max is 127 bytes.
+        mime_type: String,
+    },
+    Data {
+        /// The number of the segment in the sequence of segments being sent.
+        sequence_number: u32,
+        /// Segment size in bytes
+        size: u32,
+        /// A segment of the data being sent
+        data: Vec<u8>,
+    },
+}
+
+impl ToPacket for RawDataTransmission {
+    fn to_packet(
+        self,
+        src: Identifier,
+        dest: Identifier,
+        packet_id: Identifier,
+        reply_to: Identifier,
+    ) -> Packet {
+        Packet {
+            src,
+            dest,
+            packet_id,
+            reply_to,
+            command: 0x20,
+            data: PacketData::RawDataTransmission(self.clone()),
+        }
+    }
+}
+
+impl TryFrom<RawPacket> for RawDataTransmission {
+    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));
+
+        let mut data = raw_packet.data;
+        match raw_packet.command {
+            0x20 => {
+                let size = u64::from_be_bytes([
+                    data[0], data[1], data[2], data[3], data[4], data[5], data[6], data[7],
+                ]);
+                let mime_type = String::from_utf8(data[8..].to_vec()).map_err(|_| {
+                    io::Error::new(io::ErrorKind::InvalidInput, "Invalid mime type string")
+                })?;
+                Ok(RawDataTransmission::SetupTransmission { size, mime_type })
+            }
+            0x21 => {
+                let sequence_number = as_u32_be(&mut data);
+                let size = as_u32_be(&mut data);
+                Ok(RawDataTransmission::Data {
+                    sequence_number,
+                    size,
+                    data,
+                })
+            }
+            _ => build_invalid_input_err("Invalid command"),
+        }
+    }
+}
diff --git a/domo_proto/src/commands/raw_data_transmission/vec.rs b/domo_proto/src/commands/raw_data_transmission/vec.rs
new file mode 100644
index 0000000..9b1dacf
--- /dev/null
+++ b/domo_proto/src/commands/raw_data_transmission/vec.rs
@@ -0,0 +1,21 @@
+use super::RawDataTransmission;
+
+impl Into<Vec<u8>> for RawDataTransmission {
+    fn into(self) -> Vec<u8> {
+        match self {
+            RawDataTransmission::SetupTransmission { size, mime_type } => {
+                let mut v = vec![];
+                v.extend(size.to_be_bytes().to_vec());
+                v.extend(mime_type.into_bytes());
+                v
+            },
+            RawDataTransmission::Data { sequence_number, size, data } => {
+                let mut v = vec![];
+                v.extend(sequence_number.to_be_bytes().to_vec());
+                v.extend(size.to_be_bytes().to_vec());
+                v.extend(data);
+                v
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/domo_proto/src/data_types/mod.rs b/domo_proto/src/data_types/mod.rs
index 4b4d8c9..09bde6a 100644
--- a/domo_proto/src/data_types/mod.rs
+++ b/domo_proto/src/data_types/mod.rs
@@ -11,39 +11,30 @@ pub enum DataType {
     Array(Vec<DataType>),
 
     // Basic types
-    /// Basic Type: Switch (`0x10`]
-    Switch(bool),
+    /// Basic Type: Boolean (`0x10`]
+    Boolean(bool),
     /// Basic Type: Slider (`0x11`)
-    Slider(u8),
+    Number(u64),
     /// Basic Type: Text (`0x12`)
     Text(String),
+    /// Basic Type: Identifier (`0x13`)
+    Identifier(Identifier),
 
-    // Cosmetic
-    /// Cosmetic Type: RGB (`0x20`)
+    // Color
+    /// Color 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
-            },
+            0x01 => 2,
             0x10 => 1,
-            0x11 => 1,
+            0x11 => 8,
             0x12 => 256,
+            0x13 => 4,
             0x20 => 3,
-            0x90 => 8,
-            0xF0 => 4,
             _ => 0,
         }
     }
diff --git a/domo_proto/src/data_types/vec.rs b/domo_proto/src/data_types/vec.rs
index e6d8ebd..dad217e 100644
--- a/domo_proto/src/data_types/vec.rs
+++ b/domo_proto/src/data_types/vec.rs
@@ -1,6 +1,6 @@
 use std::io;
 use crate::data_types::{DataType, get_data_types};
-use crate::identifier::Identifier;
+use crate::identifier::{Identifier, self};
 use crate::prelude::as_u64_be;
 
 impl Into<Vec<u8>> for DataType {
@@ -14,24 +14,23 @@ impl Into<Vec<u8>> for DataType {
                 }
                 res
             },
-            DataType::Switch(b) => vec![0x10, b as u8],
-            DataType::Slider(v) => vec![0x11, v],
+            DataType::Boolean(b) => vec![0x10, b as u8],
+            DataType::Number(v) => {
+                let mut bytes = vec![0x11];
+                bytes.extend(v.to_be_bytes());
+                bytes
+            },
             DataType::Text(s) => {
                 let mut bytes = vec![0x12];
                 bytes.extend(s.into_bytes());
                 bytes
             },
+            DataType::Identifier(identifier) => {
+                let mut bytes = vec![0x13];
+                bytes.extend(identifier.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
-            },
         }
     }
 }
@@ -61,8 +60,11 @@ impl TryFrom<Vec<u8>> for DataType {
                 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])),
+            0x10 => impl_data_type!(value, 1, DataType::Boolean(value[1] != 0)),
+            0x11 => impl_data_type!(value, 8, DataType::Number(u64::from_be_bytes([
+                value[1], value[2], value[3], value[4],
+                value[5], value[6], value[7], value[8]
+            ]))),
             0x12 => impl_data_type!(
                 value,
                 256,
@@ -72,9 +74,8 @@ impl TryFrom<Vec<u8>> for DataType {
                     DataType::Nothing
                 }
             ),
+            0x13 => impl_data_type!(value, 4, DataType::Identifier(Identifier::from(&value[1..5]))),
             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.")),
         }
     }
diff --git a/domo_proto/src/lib.rs b/domo_proto/src/lib.rs
index 04c7026..edc5e16 100644
--- a/domo_proto/src/lib.rs
+++ b/domo_proto/src/lib.rs
@@ -1,6 +1,6 @@
 //! Welcome to the Domo protocol rust implementation.
 //!
-//! This implementation is up-to-date with DomoProto v1.
+//! This implementation is up-to-date with Domo protocol v1.
 //!
 //! Most code that should/is actually used (usually) has documentation with it.
 
@@ -27,7 +27,6 @@ mod tests {
     use crate::packet;
     use crate::data_types::{DataType, get_data_types};
     use crate::identifier::Identifier;
-    use crate::packet::Packet;
     use crate::packet::packet_data::PacketData;
 
     #[test]
@@ -37,12 +36,12 @@ mod tests {
             get_data_types(vec![0x00, 0x10, 0x00]),
             vec![
                 DataType::Nothing,
-                DataType::Switch(false),
+                DataType::Boolean(false),
             ]);
 
         // test DataType -> Vec<u8>
         assert_eq!(
-            Into::<Vec<u8>>::into(DataType::Switch(true)),
+            Into::<Vec<u8>>::into(DataType::Boolean(true)),
             vec![0x10, 0x01]
         )
     }
diff --git a/domo_proto/src/packet/mod.rs b/domo_proto/src/packet/mod.rs
index 33088f0..060dc4b 100644
--- a/domo_proto/src/packet/mod.rs
+++ b/domo_proto/src/packet/mod.rs
@@ -17,7 +17,7 @@ pub const FULL_PACKET_SIZE: usize = 65559;
 pub const BASE_PACKET_SIZE: usize = 65555;
 
 /// The abstraction for all DomoProto packets.
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub struct Packet {
     pub dest: Identifier,
     pub src: Identifier,
@@ -31,6 +31,20 @@ pub trait ToPacket {
     fn to_packet(self, src: Identifier, dest: Identifier, packet_id: Identifier, reply_to: Identifier) -> Packet;
 }
 
+/// Default packet is a ping from 0x0 to 0xFFFFFFFF
+impl Default for Packet {
+    fn default() -> Self {
+        Self {
+            dest: Identifier::from(0xFFFFFFFF),
+            src: Identifier::from(0),
+            command: 0x00,
+            packet_id: Identifier::random(),
+            reply_to: Identifier::default(),
+            data: PacketData::Unknown(vec![])
+        }
+    }
+}
+
 /// Secretly calls RawPacket::try_from().into()
 impl TryFrom<Vec<u8>> for Packet {
     type Error = io::Error;
diff --git a/domo_proto/src/packet/packet_data.rs b/domo_proto/src/packet/packet_data.rs
index 0daa807..f595088 100644
--- a/domo_proto/src/packet/packet_data.rs
+++ b/domo_proto/src/packet/packet_data.rs
@@ -1,6 +1,7 @@
 use std::io;
 use crate::commands::property_control::PropertyControlCommand;
 use crate::commands::node_management::NodeManagementCommand;
+use crate::commands::raw_data_transmission::RawDataTransmission;
 use crate::packet::raw_packet::RawPacket;
 
 /// Abstraction used for interpreting the data in a packet.
@@ -8,13 +9,14 @@ use crate::packet::raw_packet::RawPacket;
 pub enum PacketData {
     NodeManagement(NodeManagementCommand),
     PropertyControl(PropertyControlCommand),
-    Raw(Vec<u8>)
+    RawDataTransmission(RawDataTransmission),
+    Unknown(Vec<u8>)
 }
 
 /// Returns an empty `PacketData::Raw`
 impl Default for PacketData {
     fn default() -> Self {
-        PacketData::Raw(vec![])
+        PacketData::Unknown(vec![])
     }
 }
 
@@ -23,7 +25,8 @@ impl PacketData {
         match self {
             PacketData::NodeManagement(v) => v.into(),
             PacketData::PropertyControl(v) => v.into(),
-            PacketData::Raw(v) => v
+            PacketData::RawDataTransmission(v) => v.into(),
+            PacketData::Unknown(v) => v
         }
     }
 }
@@ -36,6 +39,7 @@ impl TryFrom<RawPacket> for PacketData {
         } {
             0x0 => Ok(PacketData::NodeManagement(NodeManagementCommand::try_from(raw_packet)?)),
             0x1 => Ok(PacketData::PropertyControl(PropertyControlCommand::try_from(raw_packet)?)),
+            0xF => Ok(PacketData::RawDataTransmission(RawDataTransmission::try_from(raw_packet)?)),
             _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "command group is unsupported"))
         }
     }
diff --git a/domo_proto/src/packet/raw_packet/packet.rs b/domo_proto/src/packet/raw_packet/packet.rs
index 0f25832..67875a0 100644
--- a/domo_proto/src/packet/raw_packet/packet.rs
+++ b/domo_proto/src/packet/raw_packet/packet.rs
@@ -44,7 +44,7 @@ impl Into<Packet> for RawPacket {
                     println!("{}", e);
                     e
                 })
-                .unwrap_or(PacketData::Raw(self.data)),
+                .unwrap_or(PacketData::Unknown(self.data)),
         }
     }
 }
\ No newline at end of file