#![warn(
// ---------- Stylistic
future_incompatible,
nonstandard_style,
rust_2018_idioms,
trivial_casts,
trivial_numeric_casts,
// ---------- Public
missing_debug_implementations,
missing_docs,
unreachable_pub,
// ---------- Unsafe
unsafe_code,
// ---------- Unused
unused_extern_crates,
unused_import_braces,
unused_qualifications,
unused_results,
)]
#[allow(unused_imports)]
#[macro_use]
extern crate log;
use std::fmt::{Display, Formatter};
use std::str::FromStr;
#[derive(Clone, Debug)]
pub enum SolidColor {
Red,
Green,
Yellow,
Blue,
White,
Cyan,
Magenta,
Custom {
red: u8,
green: u8,
blue: u8,
},
}
#[derive(Clone, Debug)]
pub enum Wave {
Short,
Long,
OverlappingShort,
OverlappingLong,
}
#[derive(Clone, Debug)]
pub enum Pattern {
Police,
TrafficLights,
Random(u8),
#[cfg(target_os = "windows")]
Rainbow,
#[cfg(target_os = "windows")]
Sea,
#[cfg(target_os = "windows")]
WhiteWave,
#[cfg(target_os = "windows")]
Synthetic,
}
pub trait Device {
fn id(&self) -> String;
fn turn_off(&self) -> error::Result<()>;
fn set_solid_color(&self, color: SolidColor) -> error::Result<()>;
fn set_fade_to_color(&self, color: SolidColor, fade_duration: u8) -> error::Result<()>;
fn set_color_strobe(
&self,
color: SolidColor,
strobe_speed: u8,
repeat_count: u8,
) -> error::Result<()>;
fn set_color_wave(
&self,
color: SolidColor,
wave_pattern: Wave,
wave_speed: u8,
repeat_count: u8,
) -> error::Result<()>;
fn set_pattern(&self, pattern: Pattern, repeat_count: u8) -> error::Result<()>;
}
#[derive(Clone, Debug)]
pub enum SpecificLED {
All,
AllFront,
AllBack,
Number(u8),
}
pub trait TargetedDevice: Device {
fn set_specific_led(&mut self, led: SpecificLED) -> error::Result<()>;
}
impl Display for SolidColor {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
fn to_hex(v: &u8) -> String {
format!("{:#04x}", v)[2..].to_string()
}
write!(
f,
"{}",
match self {
SolidColor::Red => "red".to_string(),
SolidColor::Green => "green".to_string(),
SolidColor::Yellow => "yellow".to_string(),
SolidColor::Blue => "blue".to_string(),
SolidColor::White => "white".to_string(),
SolidColor::Cyan => "cyan".to_string(),
SolidColor::Magenta => "magenta".to_string(),
SolidColor::Custom { red, green, blue } =>
format!("{}{}{}", to_hex(red), to_hex(green), to_hex(blue)),
}
)
}
}
impl FromStr for SolidColor {
type Err = error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"red" => Ok(SolidColor::Red),
"green" => Ok(SolidColor::Green),
"yellow" => Ok(SolidColor::Yellow),
"blue" => Ok(SolidColor::Blue),
"white" => Ok(SolidColor::White),
"cyan" => Ok(SolidColor::Cyan),
"magenta" => Ok(SolidColor::Magenta),
_ => {
if s.len() == 6 && s.chars().all(|c| c.is_ascii_hexdigit()) {
Ok(SolidColor::Custom {
red: u8::from_str_radix(&s[0..1], 16)?,
green: u8::from_str_radix(&s[2..3], 16)?,
blue: u8::from_str_radix(&s[4..5], 16)?,
})
} else {
Err(error::Error::InvalidColor)
}
}
}
}
}
impl Display for Wave {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Wave::Short => "short",
Wave::Long => "long",
Wave::OverlappingShort => "overlapping short",
Wave::OverlappingLong => "overlapping long",
}
)
}
}
impl FromStr for Wave {
type Err = error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"short" => Ok(Wave::Short),
"long" => Ok(Wave::Long),
"overlapping short" => Ok(Wave::OverlappingShort),
"overlapping long" => Ok(Wave::OverlappingLong),
_ => Err(error::Error::InvalidPattern),
}
}
}
impl Display for Pattern {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Pattern::Police => "police".to_string(),
Pattern::TrafficLights => "traffic lights".to_string(),
Pattern::Random(n) => format!("random {}", n),
#[cfg(target_os = "windows")]
Pattern::Rainbow => "rainbow".to_string(),
#[cfg(target_os = "windows")]
Pattern::Sea => "sea".to_string(),
#[cfg(target_os = "windows")]
Pattern::WhiteWave => "white wave".to_string(),
#[cfg(target_os = "windows")]
Pattern::Synthetic => "synthetic".to_string(),
}
)
}
}
impl FromStr for Pattern {
type Err = error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"police" => Ok(Pattern::Police),
"traffic lights" => Ok(Pattern::TrafficLights),
"random 1" => Ok(Pattern::Random(1)),
"random 2" => Ok(Pattern::Random(2)),
"random 3" => Ok(Pattern::Random(3)),
"random 4" => Ok(Pattern::Random(4)),
"random 5" => Ok(Pattern::Random(5)),
#[cfg(target_os = "windows")]
"rainbow" => Ok(Pattern::Rainbow),
#[cfg(target_os = "windows")]
"sea" => Ok(Pattern::Sea),
#[cfg(target_os = "windows")]
"white wave" => Ok(Pattern::WhiteWave),
#[cfg(target_os = "windows")]
"synthetic" => Ok(Pattern::Synthetic),
_ => Err(error::Error::InvalidPattern),
}
}
}
impl Display for SpecificLED {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
SpecificLED::All => "all".to_string(),
SpecificLED::AllFront => "front".to_string(),
SpecificLED::AllBack => "back".to_string(),
SpecificLED::Number(n) => n.to_string(),
}
)
}
}
impl FromStr for SpecificLED {
type Err = error::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.to_lowercase();
match s.as_str() {
"all" => Ok(SpecificLED::All),
"front" => Ok(SpecificLED::AllFront),
"back" => Ok(SpecificLED::AllBack),
"1" => Ok(SpecificLED::Number(1)),
"2" => Ok(SpecificLED::Number(2)),
"3" => Ok(SpecificLED::Number(3)),
"4" => Ok(SpecificLED::Number(4)),
"5" => Ok(SpecificLED::Number(5)),
"6" => Ok(SpecificLED::Number(6)),
_ => Err(error::Error::InvalidLED),
}
}
}
pub mod error {
use std::fmt::{Debug, Display};
#[derive(Debug)]
pub enum Error {
InvalidColor,
InvalidPattern,
InvalidLED,
InvalidDeviceID,
DeviceNotFound,
InvalidRequest,
UnexpectedError(u16),
UnsupportedCommand,
IoError {
source: std::io::Error,
},
CustomFmt {
source: std::num::ParseIntError,
},
#[cfg(feature = "webhook")]
Request {
source: reqwest::Error,
},
Fmt {
source: std::fmt::Error,
},
}
pub type Result<T> = std::result::Result<T, Error>;
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
Self::InvalidColor => "The color value supplied was not recognized".to_string(),
Self::InvalidPattern => "The pattern value supplied was not recognized".to_string(),
Self::InvalidLED => "The LED number is either invalid or not supported by the connected device".to_string(),
Self::InvalidDeviceID => "The provided device ID was incorrectly formatted".to_string(),
Self::DeviceNotFound => "No device was discovered, or // TODO: he ID did not resolve to a device".to_string(),
Self::InvalidRequest => "The server indicated an invalid request".to_string(),
Self::UnexpectedError(status_code)=>format!("An unexpected HTTP error was returned: {status_code}"),
Self::UnsupportedCommand => "The command is not supported by the current device, or connection to the device".to_string(),
Self::IoError{source} => format!("An I/O error occurred; source: {source}"),
Self::CustomFmt { source } => format!("A parsing error occurred; source: {source}"),
#[cfg(feature = "webhook")]
Self::Request { source } => format!("An HTTP request error occurred; source: {source}"),
Self::Fmt { source } => format!("An formatting error occurred; source: {source}"),
}
)
}
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Error::IoError { source } => Some(source),
Error::CustomFmt { source } => Some(source),
#[cfg(feature = "webhook")]
Error::Request { source } => Some(source),
Error::Fmt { source } => Some(source),
_ => None,
}
}
}
impl From<std::io::Error> for Error {
fn from(source: std::io::Error) -> Self {
Self::IoError { source }
}
}
impl From<std::num::ParseIntError> for Error {
fn from(source: std::num::ParseIntError) -> Self {
Self::CustomFmt { source }
}
}
#[cfg(feature = "webhook")]
impl From<reqwest::Error> for Error {
fn from(source: reqwest::Error) -> Self {
Self::Request { source }
}
}
impl From<std::fmt::Error> for Error {
fn from(source: std::fmt::Error) -> Self {
Self::Fmt { source }
}
}
}
#[cfg(feature = "usb")]
pub mod usb_hid;
#[cfg(feature = "webhook")]
pub mod webhook;