Skip to content

Commit

Permalink
feat: methods to update device led state with partial values
Browse files Browse the repository at this point in the history
  • Loading branch information
meskill committed Aug 6, 2022
1 parent 3176a2c commit 4f37a44
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 126 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
async-graphql = { version="4.0.1", optional=true }
custom_error = "1.9.2"
either = "1.7.0"
libloading = "0.7.3"
log = "0.4.17"
oaidl = "0.2.1"
Expand Down
4 changes: 2 additions & 2 deletions examples/disable_light_for_5_sec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@ const LIB_PATH: &str = if cfg!(target_arch = "x86_64") {
fn main() -> Result<(), CommonError> {
let sdk = MysticLightSDK::new(LIB_PATH)?;

let devices = sdk.get_devices()?;
let devices: Vec<_> = sdk.devices_iter().collect();

println!("{:#?}", devices);

println!("Second Device name is {}", devices[2].name());

let mut keyboard_leds = devices[2].leds()?;
let keyboard_leds: Vec<_> = devices[2].leds_iter().collect();

println!("{:#?}", keyboard_leds);

Expand Down
43 changes: 43 additions & 0 deletions examples/get_all_states.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use mystic_light_sdk::{CommonError, MysticLightSDK};
use std::time::Instant;

const LIB_PATH: &str = if cfg!(target_arch = "x86_64") {
"../sdk/MysticLight_SDK_x64.dll"
} else {
"../sdk/MysticLight_SDK.dll"
};

fn main() -> Result<(), CommonError> {
let timer = Instant::now();
let sdk = MysticLightSDK::new(LIB_PATH)?;

println!("Init in {:?} secs", timer.elapsed().as_secs_f32());

let devices = sdk.devices_iter();

println!(
"Getting devices for {:#?} secs",
timer.elapsed().as_secs_f32()
);

let leds = devices.map(|device| device.leds_iter());

println!("Getting leds for {:#?} secs", timer.elapsed().as_secs_f32());

let states: Vec<Vec<_>> = leds
.map(|led| {
led.into_iter()
.map(|led| led.get_state().unwrap())
.collect()
})
.collect();

println!(
"Getting states for {:#?} secs",
timer.elapsed().as_secs_f32()
);

println!("States: {:#?}", states);

Ok(())
}
86 changes: 51 additions & 35 deletions src/sdk/device.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
use either::Either;
use libloading::Library;
use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};

#[cfg(feature = "async-graphql")]
use crate::DeviceLedMutation;
use super::led::DeviceLedMutation;

use super::led::DeviceLed;
use super::types::{Filter, Result};
use libloading::Library;

/// used for filtering device's leds.
/// Currently, supports only filtering by name
Expand All @@ -31,11 +33,24 @@ impl Filter<&DeviceLed> for DeviceLedFilter {
}
}

#[cfg(feature = "async-graphql")]
fn filter_leds(
leds: &HashMap<String, DeviceLed>,
filter: DeviceLedFilter,
) -> Either<impl Iterator<Item = &DeviceLed>, impl Iterator<Item = &DeviceLed>> {
match filter.names {
Some(names) => Either::Left(names.into_iter().filter_map(|led_name| leds.get(&led_name))),
None => Either::Right(leds.values()),
}
}

/// Represents single hardware MysticLight Device
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct Device {
name: String,

pub(crate) leds: HashMap<String, DeviceLed>,

#[cfg_attr(feature = "serde", serde(skip))]
library: Arc<Mutex<Library>>,
#[cfg_attr(feature = "serde", serde(skip))]
Expand All @@ -51,32 +66,24 @@ impl Device {
self.name()
}

#[graphql(name = "leds")]
async fn async_graphql_leds(
&self,
#[graphql(default)] filter: DeviceLedFilter,
) -> Result<Vec<DeviceLed>> {
self.leds_with_filter(filter)
async fn leds(&self, #[graphql(default)] filter: DeviceLedFilter) -> Vec<&DeviceLed> {
filter_leds(&self.leds, filter).collect()
}
}

/// Mutation wrapper for a device
#[cfg(feature = "async-graphql")]
#[cfg_attr(docsrs, doc(cfg(feature = "async-graphql")))]
pub struct DeviceMutation(pub Device);
pub struct DeviceMutation<'a>(pub &'a Device);

