mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-04 18:45:23 +00:00


230 lines
6.8 KiB
Rust
230 lines
6.8 KiB
Rust
use std::env;
|
|
use std::io::{stdout, BufWriter, IsTerminal, Write};
|
|
use std::path::Path;
|
|
use std::process::exit;
|
|
|
|
use anyhow::Result;
|
|
use clap::Parser;
|
|
use television::utils::clipboard::CLIPBOARD;
|
|
use tracing::{debug, error, info};
|
|
|
|
use television::app::App;
|
|
use television::channels::{
|
|
entry::PreviewType, stdin::Channel as StdinChannel, TelevisionChannel,
|
|
};
|
|
use television::cli::{
|
|
args::{Cli, Command},
|
|
guess_channel_from_prompt, list_channels, ParsedCliChannel,
|
|
PostProcessedCli,
|
|
};
|
|
|
|
use television::config::{Config, ConfigEnv};
|
|
use television::utils::shell::render_autocomplete_script_template;
|
|
use television::utils::{
|
|
shell::{completion_script, Shell},
|
|
stdin::is_readable_stdin,
|
|
};
|
|
|
|
#[tokio::main(flavor = "multi_thread")]
|
|
async fn main() -> Result<()> {
|
|
television::errors::init()?;
|
|
television::logging::init()?;
|
|
|
|
let args: PostProcessedCli = Cli::parse().into();
|
|
debug!("{:?}", args);
|
|
|
|
let mut config = Config::new(&ConfigEnv::init()?)?;
|
|
|
|
if let Some(command) = args.command {
|
|
match command {
|
|
Command::ListChannels => {
|
|
list_channels();
|
|
exit(0);
|
|
}
|
|
Command::InitShell { shell } => {
|
|
let target_shell = Shell::from(shell);
|
|
// the completion scripts for the various shells are templated
|
|
// so that it's possible to override the keybindings triggering
|
|
// shell autocomplete and command history in tv
|
|
let script = render_autocomplete_script_template(
|
|
target_shell,
|
|
completion_script(target_shell)?,
|
|
&config.shell_integration,
|
|
)?;
|
|
println!("{script}");
|
|
exit(0);
|
|
}
|
|
}
|
|
}
|
|
|
|
config.config.tick_rate =
|
|
args.tick_rate.unwrap_or(config.config.tick_rate);
|
|
if args.no_preview {
|
|
config.ui.show_preview_panel = false;
|
|
}
|
|
|
|
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)?;
|
|
}
|
|
|
|
CLIPBOARD.with(<_>::default);
|
|
|
|
let channel =
|
|
determine_channel(args.clone(), &config, is_readable_stdin())?;
|
|
|
|
let mut app =
|
|
App::new(channel, config, &args.passthrough_keybindings, args.input);
|
|
|
|
stdout().flush()?;
|
|
let output = app.run(stdout().is_terminal(), false).await?;
|
|
info!("{:?}", output);
|
|
// lock stdout
|
|
let stdout_handle = stdout().lock();
|
|
let mut bufwriter = BufWriter::new(stdout_handle);
|
|
if let Some(passthrough) = output.passthrough {
|
|
writeln!(bufwriter, "{passthrough}")?;
|
|
}
|
|
if let Some(entries) = output.selected_entries {
|
|
for entry in &entries {
|
|
writeln!(bufwriter, "{}", entry.stdout_repr())?;
|
|
}
|
|
}
|
|
bufwriter.flush()?;
|
|
exit(0);
|
|
}
|
|
|
|
pub fn determine_channel(
|
|
args: PostProcessedCli,
|
|
config: &Config,
|
|
readable_stdin: bool,
|
|
) -> Result<TelevisionChannel> {
|
|
if readable_stdin {
|
|
debug!("Using stdin channel");
|
|
Ok(TelevisionChannel::Stdin(StdinChannel::new(
|
|
args.preview_command.map(PreviewType::Command),
|
|
)))
|
|
} else if let Some(prompt) = args.autocomplete_prompt {
|
|
let channel = guess_channel_from_prompt(
|
|
&prompt,
|
|
&config.shell_integration.commands,
|
|
)?;
|
|
debug!("Using guessed channel: {:?}", channel);
|
|
match channel {
|
|
ParsedCliChannel::Builtin(c) => Ok(c.to_channel()),
|
|
ParsedCliChannel::Cable(c) => {
|
|
Ok(TelevisionChannel::Cable(c.into()))
|
|
}
|
|
}
|
|
} else {
|
|
debug!("Using {:?} channel", args.channel);
|
|
match args.channel {
|
|
ParsedCliChannel::Builtin(c) => Ok(c.to_channel()),
|
|
ParsedCliChannel::Cable(c) => {
|
|
Ok(TelevisionChannel::Cable(c.into()))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use rustc_hash::FxHashMap;
|
|
|
|
use super::*;
|
|
|
|
fn assert_is_correct_channel(
|
|
args: &PostProcessedCli,
|
|
config: &Config,
|
|
readable_stdin: bool,
|
|
expected_channel: &TelevisionChannel,
|
|
) {
|
|
let channel =
|
|
determine_channel(args.clone(), config, readable_stdin).unwrap();
|
|
|
|
assert!(
|
|
channel.name() == expected_channel.name(),
|
|
"Expected {:?} but got {:?}",
|
|
expected_channel.name(),
|
|
channel.name()
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_determine_channel_readable_stdin() {
|
|
let channel = television::cli::ParsedCliChannel::Builtin(
|
|
television::channels::CliTvChannel::Env,
|
|
);
|
|
let args = PostProcessedCli {
|
|
channel,
|
|
..Default::default()
|
|
};
|
|
let config = Config::default();
|
|
assert_is_correct_channel(
|
|
&args,
|
|
&config,
|
|
true,
|
|
&TelevisionChannel::Stdin(StdinChannel::new(None)),
|
|
);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_determine_channel_autocomplete_prompt() {
|
|
let autocomplete_prompt = Some("cd".to_string());
|
|
let expected_channel = television::channels::TelevisionChannel::Dirs(
|
|
television::channels::dirs::Channel::default(),
|
|
);
|
|
let args = PostProcessedCli {
|
|
autocomplete_prompt,
|
|
..Default::default()
|
|
};
|
|
let mut config = Config {
|
|
shell_integration:
|
|
television::config::shell_integration::ShellIntegrationConfig {
|
|
commands: FxHashMap::default(),
|
|
channel_triggers: {
|
|
let mut m = FxHashMap::default();
|
|
m.insert("dirs".to_string(), vec!["cd".to_string()]);
|
|
m
|
|
},
|
|
keybindings: FxHashMap::default(),
|
|
},
|
|
..Default::default()
|
|
};
|
|
config.shell_integration.merge_triggers();
|
|
|
|
assert_is_correct_channel(&args, &config, false, &expected_channel);
|
|
}
|
|
|
|
#[tokio::test]
|
|
async fn test_determine_channel_standard_case() {
|
|
let channel = television::cli::ParsedCliChannel::Builtin(
|
|
television::channels::CliTvChannel::Dirs,
|
|
);
|
|
let args = PostProcessedCli {
|
|
channel,
|
|
..Default::default()
|
|
};
|
|
let config = Config::default();
|
|
assert_is_correct_channel(
|
|
&args,
|
|
&config,
|
|
false,
|
|
&TelevisionChannel::Dirs(
|
|
television::channels::dirs::Channel::default(),
|
|
),
|
|
);
|
|
}
|
|
}
|