use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use uuid::Uuid;
pub const ALPINE_VERSION: &str = "1.0";
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum MessageType {
AlpineDiscover,
AlpineDiscoverReply,
SessionInit,
SessionAck,
SessionReady,
SessionComplete,
AlpineControl,
AlpineControlAck,
AlpineFrame,
Keepalive,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DiscoveryRequest {
#[serde(rename = "type")]
pub message_type: MessageType,
pub version: String,
pub client_nonce: Vec<u8>,
pub requested: Vec<String>,
}
impl DiscoveryRequest {
pub fn new(requested: Vec<String>, client_nonce: Vec<u8>) -> Self {
Self {
message_type: MessageType::AlpineDiscover,
version: ALPINE_VERSION.to_string(),
client_nonce,
requested,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DiscoveryReply {
#[serde(rename = "type")]
pub message_type: MessageType,
pub alpine_version: String,
pub device_id: String,
pub manufacturer_id: String,
pub model_id: String,
pub hardware_rev: String,
pub firmware_rev: String,
pub mac: String,
pub server_nonce: Vec<u8>,
pub capabilities: CapabilitySet,
pub signature: Vec<u8>,
}
impl DiscoveryReply {
pub fn new(
identity: &DeviceIdentity,
mac: String,
server_nonce: Vec<u8>,
capabilities: CapabilitySet,
signature: Vec<u8>,
) -> Self {
Self {
message_type: MessageType::AlpineDiscoverReply,
alpine_version: ALPINE_VERSION.to_string(),
device_id: identity.device_id.clone(),
manufacturer_id: identity.manufacturer_id.clone(),
model_id: identity.model_id.clone(),
hardware_rev: identity.hardware_rev.clone(),
firmware_rev: identity.firmware_rev.clone(),
mac,
server_nonce,
capabilities,
signature,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
pub struct DeviceIdentity {
pub device_id: String,
pub manufacturer_id: String,
pub model_id: String,
pub hardware_rev: String,
pub firmware_rev: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CapabilitySet {
pub channel_formats: Vec<ChannelFormat>,
pub max_channels: u32,
pub grouping_supported: bool,
pub streaming_supported: bool,
pub encryption_supported: bool,
pub vendor_extensions: Option<HashMap<String, serde_json::Value>>,
}
impl Default for CapabilitySet {
fn default() -> Self {
Self {
channel_formats: vec![ChannelFormat::U8],
max_channels: 512,
grouping_supported: false,
streaming_supported: true,
encryption_supported: true,
vendor_extensions: None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "lowercase")]
pub enum ChannelFormat {
U8,
U16,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SessionInit {
#[serde(rename = "type")]
pub message_type: MessageType,
pub controller_nonce: Vec<u8>,
pub controller_pubkey: Vec<u8>,
pub requested: CapabilitySet,
pub session_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SessionAck {
#[serde(rename = "type")]
pub message_type: MessageType,
pub device_nonce: Vec<u8>,
pub device_pubkey: Vec<u8>,
pub device_identity: DeviceIdentity,
pub capabilities: CapabilitySet,
pub signature: Vec<u8>,
pub session_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SessionReady {
#[serde(rename = "type")]
pub message_type: MessageType,
pub session_id: Uuid,
pub mac: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SessionComplete {
#[serde(rename = "type")]
pub message_type: MessageType,
pub session_id: Uuid,
pub ok: bool,
pub error: Option<ErrorCode>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SessionEstablished {
pub session_id: Uuid,
pub controller_nonce: Vec<u8>,
pub device_nonce: Vec<u8>,
pub capabilities: CapabilitySet,
pub device_identity: DeviceIdentity,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ControlEnvelope {
#[serde(rename = "type")]
pub message_type: MessageType,
pub session_id: Uuid,
pub seq: u64,
pub op: ControlOp,
pub payload: serde_json::Value,
pub mac: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Acknowledge {
#[serde(rename = "type")]
pub message_type: MessageType,
pub session_id: Uuid,
pub seq: u64,
pub ok: bool,
pub detail: Option<String>,
pub mac: Vec<u8>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ControlOp {
GetInfo,
GetCaps,
Identify,
Restart,
GetStatus,
SetConfig,
SetMode,
TimeSync,
Vendor,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FrameEnvelope {
#[serde(rename = "type")]
pub message_type: MessageType,
pub session_id: Uuid,
pub timestamp_us: u64,
pub priority: u8,
pub channel_format: ChannelFormat,
pub channels: Vec<u16>,
pub groups: Option<HashMap<String, Vec<u16>>>,
pub metadata: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct Keepalive {
#[serde(rename = "type")]
pub message_type: MessageType,
pub session_id: Uuid,
pub tick_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ErrorCode {
DiscoveryInvalidSignature,
DiscoveryNonceMismatch,
DiscoveryUnsupportedVersion,
HandshakeSignatureInvalid,
HandshakeKeyDerivationFailed,
HandshakeTimeout,
HandshakeReplay,
SessionExpired,
SessionInvalidToken,
SessionMacMismatch,
ControlUnknownOp,
ControlPayloadInvalid,
ControlUnauthorized,
StreamBadFormat,
StreamTooLarge,
StreamUnsupportedChannelMode,
}