/// Mutation wrapper for a device
#[cfg(feature = "async-graphql")]
#[async_graphql::Object]
impl DeviceMutation {
async fn leds(
&self,
ctx: &async_graphql::Context<'_>,
#[graphql(default)] filter: DeviceLedFilter,
) -> Result<Vec<DeviceLedMutation>> {
let leds = self.0.async_graphql_leds(ctx, filter).await?;

Ok(leds.into_iter().map(DeviceLedMutation::new).collect())
impl<'a> DeviceMutation<'a> {
async fn leds(&self, #[graphql(default)] filter: DeviceLedFilter) -> Vec<DeviceLedMutation> {
filter_leds(&self.0.leds, filter)
.map(DeviceLedMutation)
.collect()
}
}

Expand All @@ -94,39 +101,48 @@ impl Device {
&self.name
}

pub(crate) fn new(library: Arc<Mutex<Library>>, name: String, led_count: u32) -> Self {
pub(crate) fn new(library: Arc<Mutex<Library>>, name: String, led_count: u32) -> Result<Self> {
log::debug!(
"fn:new call with args: name={}, led_count={}",
name,
led_count
);

Self {
let leds = Self::resolve_leds(&library, &name, led_count)?;

Ok(Self {
library,
name,
led_count,
}
leds,
})
}

/// returns vec of device's leds
pub fn leds(&self) -> Result<Vec<DeviceLed>> {
self.leds_with_filter(DeviceLedFilter::default())
pub fn leds_iter(&self) -> impl Iterator<Item = &DeviceLed> {
self.leds.values()
}

pub fn leds_with_filter<F>(&self, filter: F) -> Result<Vec<DeviceLed>>
where
F: for<'a> Filter<&'a DeviceLed>,
{
log::debug!("fn:leds_with_filter call");
pub fn reload(&mut self) -> Result<()> {
log::debug!("fn:reload call");

self.leds = Self::resolve_leds(&self.library, &self.name, self.led_count)?;

let leds = (0..self.led_count)
Ok(())
}

fn resolve_leds(
library: &Arc<Mutex<Library>>,
name: &str,
led_count: u32,
) -> Result<HashMap<String, DeviceLed>> {
let leds = (0..led_count)
.into_iter()
.map(|led_index| DeviceLed::new(Arc::clone(&self.library), &self.name, led_index))
.filter(|led| match led {
Ok(led) => filter.predicate(led),
Err(_) => true,
.map(|led_index| {
let led = DeviceLed::new(Arc::clone(library), name, led_index)?;

Ok((led.name().to_owned(), led))
})
.collect::<Result<Vec<_>>>()?;
.collect::<Result<_>>()?;

Ok(leds)
}
Expand Down
11 changes: 9 additions & 2 deletions src/sdk/error.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use std::sync::PoisonError;

use custom_error::custom_error;
use libloading::Error as LibLoadingError;

use custom_error::custom_error;
use super::types::{BrightLevel, SpeedLevel};

custom_error! {
/// Errors generated by the MysticLight SDK, see [SDK docs](https://www.msi.com/Landing/mystic-light-rgb-gaming-pc/download)
Expand All @@ -25,7 +26,13 @@ custom_error! {
pub UsageError
/// Tried to set style that is not supported by current device
#[non_exhaustive]
NotSupportedStyle{style: String, supported_styles: String} = "{style} is not in the supported style list: {supported_styles}"
NotSupportedStyle{style: String, supported_styles: String} = "{style} is not in the supported style list: {supported_styles}",
/// Tried to set brightness level higher that supported
#[non_exhaustive]
ExcessBrightLevel{level: BrightLevel, max_level: BrightLevel} = "Passed bright level={level} exceeds supported {max_level}",
/// Tried to set speed level higher that supported
#[non_exhaustive]
ExcessSpeedLevel{level: SpeedLevel, max_level: SpeedLevel} = "Passed speed level={level} exceeds supported {max_level}",
}

custom_error! {
Expand Down
Loading

0 comments on commit 4f37a44

Please sign in to comment.