feat(cli): add argument to start tv in another working directory (#132)

Resolves #111

---------

Co-authored-by: Alexandre Pasmantier <alex.pasmant@gmail.com>
This commit is contained in:
defigli 2024-12-15 22:13:11 +01:00 committed by GitHub
parent 3b8ab1fbd8
commit 882737d147
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 94 additions and 6 deletions

View File

@ -1,3 +1,5 @@
use std::path::Path;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use color_eyre::{eyre::eyre, Result}; use color_eyre::{eyre::eyre, Result};
@ -14,8 +16,8 @@ use television_channels::{
#[command(author, version = version(), about)] #[command(author, version = version(), about)]
pub struct Cli { pub struct Cli {
/// Which channel shall we watch? /// Which channel shall we watch?
#[arg(value_enum, default_value = "files", value_parser = channel_parser)] #[arg(value_enum, default_value = "files", index = 1)]
pub channel: ParsedCliChannel, pub channel: String,
/// Use a custom preview command (currently only supported by the stdin channel) /// Use a custom preview command (currently only supported by the stdin channel)
#[arg(short, long, value_name = "STRING")] #[arg(short, long, value_name = "STRING")]
@ -40,11 +42,15 @@ pub struct Cli {
#[arg(long, value_name = "STRING")] #[arg(long, value_name = "STRING")]
pub passthrough_keybindings: Option<String>, pub passthrough_keybindings: Option<String>,
/// The working directory to start in
#[arg(value_name = "PATH", index = 2)]
pub working_directory: Option<String>,
#[command(subcommand)] #[command(subcommand)]
command: Option<Command>, command: Option<Command>,
} }
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug, PartialEq)]
pub enum Command { pub enum Command {
/// Lists available channels /// Lists available channels
ListChannels, ListChannels,
@ -58,6 +64,7 @@ pub struct PostProcessedCli {
pub frame_rate: f64, pub frame_rate: f64,
pub passthrough_keybindings: Vec<String>, pub passthrough_keybindings: Vec<String>,
pub command: Option<Command>, pub command: Option<Command>,
pub working_directory: Option<String>,
} }
impl From<Cli> for PostProcessedCli { impl From<Cli> for PostProcessedCli {
@ -74,17 +81,48 @@ impl From<Cli> for PostProcessedCli {
delimiter: cli.delimiter.clone(), delimiter: cli.delimiter.clone(),
}); });
let channel: ParsedCliChannel;
let working_directory: Option<String>;
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 { Self {
channel: cli.channel, channel,
preview_command, preview_command,
tick_rate: cli.tick_rate, tick_rate: cli.tick_rate,
frame_rate: cli.frame_rate, frame_rate: cli.frame_rate,
passthrough_keybindings, passthrough_keybindings,
command: cli.command, 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)] #[derive(Debug, Clone, PartialEq)]
pub enum ParsedCliChannel { pub enum ParsedCliChannel {
Builtin(CliTvChannel), Builtin(CliTvChannel),
@ -189,13 +227,14 @@ mod tests {
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
fn test_from_cli() { fn test_from_cli() {
let cli = Cli { let cli = Cli {
channel: ParsedCliChannel::Builtin(CliTvChannel::Files), channel: "files".to_string(),
preview: Some("bat -n --color=always {}".to_string()), preview: Some("bat -n --color=always {}".to_string()),
delimiter: ":".to_string(), delimiter: ":".to_string(),
tick_rate: 50.0, tick_rate: 50.0,
frame_rate: 60.0, frame_rate: 60.0,
passthrough_keybindings: Some("q,ctrl-w,ctrl-t".to_string()), passthrough_keybindings: Some("q,ctrl-w,ctrl-t".to_string()),
command: None, command: None,
working_directory: Some("/home/user".to_string()),
}; };
let post_processed_cli: PostProcessedCli = cli.into(); let post_processed_cli: PostProcessedCli = cli.into();
@ -217,5 +256,36 @@ mod tests {
post_processed_cli.passthrough_keybindings, post_processed_cli.passthrough_keybindings,
vec!["q".to_string(), "ctrl-w".to_string(), "ctrl-t".to_string()] 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);
} }
} }

View File

@ -1,4 +1,6 @@
use std::env;
use std::io::{stdout, IsTerminal, Write}; use std::io::{stdout, IsTerminal, Write};
use std::path::Path;
use std::process::exit; use std::process::exit;
use clap::Parser; use clap::Parser;
@ -6,7 +8,7 @@ use cli::{list_channels, ParsedCliChannel, PostProcessedCli};
use color_eyre::Result; use color_eyre::Result;
use television_channels::channels::TelevisionChannel; use television_channels::channels::TelevisionChannel;
use television_channels::entry::PreviewType; use television_channels::entry::PreviewType;
use tracing::{debug, info}; use tracing::{debug, error, info};
use crate::app::App; use crate::app::App;
use crate::cli::Cli; 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( match App::new(
{ {
if is_readable_stdin() { if is_readable_stdin() {