diff --git a/crates/television/cli.rs b/crates/television/cli.rs index 309ece1..5c84214 100644 --- a/crates/television/cli.rs +++ b/crates/television/cli.rs @@ -1,3 +1,5 @@ +use std::path::Path; + use clap::{Parser, Subcommand}; use color_eyre::{eyre::eyre, Result}; @@ -14,8 +16,8 @@ use television_channels::{ #[command(author, version = version(), about)] pub struct Cli { /// Which channel shall we watch? - #[arg(value_enum, default_value = "files", value_parser = channel_parser)] - pub channel: ParsedCliChannel, + #[arg(value_enum, default_value = "files", index = 1)] + pub channel: String, /// Use a custom preview command (currently only supported by the stdin channel) #[arg(short, long, value_name = "STRING")] @@ -40,11 +42,15 @@ pub struct Cli { #[arg(long, value_name = "STRING")] pub passthrough_keybindings: Option, + /// The working directory to start in + #[arg(value_name = "PATH", index = 2)] + pub working_directory: Option, + #[command(subcommand)] command: Option, } -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Debug, PartialEq)] pub enum Command { /// Lists available channels ListChannels, @@ -58,6 +64,7 @@ pub struct PostProcessedCli { pub frame_rate: f64, pub passthrough_keybindings: Vec, pub command: Option, + pub working_directory: Option, } impl From for PostProcessedCli { @@ -74,17 +81,48 @@ impl From for PostProcessedCli { delimiter: cli.delimiter.clone(), }); + let channel: ParsedCliChannel; + let working_directory: Option; + + match channel_parser(&cli.channel) { + Ok(p) => { + channel = p; + working_directory = cli.working_directory; + } + Err(_) => { + // if the path is provided as first argument and it exists, use it as the working + // directory and default to the files channel + if cli.working_directory.is_none() + && Path::new(&cli.channel).exists() + { + channel = ParsedCliChannel::Builtin(CliTvChannel::Files); + working_directory = Some(cli.channel.clone()); + } else { + unknown_channel_exit(&cli.channel); + unreachable!(); + } + } + } + Self { - channel: cli.channel, + channel, preview_command, tick_rate: cli.tick_rate, frame_rate: cli.frame_rate, passthrough_keybindings, command: cli.command, + working_directory, } } } +fn unknown_channel_exit(channel: &str) { + eprintln!("Unknown channel: {channel}\n"); + // print the list of channels + list_channels(); + std::process::exit(1); +} + #[derive(Debug, Clone, PartialEq)] pub enum ParsedCliChannel { Builtin(CliTvChannel), @@ -189,13 +227,14 @@ mod tests { #[allow(clippy::float_cmp)] fn test_from_cli() { let cli = Cli { - channel: ParsedCliChannel::Builtin(CliTvChannel::Files), + channel: "files".to_string(), preview: Some("bat -n --color=always {}".to_string()), delimiter: ":".to_string(), tick_rate: 50.0, frame_rate: 60.0, passthrough_keybindings: Some("q,ctrl-w,ctrl-t".to_string()), command: None, + working_directory: Some("/home/user".to_string()), }; let post_processed_cli: PostProcessedCli = cli.into(); @@ -217,5 +256,36 @@ mod tests { post_processed_cli.passthrough_keybindings, vec!["q".to_string(), "ctrl-w".to_string(), "ctrl-t".to_string()] ); + assert_eq!( + post_processed_cli.working_directory, + Some("/home/user".to_string()) + ); + } + + #[test] + #[allow(clippy::float_cmp)] + fn test_from_cli_no_args() { + let cli = Cli { + channel: ".".to_string(), + preview: None, + delimiter: ":".to_string(), + tick_rate: 50.0, + frame_rate: 60.0, + passthrough_keybindings: None, + command: None, + working_directory: None, + }; + + let post_processed_cli: PostProcessedCli = cli.into(); + + assert_eq!( + post_processed_cli.channel, + ParsedCliChannel::Builtin(CliTvChannel::Files) + ); + assert_eq!( + post_processed_cli.working_directory, + Some(".".to_string()) + ); + assert_eq!(post_processed_cli.command, None); } } diff --git a/crates/television/main.rs b/crates/television/main.rs index 9d11fc1..d0c8172 100644 --- a/crates/television/main.rs +++ b/crates/television/main.rs @@ -1,4 +1,6 @@ +use std::env; use std::io::{stdout, IsTerminal, Write}; +use std::path::Path; use std::process::exit; use clap::Parser; @@ -6,7 +8,7 @@ use cli::{list_channels, ParsedCliChannel, PostProcessedCli}; use color_eyre::Result; use television_channels::channels::TelevisionChannel; use television_channels::entry::PreviewType; -use tracing::{debug, info}; +use tracing::{debug, error, info}; use crate::app::App; use crate::cli::Cli; @@ -45,6 +47,22 @@ async fn main() -> Result<()> { } } + if let Some(working_directory) = args.working_directory { + let path = Path::new(&working_directory); + if !path.exists() { + error!( + "Working directory \"{}\" does not exist", + &working_directory + ); + println!( + "Error: Working directory \"{}\" does not exist", + &working_directory + ); + exit(1); + } + env::set_current_dir(path)?; + } + match App::new( { if is_readable_stdin() {