use std::collections::HashMap;
use crate::{
Color, Controller, OpenRgbError, OpenRgbResult,
client::group::{ControllerGroup, ControllerIndex},
};
enum SetLedCommand {
Controller {
colors: Vec<Color>,
},
Zone {
zone_id: usize,
colors: Vec<Color>,
},
Segment {
zone_id: usize,
segment_id: usize,
colors: Vec<Color>,
},
Single {
led_id: usize,
color: Color,
},
}
pub struct CommandGroup<'a> {
group: &'a ControllerGroup,
commands: HashMap<usize, Command<'a>>,
}
impl<'a> CommandGroup<'a> {
pub(crate) fn new(group: &'a ControllerGroup) -> Self {
let map = group
.controllers()
.iter()
.map(|c| (c.id(), Command::new(c)))
.collect();
Self {
group,
commands: map,
}
}
pub async fn execute(self) -> OpenRgbResult<()> {
for cmd in self.commands.into_values() {
cmd.execute().await?;
}
Ok(())
}
fn get_cmd_mut(
&mut self,
controller_id: impl ControllerIndex,
) -> OpenRgbResult<&mut Command<'a>> {
let c = self.group.get_controller(controller_id)?;
self.commands.get_mut(&c.id()).ok_or_else(|| {
OpenRgbError::CommandError(format!("Controller with id {} not found in group", c.id()))
})
}
pub fn set_controller_led<C: Into<Color>>(
&mut self,
controller_id: impl ControllerIndex,
led_id: usize,
color: C,
) -> OpenRgbResult<()> {
let cmd = self.get_cmd_mut(controller_id)?;
cmd.set_led(led_id, color)
}
pub fn set_controller_leds<C: Into<Color>>(
&mut self,
controller_id: impl ControllerIndex,
colors: impl IntoIterator<Item = C>,
) -> OpenRgbResult<()> {
let cmd = self.get_cmd_mut(controller_id)?;
cmd.set_leds(colors)
}
pub fn set_controller_zone_leds<C: Into<Color>>(
&mut self,
controller_id: impl ControllerIndex,
zone_id: usize,
colors: impl IntoIterator<Item = C>,
) -> OpenRgbResult<()> {
let cmd = self.get_cmd_mut(controller_id)?;
cmd.set_zone_leds(zone_id, colors)
}
pub fn set_controller_zone_led<C: Into<Color>>(
&mut self,
controller_id: impl ControllerIndex,
zone_id: usize,
led_idx: usize,
color: C,
) -> OpenRgbResult<()> {
let cmd = self.get_cmd_mut(controller_id)?;
cmd.set_zone_led(zone_id, led_idx, color)
}
pub fn set_controller_segment_leds<C: Into<Color>>(
&mut self,
controller_id: impl ControllerIndex,
zone_id: usize,
segment_id: usize,
colors: impl IntoIterator<Item = C>,
) -> OpenRgbResult<()> {
let cmd = self.get_cmd_mut(controller_id)?;
cmd.set_segment_leds(zone_id, segment_id, colors)
}
}
#[derive(Debug)]
pub struct Command<'a> {
controller: &'a Controller,
colors: Vec<Color>,
}
impl<'a> Command<'a> {
pub(crate) fn new(controller: &'a Controller) -> Self {
Self {
controller,
colors: Vec::with_capacity(controller.num_leds()),
}
}
pub async fn execute(self) -> OpenRgbResult<()> {
self.controller.set_leds(self.colors).await?;
Ok(())
}
pub fn set_led<C: Into<Color>>(&mut self, led_id: usize, color: C) -> OpenRgbResult<()> {
self.add_command(SetLedCommand::Single {
led_id,
color: color.into(),
})
}
pub fn set_leds<C: Into<Color>>(
&mut self,
colors: impl IntoIterator<Item = C>,
) -> OpenRgbResult<()> {
self.add_command(SetLedCommand::Controller {
colors: colors.into_iter().map(Into::into).collect(),
})
}
pub fn set_zone_led<C: Into<Color>>(
&mut self,
zone_id: usize,
led_idx: usize,
color: C,
) -> OpenRgbResult<()> {
self.add_command(SetLedCommand::Single {
led_id: self.controller.get_zone_led_offset(zone_id)? + led_idx,
color: color.into(),
})
}
pub fn set_zone_leds<C: Into<Color>>(
&mut self,
zone_id: usize,
colors: impl IntoIterator<Item = C>,
) -> OpenRgbResult<()> {
self.add_command(SetLedCommand::Zone {
zone_id,
colors: colors.into_iter().map(Into::into).collect(),
})
}
pub fn set_segment_led<C: Into<Color>>(
&mut self,
zone_id: usize,
segment_id: usize,
led_idx: usize,
color: C,
) -> OpenRgbResult<()> {
let led_id = led_idx
+ self
.controller
.get_zone(zone_id)?
.get_segment(segment_id)?
.offset();
self.add_command(SetLedCommand::Single {
led_id,
color: color.into(),
})
}
pub fn set_segment_leds<C: Into<Color>>(
&mut self,
zone_id: usize,
segment_id: usize,
colors: impl IntoIterator<Item = C>,
) -> OpenRgbResult<()> {
self.add_command(SetLedCommand::Segment {
zone_id,
segment_id,
colors: colors.into_iter().map(Into::into).collect(),
})
}
fn add_command(&mut self, cmd: SetLedCommand) -> OpenRgbResult<()> {
match cmd {
SetLedCommand::Controller { colors } => {
if colors.len() > self.controller.num_leds() {
return Err(OpenRgbError::CommandError(format!(
"Controller {} was given {} colors, while its length is {}",
self.controller.name(),
colors.len(),
self.controller.num_leds()
)));
}
self.set_colors(0, &colors)?;
}
SetLedCommand::Zone { zone_id, colors } => {
let zone = self.controller.get_zone(zone_id)?;
if colors.len() >= zone.num_leds() {
return Err(OpenRgbError::CommandError(format!(
"Zone {} for controller {} was given {} colors, while its length is {}. This might become a hard error in the future.",
zone_id,
self.controller.name(),
colors.len(),
zone.num_leds()
)));
}
let offset = self.controller.get_zone_led_offset(zone_id)?;
let len = colors.len().min(zone.num_leds());
self.set_colors(offset, &colors[..len])?;
}
SetLedCommand::Segment {
zone_id,
segment_id,
colors,
} => {
let zone = self.controller.get_zone(zone_id)?;
let seg = zone.get_segment(segment_id)?;
if colors.len() >= seg.num_leds() {
return Err(OpenRgbError::CommandError(format!(
"Segment {} for zone {} in controller {} was given {} colors, while its length is {}. This might become a hard error in the future.",
seg.name(),
zone_id,
self.controller.name(),
colors.len(),
seg.num_leds()
)));
}
let offset = zone.offset() + seg.offset();
self.set_colors(offset, &colors)?;
}
SetLedCommand::Single { led_id, color } => {
if led_id >= self.controller.num_leds() {
return Err(OpenRgbError::CommandError(format!(
"LED id {} is out of bounds for controller {} with {} LEDs",
led_id,
self.controller.name(),
self.controller.num_leds()
)));
}
self.set_colors(led_id, &[color])?;
}
}
Ok(())
}
fn set_colors(&mut self, offset: usize, colors: &[Color]) -> OpenRgbResult<()> {
let len = offset + colors.len();
if self.controller.colors().len() < len {
return Err(OpenRgbError::CommandError(format!(
"Cannot set {} colors at offset {}, controller only has {} colors",
colors.len(),
offset,
self.controller.colors().len()
)));
}
if self.colors.len() < len {
self.colors.resize(len, Color::default());
}
self.colors[offset..len].copy_from_slice(colors);
Ok(())
}
}