#[cfg(feature = "logging")]
pub mod logging;
pub mod registration;
pub mod socket;
pub use crate::registration::RegistrationInfo;
pub use crate::socket::StreamDeckSocket;
use serde::{de, ser};
use serde_derive::{Deserialize, Serialize};
use serde_repr::{Deserialize_repr, Serialize_repr};
use std::fmt;
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "event", rename_all = "camelCase")]
pub enum Message<G, S, M> {
#[serde(rename_all = "camelCase")]
KeyDown {
action: String,
context: String,
device: String,
payload: KeyPayload<S>,
},
#[serde(rename_all = "camelCase")]
KeyUp {
action: String,
context: String,
device: String,
payload: KeyPayload<S>,
},
#[serde(rename_all = "camelCase")]
WillAppear {
action: String,
context: String,
device: Option<String>,
payload: VisibilityPayload<S>,
},
#[serde(rename_all = "camelCase")]
WillDisappear {
action: String,
context: String,
device: Option<String>,
payload: VisibilityPayload<S>,
},
#[serde(rename_all = "camelCase")]
TitleParametersDidChange {
action: String,
context: String,
device: Option<String>,
payload: TitleParametersPayload<S>,
},
#[serde(rename_all = "camelCase")]
DeviceDidConnect {
device: String,
device_info: DeviceInfo,
},
#[serde(rename_all = "camelCase")]
DeviceDidDisconnect {
device: String,
},
#[serde(rename_all = "camelCase")]
ApplicationDidLaunch {
payload: ApplicationPayload,
},
#[serde(rename_all = "camelCase")]
ApplicationDidTerminate {
payload: ApplicationPayload,
},
#[serde(rename_all = "camelCase")]
SendToPlugin {
action: String,
context: String,
payload: M,
},
#[serde(rename_all = "camelCase")]
DidReceiveSettings {
action: String,
context: String,
device: String,
payload: KeyPayload<S>,
},
#[serde(rename_all = "camelCase")]
PropertyInspectorDidAppear {
action: String,
context: String,
device: String,
},
#[serde(rename_all = "camelCase")]
PropertyInspectorDidDisappear {
action: String,
context: String,
device: String,
},
#[serde(rename_all = "camelCase")]
DidReceiveGlobalSettings {
payload: GlobalSettingsPayload<G>,
},
SystemDidWakeUp,
#[serde(other)]
Unknown,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(tag = "event", rename_all = "camelCase")]
pub enum MessageOut<G, S, M> {
#[serde(rename_all = "camelCase")]
SetTitle {
context: String,
payload: TitlePayload,
},
#[serde(rename_all = "camelCase")]
SetImage {
context: String,
payload: ImagePayload,
},
#[serde(rename_all = "camelCase")]
ShowAlert {
context: String,
},
#[serde(rename_all = "camelCase")]
ShowOk {
context: String,
},
#[serde(rename_all = "camelCase")]
GetSettings {
context: String,
},
#[serde(rename_all = "camelCase")]
SetSettings {
context: String,
payload: S,
},
#[serde(rename_all = "camelCase")]
SetState {
context: String,
payload: StatePayload,
},
#[serde(rename_all = "camelCase")]
SendToPropertyInspector {
action: String,
context: String,
payload: M,
},
#[serde(rename_all = "camelCase")]
SwitchToProfile {
context: String,
device: String,
payload: ProfilePayload,
},
#[serde(rename_all = "camelCase")]
OpenUrl {
payload: UrlPayload,
},
#[serde(rename_all = "camelCase")]
GetGlobalSettings {
context: String,
},
#[serde(rename_all = "camelCase")]
SetGlobalSettings {
context: String,
payload: G,
},
#[serde(rename_all = "camelCase")]
LogMessage {
payload: LogMessagePayload,
},
}
#[derive(Debug, Deserialize_repr, Serialize_repr)]
#[repr(u8)]
pub enum Target {
Both = 0,
Hardware = 1,
Software = 2,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TitlePayload {
pub title: Option<String>,
pub target: Target,
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<u8>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ImagePayload {
pub image: Option<String>,
pub target: Target,
#[serde(skip_serializing_if = "Option::is_none")]
pub state: Option<u8>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StatePayload {
pub state: u8,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ProfilePayload {
pub profile: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct UrlPayload {
pub url: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct KeyPayload<S> {
pub settings: S,
pub coordinates: Option<Coordinates>,
pub state: Option<u8>,
pub user_desired_state: Option<u8>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct VisibilityPayload<S> {
pub settings: S,
pub coordinates: Option<Coordinates>,
pub state: Option<u8>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TitleParametersPayload<S> {
pub settings: S,
pub coordinates: Coordinates,
pub state: Option<u8>,
pub title: String,
pub title_parameters: TitleParameters,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GlobalSettingsPayload<G> {
pub settings: G,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct LogMessagePayload {
pub message: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeviceInfo {
pub name: Option<String>,
pub size: DeviceSize,
#[serde(rename = "type")]
pub _type: Option<DeviceType>,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ApplicationPayload {
pub application: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Coordinates {
pub column: u8,
pub row: u8,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub enum Alignment {
Top,
Middle,
Bottom,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct TitleParameters {
pub font_family: String,
pub font_size: u8,
pub font_style: String,
pub font_underline: bool,
pub show_title: bool,
pub title_alignment: Alignment,
pub title_color: String,
}
#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct DeviceSize {
pub columns: u8,
pub rows: u8,
}
#[derive(Debug)]
pub enum DeviceType {
StreamDeck, StreamDeckMini, StreamDeckXl, StreamDeckMobile, CorsairGKeys, StreamDeckPedal, CorsairVoyager, StreamDeckPlus, Unknown(u64),
}
impl ser::Serialize for DeviceType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serializer.serialize_u64(match self {
DeviceType::StreamDeck => 0,
DeviceType::StreamDeckMini => 1,
DeviceType::StreamDeckXl => 2,
DeviceType::StreamDeckMobile => 3,
DeviceType::CorsairGKeys => 4,
DeviceType::StreamDeckPedal => 5,
DeviceType::CorsairVoyager => 6,
DeviceType::StreamDeckPlus => 7,
DeviceType::Unknown(value) => *value,
})
}
}
impl<'de> de::Deserialize<'de> for DeviceType {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = DeviceType;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("an integer")
}
fn visit_u64<E>(self, value: u64) -> Result<DeviceType, E>
where
E: de::Error,
{
Ok(match value {
0 => DeviceType::StreamDeck,
1 => DeviceType::StreamDeckMini,
2 => DeviceType::StreamDeckXl,
3 => DeviceType::StreamDeckMobile,
4 => DeviceType::CorsairGKeys,
5 => DeviceType::StreamDeckPedal,
6 => DeviceType::CorsairVoyager,
7 => DeviceType::StreamDeckPlus,
value => DeviceType::Unknown(value),
})
}
}
deserializer.deserialize_u64(Visitor)
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Color {
r: u8,
g: u8,
b: u8,
}
impl ser::Serialize for Color {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
let html_color = format!("#{:02x}{:02x}{:02x}", self.r, self.g, self.b);
serializer.serialize_str(&html_color)
}
}
impl<'de> de::Deserialize<'de> for Color {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
struct Visitor;
impl<'de> de::Visitor<'de> for Visitor {
type Value = Color;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
formatter.write_str("a hex color")
}
fn visit_str<E>(self, value: &str) -> Result<Color, E>
where
E: de::Error,
{
if value.len() != 7 {
return Err(E::invalid_length(value.len(), &self));
}
if &value[0..1] != "#" {
return Err(E::custom("expected string to begin with '#'"));
}
let r = u8::from_str_radix(&value[1..3], 16)
.map_err(|_| E::invalid_value(de::Unexpected::Str(value), &self))?;
let g = u8::from_str_radix(&value[3..5], 16)
.map_err(|_| E::invalid_value(de::Unexpected::Str(value), &self))?;
let b = u8::from_str_radix(&value[5..7], 16)
.map_err(|_| E::invalid_value(de::Unexpected::Str(value), &self))?;
Ok(Color { r, g, b })
}
}
deserializer.deserialize_str(Visitor)
}
}
#[cfg(test)]
mod test {
use super::Color;
#[test]
fn color() {
let color_a = Color {
r: 0x12,
g: 0x34,
b: 0x56,
};
let color_b = Color {
r: 0x12,
g: 0x12,
b: 0x12,
};
let as_json = r##"["#123456","#121212"]"##;
let colors: Vec<Color> = serde_json::from_str(as_json).expect("array of colors");
assert_eq!(2, colors.len());
assert_eq!(color_a, colors[0]);
assert_eq!(color_b, colors[1]);
let json_str: String = serde_json::to_string(&vec![color_a, color_b]).expect("JSON array");
assert_eq!(as_json, json_str);
}
}