use anyhow::Result;
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct AudioRoute {
pub source: String,
pub destination: String,
pub channels: Vec<usize>,
pub mix_level: f32,
}
impl Default for AudioRoute {
fn default() -> Self {
Self {
source: String::new(),
destination: "master".to_string(),
channels: vec![0, 1], mix_level: 1.0,
}
}
}
pub fn execute_routing(
route: &AudioRoute,
routing_table: &mut HashMap<String, Vec<AudioRoute>>,
) -> Result<()> {
if route.source.is_empty() {
anyhow::bail!("Routing source cannot be empty");
}
if route.destination.is_empty() {
anyhow::bail!("Routing destination cannot be empty");
}
if route.mix_level < 0.0 || route.mix_level > 2.0 {
anyhow::bail!(
"Mix level must be between 0.0 and 2.0, got: {}",
route.mix_level
);
}
if route.channels.is_empty() {
anyhow::bail!("Routing must specify at least one channel");
}
routing_table
.entry(route.source.clone())
.or_insert_with(Vec::new)
.push(route.clone());
#[cfg(feature = "cli")]
{
eprintln!(
"Route: {} -> {} ({} ch @ {:.2})",
route.source,
route.destination,
route.channels.len(),
route.mix_level
);
}
Ok(())
}
pub fn create_default_route(source: String) -> AudioRoute {
AudioRoute {
source,
destination: "master".to_string(),
channels: vec![0, 1],
mix_level: 1.0,
}
}
pub fn parse_routing_expr(expr: &str) -> Result<AudioRoute> {
let parts: Vec<&str> = expr.split("->").collect();
if parts.len() != 2 {
anyhow::bail!("Invalid routing expression: {}", expr);
}
let source = parts[0].trim().to_string();
let dest_and_level: Vec<&str> = parts[1].split('@').collect();
let destination = dest_and_level[0].trim().to_string();
let mix_level = if dest_and_level.len() > 1 {
dest_and_level[1]
.trim()
.parse::<f32>()
.map_err(|_| anyhow::anyhow!("Invalid mix level: {}", dest_and_level[1]))?
} else {
1.0
};
Ok(AudioRoute {
source,
destination,
channels: vec![0, 1],
mix_level,
})
}