mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-06 19:45:23 +00:00
feat(cable): support cable channel invocation through the cli (#116)
This commit is contained in:
parent
913aa85af0
commit
937d0f0758
@ -635,7 +635,6 @@ pub fn results_list_benchmark(c: &mut Criterion) {
|
||||
result_line_number_fg: Color::Indexed(222),
|
||||
result_selected_bg: Color::Indexed(222),
|
||||
match_foreground_color: Color::Indexed(222),
|
||||
pointer_fg: Color::Indexed(222),
|
||||
};
|
||||
|
||||
c.bench_function("results_list", |b| {
|
||||
|
@ -4,7 +4,7 @@ use std::{
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug, serde::Deserialize)]
|
||||
#[derive(Clone, Debug, serde::Deserialize, PartialEq)]
|
||||
pub struct CableChannelPrototype {
|
||||
pub name: String,
|
||||
pub source_command: String,
|
||||
|
@ -56,7 +56,7 @@ fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream {
|
||||
);
|
||||
|
||||
// create the CliTvChannel enum
|
||||
let cli_enum_variants = variants
|
||||
let cli_enum_variants: Vec<_> = variants
|
||||
.iter()
|
||||
.filter(|variant| !has_attribute(&variant.attrs, EXCLUDE_FROM_CLI))
|
||||
.map(|variant| {
|
||||
@ -64,7 +64,22 @@ fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream {
|
||||
quote! {
|
||||
#variant_name
|
||||
}
|
||||
});
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Generate lowercase mappings for the TryFrom implementation
|
||||
let lowercase_match_arms: Vec<_> = variants
|
||||
.iter()
|
||||
.filter(|variant| !has_attribute(&variant.attrs, EXCLUDE_FROM_CLI))
|
||||
.map(|variant| {
|
||||
let variant_name = &variant.ident;
|
||||
let lowercase_name = variant_name.to_string().to_lowercase();
|
||||
quote! {
|
||||
#lowercase_name => Ok(Self::#variant_name),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
let cli_enum = quote! {
|
||||
use clap::ValueEnum;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -76,6 +91,19 @@ fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream {
|
||||
#[default]
|
||||
#(#cli_enum_variants),*
|
||||
}
|
||||
|
||||
impl std::convert::TryFrom<&str> for CliTvChannel {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(channel: &str) -> Result<Self, Self::Error> {
|
||||
match channel {
|
||||
#(
|
||||
#lowercase_match_arms
|
||||
)*
|
||||
_ => Err(format!("Invalid cli channel name: {}", channel)),
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Generate the match arms for the `to_channel` method
|
||||
@ -110,6 +138,14 @@ fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream {
|
||||
#(#arms),*
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all_channels() -> Vec<String> {
|
||||
vec![
|
||||
#(
|
||||
stringify!(#cli_enum_variants).to_lowercase(),
|
||||
)*
|
||||
]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,14 +1,21 @@
|
||||
use clap::Parser;
|
||||
use clap::{Parser, Subcommand};
|
||||
use color_eyre::{eyre::eyre, Result};
|
||||
|
||||
use crate::config::{get_config_dir, get_data_dir};
|
||||
use television_channels::{channels::CliTvChannel, entry::PreviewCommand};
|
||||
use crate::{
|
||||
cable,
|
||||
config::{get_config_dir, get_data_dir},
|
||||
};
|
||||
use television_channels::{
|
||||
cable::CableChannelPrototype, channels::CliTvChannel,
|
||||
entry::PreviewCommand,
|
||||
};
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(author, version = version(), about)]
|
||||
pub struct Cli {
|
||||
/// Which channel shall we watch?
|
||||
#[arg(value_enum, default_value = "files")]
|
||||
pub channel: CliTvChannel,
|
||||
#[arg(value_enum, default_value = "files", value_parser = channel_parser)]
|
||||
pub channel: ParsedCliChannel,
|
||||
|
||||
/// Use a custom preview command (currently only supported by the stdin channel)
|
||||
#[arg(short, long, value_name = "STRING")]
|
||||
@ -32,15 +39,25 @@ pub struct Cli {
|
||||
/// to be handled by the parent process.
|
||||
#[arg(long, value_name = "STRING")]
|
||||
pub passthrough_keybindings: Option<String>,
|
||||
|
||||
#[command(subcommand)]
|
||||
command: Option<Command>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand, Debug)]
|
||||
pub enum Command {
|
||||
/// Lists available channels
|
||||
ListChannels,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PostProcessedCli {
|
||||
pub channel: CliTvChannel,
|
||||
pub channel: ParsedCliChannel,
|
||||
pub preview_command: Option<PreviewCommand>,
|
||||
pub tick_rate: f64,
|
||||
pub frame_rate: f64,
|
||||
pub passthrough_keybindings: Vec<String>,
|
||||
pub command: Option<Command>,
|
||||
}
|
||||
|
||||
impl From<Cli> for PostProcessedCli {
|
||||
@ -63,10 +80,66 @@ impl From<Cli> for PostProcessedCli {
|
||||
tick_rate: cli.tick_rate,
|
||||
frame_rate: cli.frame_rate,
|
||||
passthrough_keybindings,
|
||||
command: cli.command,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ParsedCliChannel {
|
||||
Builtin(CliTvChannel),
|
||||
Cable(CableChannelPrototype),
|
||||
}
|
||||
|
||||
fn channel_parser(channel: &str) -> Result<ParsedCliChannel> {
|
||||
let cable_channels = cable::load_cable_channels()?;
|
||||
CliTvChannel::try_from(channel)
|
||||
.map(ParsedCliChannel::Builtin)
|
||||
.or_else(|_| {
|
||||
cable_channels
|
||||
.iter()
|
||||
.find(|(k, _)| k.to_lowercase() == channel)
|
||||
.map_or_else(
|
||||
|| Err(eyre!("Unknown channel: {}", channel)),
|
||||
|(_, v)| Ok(ParsedCliChannel::Cable(v.clone())),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn list_cable_channels() -> Vec<String> {
|
||||
cable::load_cable_channels()
|
||||
.unwrap_or_default()
|
||||
.iter()
|
||||
.map(|(k, _)| k.clone())
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn list_builtin_channels() -> Vec<String> {
|
||||
CliTvChannel::all_channels()
|
||||
.iter()
|
||||
.map(std::string::ToString::to_string)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn list_channels() {
|
||||
println!("\x1b[4mBuiltin channels:\x1b[0m");
|
||||
for c in list_builtin_channels() {
|
||||
println!("\t{c}");
|
||||
}
|
||||
println!("\n\x1b[4mCustom channels:\x1b[0m");
|
||||
for c in list_cable_channels().iter().map(|c| c.to_lowercase()) {
|
||||
println!("\t{c}");
|
||||
}
|
||||
}
|
||||
|
||||
fn delimiter_parser(s: &str) -> Result<String, String> {
|
||||
Ok(match s {
|
||||
"" => ":".to_string(),
|
||||
"\\t" => "\t".to_string(),
|
||||
_ => s.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
const VERSION_MESSAGE: &str = concat!(
|
||||
env!("CARGO_PKG_VERSION"),
|
||||
"\ntarget triple: ",
|
||||
@ -116,17 +189,21 @@ mod tests {
|
||||
#[allow(clippy::float_cmp)]
|
||||
fn test_from_cli() {
|
||||
let cli = Cli {
|
||||
channel: CliTvChannel::Files,
|
||||
channel: ParsedCliChannel::Builtin(CliTvChannel::Files),
|
||||
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,
|
||||
};
|
||||
|
||||
let post_processed_cli: PostProcessedCli = cli.into();
|
||||
|
||||
assert_eq!(post_processed_cli.channel, CliTvChannel::Files);
|
||||
assert_eq!(
|
||||
post_processed_cli.channel,
|
||||
ParsedCliChannel::Builtin(CliTvChannel::Files)
|
||||
);
|
||||
assert_eq!(
|
||||
post_processed_cli.preview_command,
|
||||
Some(PreviewCommand {
|
||||
@ -142,11 +219,3 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn delimiter_parser(s: &str) -> Result<String, String> {
|
||||
Ok(match s {
|
||||
"" => ":".to_string(),
|
||||
"\\t" => "\t".to_string(),
|
||||
_ => s.to_string(),
|
||||
})
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ use std::io::{stdout, IsTerminal, Write};
|
||||
use std::process::exit;
|
||||
|
||||
use clap::Parser;
|
||||
use cli::PostProcessedCli;
|
||||
use cli::{list_channels, ParsedCliChannel, PostProcessedCli};
|
||||
use color_eyre::Result;
|
||||
use television_channels::channels::TelevisionChannel;
|
||||
use television_channels::entry::PreviewType;
|
||||
@ -34,9 +34,17 @@ async fn main() -> Result<()> {
|
||||
logging::init()?;
|
||||
|
||||
let args: PostProcessedCli = Cli::parse().into();
|
||||
|
||||
debug!("{:?}", args);
|
||||
|
||||
if let Some(command) = args.command {
|
||||
match command {
|
||||
cli::Command::ListChannels => {
|
||||
list_channels();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
match App::new(
|
||||
{
|
||||
if is_readable_stdin() {
|
||||
@ -46,7 +54,12 @@ async fn main() -> Result<()> {
|
||||
))
|
||||
} else {
|
||||
debug!("Using {:?} channel", args.channel);
|
||||
args.channel.to_channel()
|
||||
match args.channel {
|
||||
ParsedCliChannel::Builtin(c) => c.to_channel(),
|
||||
ParsedCliChannel::Cable(c) => {
|
||||
TelevisionChannel::Cable(c.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
args.tick_rate,
|
||||
|
Loading…
x
Reference in New Issue
Block a user