mod brightness;
mod bluetooth;
mod music;
mod volume;
mod weather;
use brightness::BrightnessController;
use bluetooth::BluetoothController;
use clap::{Parser, Subcommand};
use music::MusicController;
use volume::VolumeController;
use weather::WeatherController;
#[derive(Parser, Debug)]
#[command(name = "mac")]
#[command(about = "Control macOS system features and get weather info", long_about = None)]
struct Cli {
#[command(subcommand)]
command: Commands,
}
#[derive(Subcommand, Debug)]
enum Commands {
Brightness {
percentage: Option<f32>,
},
Volume {
percentage: Option<f32>,
},
#[command(subcommand)]
Music(MusicCommands),
Bluetooth,
Weather {
location: Option<String>,
},
}
#[derive(Subcommand, Debug)]
enum MusicCommands {
Play,
Pause,
Next,
Previous,
Current,
Playlists {
name: Option<String>,
#[arg(short, long)]
list: bool,
},
}
fn main() {
let cli = Cli::parse();
let result = match cli.command {
Commands::Brightness { percentage } => handle_brightness(percentage),
Commands::Volume { percentage } => handle_volume(percentage),
Commands::Music(music_cmd) => handle_music(music_cmd),
Commands::Bluetooth => handle_bluetooth(),
Commands::Weather { location } => handle_weather(location),
};
if let Err(e) = result {
eprintln!("Error: {}", e);
std::process::exit(1);
}
}
fn handle_brightness(percentage: Option<f32>) -> Result<(), String> {
let controller = BrightnessController::new()?;
match percentage {
Some(pct) => {
if pct == 0.0 {
return Err("Brightness cannot be 0".to_string());
}
if pct < 10.0 || pct > 100.0 {
return Err("Brightness must be between 10 and 100".to_string());
}
controller.set(pct / 100.0)?;
println!("Brightness set to {:.0}%", pct);
}
None => {
let brightness = controller.get()?;
println!("{:.0}%", brightness * 100.0);
}
}
Ok(())
}
fn handle_volume(percentage: Option<f32>) -> Result<(), String> {
let controller = VolumeController::new()?;
match percentage {
Some(pct) => {
if pct < 0.0 || pct > 100.0 {
return Err("Volume must be between 0 and 100".to_string());
}
controller.set(pct / 100.0)?;
println!("Volume set to {:.0}%", pct);
}
None => {
let volume = controller.get()?;
println!("{:.0}%", volume * 100.0);
}
}
Ok(())
}
fn handle_music(cmd: MusicCommands) -> Result<(), String> {
match cmd {
MusicCommands::Play => {
MusicController::play()?;
println!("Playing");
}
MusicCommands::Pause => {
MusicController::pause()?;
println!("Paused");
}
MusicCommands::Next => {
MusicController::next()?;
println!("Next track");
}
MusicCommands::Previous => {
MusicController::previous()?;
println!("Previous track");
}
MusicCommands::Current => {
let info = MusicController::current()?;
println!("{}", info);
}
MusicCommands::Playlists { name, list } => {
if list {
let playlists = MusicController::list_playlists()?;
if playlists.is_empty() {
println!("No playlists found");
} else {
println!("Playlists:");
for playlist in playlists {
println!(" - {}", playlist);
}
}
} else {
match name {
Some(playlist_name) => {
MusicController::play_playlist(&playlist_name)?;
println!("Playing playlist: {}", playlist_name);
std::thread::sleep(std::time::Duration::from_millis(500));
match MusicController::current() {
Ok(info) => println!("Now playing: {}", info),
Err(_) => {} }
}
None => {
match MusicController::play_playlist_interactive() {
Ok(selected) => {
println!("Playing playlist: {}", selected);
std::thread::sleep(std::time::Duration::from_millis(500));
match MusicController::current() {
Ok(info) => println!("Now playing: {}", info),
Err(_) => {}
}
}
Err(e) => return Err(e),
}
}
}
}
}
}
Ok(())
}
fn handle_bluetooth() -> Result<(), String> {
let devices = BluetoothController::list_devices_simple()?;
if devices.is_empty() {
println!("No Bluetooth devices found");
} else {
println!("Bluetooth Devices:");
for device in devices {
println!(" - {}", device);
}
}
Ok(())
}
fn handle_weather(location: Option<String>) -> Result<(), String> {
let location_ref = location.as_deref();
let weather = WeatherController::get_weather(location_ref)?;
println!("{}", weather);
Ok(())
}