use std::collections::HashMap;
use crate::{
Controller, OpenRgbError, OpenRgbResult, client::command::CommandGroup, data::DeviceType,
};
pub trait ControllerIndex {
fn controller_id(&self) -> usize;
fn index<'a>(&self, group: &'a ControllerGroup) -> OpenRgbResult<&'a Controller> {
group.controllers.get(self.controller_id()).ok_or_else(|| {
OpenRgbError::CommandError(format!(
"Controller with index {} not found",
self.controller_id()
))
})
}
fn remove(&self, group: &mut ControllerGroup) -> OpenRgbResult<Controller> {
let index = self.controller_id();
if index >= group.controllers.len() {
return Err(OpenRgbError::CommandError(format!(
"Controller with index {index} not found"
)));
}
Ok(group.controllers.remove(index))
}
}
impl ControllerIndex for usize {
fn controller_id(&self) -> usize {
*self
}
}
impl ControllerIndex for &Controller {
fn controller_id(&self) -> usize {
self.id()
}
}
impl ControllerIndex for Controller {
fn controller_id(&self) -> usize {
(&self).controller_id()
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct ControllerGroup {
controllers: Vec<Controller>,
}
impl ControllerGroup {
pub(crate) fn new(controllers: Vec<Controller>) -> Self {
Self { controllers }
}
fn empty() -> Self {
Self {
controllers: Vec::new(),
}
}
pub fn controllers(&self) -> &[Controller] {
&self.controllers
}
pub fn controllers_mut(&mut self) -> &mut [Controller] {
&mut self.controllers
}
pub fn is_empty(&self) -> bool {
self.controllers.is_empty()
}
pub fn len(&self) -> usize {
self.controllers.len()
}
pub fn iter(&self) -> impl Iterator<Item = &Controller> {
self.controllers.iter()
}
pub fn get_controller<I>(&self, idx: I) -> OpenRgbResult<&Controller>
where
I: ControllerIndex,
{
idx.index(self)
}
pub fn split_per_type(self) -> HashMap<DeviceType, ControllerGroup> {
self.controllers
.into_iter()
.fold(HashMap::new(), |mut acc, controller| {
let entry = acc
.entry(controller.device_type())
.or_insert_with(ControllerGroup::empty);
entry.controllers.push(controller);
acc
})
}
pub fn get_by_type(&self, device_type: DeviceType) -> impl Iterator<Item = &Controller> {
self.controllers
.iter()
.filter(move |c| c.device_type() == device_type)
}
pub fn into_first(self) -> OpenRgbResult<Controller> {
self.controllers
.into_iter()
.next()
.ok_or_else(|| OpenRgbError::CommandError("No controllers in group".to_owned()))
}
pub fn cmd(&self) -> CommandGroup<'_> {
CommandGroup::new(self)
}
pub async fn init(&self) -> OpenRgbResult<()> {
for controller in &self.controllers {
controller.init().await?;
}
Ok(())
}
pub async fn set_controllable_mode(&self) -> OpenRgbResult<()> {
for controller in &self.controllers {
controller.set_controllable_mode().await?;
}
Ok(())
}
pub async fn turn_off_leds(&self) -> OpenRgbResult<()> {
for controller in &self.controllers {
controller.turn_off_leds().await?;
}
Ok(())
}
}
impl IntoIterator for ControllerGroup {
type Item = Controller;
type IntoIter = <Vec<Controller> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.controllers.into_iter()
}
}
impl<'a> IntoIterator for &'a ControllerGroup {
type Item = &'a Controller;
type IntoIter = <&'a Vec<Controller> as IntoIterator>::IntoIter;
fn into_iter(self) -> Self::IntoIter {
self.controllers.iter()
}
}
#[cfg(test)]
mod tests {
use crate::OpenRgbClient;
use super::*;
#[tokio::test]
#[ignore = "can only test with openrgb running"]
async fn test_group() -> OpenRgbResult<()> {
let client = OpenRgbClient::connect().await?;
let group = client.get_all_controllers().await?;
group.init().await?;
Ok(())
}
#[tokio::test]
#[ignore = "can only test with openrgb running"]
async fn test_per_type() -> OpenRgbResult<()> {
let client = OpenRgbClient::connect().await?;
let group = client.get_all_controllers().await?;
let split = group.split_per_type();
for (device_type, controllers) in split {
println!("Device type: {device_type:?}");
for controller in controllers.controllers() {
println!(" Controller: {} ({})", controller.name(), controller.id());
}
}
Ok(())
}
}