type channels with an enum rather than a dyn trait

This commit is contained in:
Alexandre Pasmantier 2024-10-20 13:03:01 +02:00
parent db3aa1ad49
commit 10f302546d
6 changed files with 61 additions and 46 deletions

View File

@ -56,7 +56,7 @@ use color_eyre::Result;
use tokio::sync::{mpsc, Mutex}; use tokio::sync::{mpsc, Mutex};
use tracing::{debug, info}; use tracing::{debug, info};
use crate::channels::CliTvChannel; use crate::channels::{AvailableChannel, CliTvChannel};
use crate::television::Television; use crate::television::Television;
use crate::{ use crate::{
action::Action, action::Action,
@ -84,7 +84,7 @@ pub struct App {
impl App { impl App {
pub fn new( pub fn new(
channel: CliTvChannel, channel: AvailableChannel,
tick_rate: f64, tick_rate: f64,
frame_rate: f64, frame_rate: f64,
) -> Result<Self> { ) -> Result<Self> {

View File

@ -7,7 +7,7 @@ pub mod channels;
mod env; mod env;
mod files; mod files;
mod git_repos; mod git_repos;
mod stdin; pub mod stdin;
mod text; mod text;
/// The interface that all television channels must implement. /// The interface that all television channels must implement.
@ -97,6 +97,7 @@ pub enum AvailableChannel {
Text(text::Channel), Text(text::Channel),
Stdin(stdin::Channel), Stdin(stdin::Channel),
Alias(alias::Channel), Alias(alias::Channel),
Channel(channels::SelectionChannel),
} }
/// NOTE: this could be generated by a derive macro /// NOTE: this could be generated by a derive macro
@ -104,7 +105,7 @@ impl TryFrom<&Entry> for AvailableChannel {
type Error = String; type Error = String;
fn try_from(entry: &Entry) -> Result<Self, Self::Error> { fn try_from(entry: &Entry) -> Result<Self, Self::Error> {
match entry.name.as_ref() { match entry.name.to_ascii_lowercase().as_ref() {
"env" => Ok(AvailableChannel::Env(env::Channel::default())), "env" => Ok(AvailableChannel::Env(env::Channel::default())),
"files" => Ok(AvailableChannel::Files(files::Channel::default())), "files" => Ok(AvailableChannel::Files(files::Channel::default())),
"gitrepos" => { "gitrepos" => {

View File

@ -8,7 +8,7 @@ use nucleo::{
}; };
use crate::{ use crate::{
channels::{CliTvChannel, TelevisionChannel}, channels::{AvailableChannel, CliTvChannel, TelevisionChannel},
entry::Entry, entry::Entry,
fuzzy::MATCHER, fuzzy::MATCHER,
previewers::PreviewType, previewers::PreviewType,
@ -24,8 +24,6 @@ pub struct SelectionChannel {
const NUM_THREADS: usize = 1; const NUM_THREADS: usize = 1;
const CHANNEL_BLACKLIST: [CliTvChannel; 1] = [CliTvChannel::Stdin];
impl SelectionChannel { impl SelectionChannel {
pub fn new() -> Self { pub fn new() -> Self {
let matcher = Nucleo::new( let matcher = Nucleo::new(
@ -36,9 +34,6 @@ impl SelectionChannel {
); );
let injector = matcher.injector(); let injector = matcher.injector();
for variant in CliTvChannel::value_variants() { for variant in CliTvChannel::value_variants() {
if CHANNEL_BLACKLIST.contains(variant) {
continue;
}
let _ = injector.push(*variant, |e, cols| { let _ = injector.push(*variant, |e, cols| {
cols[0] = (*e).to_string().into(); cols[0] = (*e).to_string().into();
}); });
@ -55,6 +50,12 @@ impl SelectionChannel {
const MATCHER_TICK_TIMEOUT: u64 = 2; const MATCHER_TICK_TIMEOUT: u64 = 2;
} }
impl Default for SelectionChannel {
fn default() -> Self {
Self::new()
}
}
const TV_ICON: FileIcon = FileIcon { const TV_ICON: FileIcon = FileIcon {
icon: '📺', icon: '📺',
color: "#ffffff", color: "#ffffff",

View File

@ -1,11 +1,12 @@
use std::io::{stdout, IsTerminal, Write}; use std::io::{stdout, IsTerminal, Write};
use channels::AvailableChannel;
use clap::Parser; use clap::Parser;
use color_eyre::Result; use color_eyre::Result;
use tracing::{debug, info}; use tracing::{debug, info};
use crate::app::App; use crate::app::App;
use crate::channels::CliTvChannel; use crate::channels::stdin::Channel as StdinChannel;
use crate::cli::Cli; use crate::cli::Cli;
mod action; mod action;
@ -36,10 +37,10 @@ async fn main() -> Result<()> {
{ {
if is_readable_stdin() { if is_readable_stdin() {
debug!("Using stdin channel"); debug!("Using stdin channel");
CliTvChannel::Stdin AvailableChannel::Stdin(StdinChannel::default())
} else { } else {
debug!("Using {:?} channel", args.channel); debug!("Using {:?} channel", args.channel);
args.channel args.channel.to_channel()
} }
}, },
args.tick_rate, args.tick_rate,

View File

@ -71,9 +71,8 @@ pub struct Television {
impl Television { impl Television {
#[must_use] #[must_use]
pub fn new(cli_channel: CliTvChannel) -> Self { pub fn new(mut channel: AvailableChannel) -> Self {
let mut tv_channel = cli_channel.to_channel(); channel.find(EMPTY_STRING);
tv_channel.find(EMPTY_STRING);
let spinner = Spinner::default(); let spinner = Spinner::default();
let spinner_state = SpinnerState::from(&spinner); let spinner_state = SpinnerState::from(&spinner);
@ -81,7 +80,7 @@ impl Television {
Self { Self {
action_tx: None, action_tx: None,
config: Config::default(), config: Config::default(),
channel: tv_channel, channel,
current_pattern: EMPTY_STRING.to_string(), current_pattern: EMPTY_STRING.to_string(),
mode: Mode::Channel, mode: Mode::Channel,
input: Input::new(EMPTY_STRING.to_string()), input: Input::new(EMPTY_STRING.to_string()),
@ -99,7 +98,7 @@ impl Television {
} }
} }
pub fn change_channel(&mut self, channel: Box<dyn TelevisionChannel>) { pub fn change_channel(&mut self, channel: AvailableChannel) {
self.reset_preview_scroll(); self.reset_preview_scroll();
self.reset_results_selection(); self.reset_results_selection();
self.current_pattern = EMPTY_STRING.to_string(); self.current_pattern = EMPTY_STRING.to_string();
@ -296,7 +295,8 @@ impl Television {
Action::ScrollPreviewHalfPageUp => self.scroll_preview_up(20), Action::ScrollPreviewHalfPageUp => self.scroll_preview_up(20),
Action::ToChannelSelection => { Action::ToChannelSelection => {
self.mode = Mode::ChannelSelection; self.mode = Mode::ChannelSelection;
let selection_channel = Box::new(SelectionChannel::new()); let selection_channel =
AvailableChannel::Channel(SelectionChannel::new());
self.change_channel(selection_channel); self.change_channel(selection_channel);
} }
Action::SelectEntry => { Action::SelectEntry => {
@ -308,10 +308,12 @@ impl Television {
.unwrap() .unwrap()
.send(Action::SelectAndExit)?, .send(Action::SelectAndExit)?,
Mode::ChannelSelection => { Mode::ChannelSelection => {
self.mode = Mode::Channel; if let Ok(new_channel) =
let new_channel = AvailableChannel::try_from(&entry)
AvailableChannel::from_entry(&entry)?; {
self.change_channel(new_channel); self.mode = Mode::Channel;
self.change_channel(new_channel);
}
} }
} }
} }

View File

@ -11,6 +11,8 @@ pub fn cli_channel_derive(input: TokenStream) -> TokenStream {
impl_cli_channel(&ast) impl_cli_channel(&ast)
} }
const VARIANT_BLACKLIST: [&str; 2] = ["Stdin", "Channel"];
fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream { fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream {
// check that the struct is an enum // check that the struct is an enum
let variants = if let syn::Data::Enum(data_enum) = &ast.data { let variants = if let syn::Data::Enum(data_enum) = &ast.data {
@ -26,12 +28,15 @@ fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream {
); );
// create the CliTvChannel enum // create the CliTvChannel enum
let cli_enum_variants = variants.iter().map(|variant| { let cli_enum_variants = variants
let variant_name = &variant.ident; .iter()
quote! { .filter(|v| !VARIANT_BLACKLIST.contains(&v.ident.to_string().as_str()))
#variant_name .map(|variant| {
} let variant_name = &variant.ident;
}); quote! {
#variant_name
}
});
let cli_enum = quote! { let cli_enum = quote! {
use clap::ValueEnum; use clap::ValueEnum;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -46,7 +51,9 @@ fn impl_cli_channel(ast: &syn::DeriveInput) -> TokenStream {
}; };
// Generate the match arms for the `to_channel` method // Generate the match arms for the `to_channel` method
let arms = variants.iter().map(|variant| { let arms = variants.iter().filter(
|variant| !VARIANT_BLACKLIST.contains(&variant.ident.to_string().as_str()),
).map(|variant| {
let variant_name = &variant.ident; let variant_name = &variant.ident;
// Get the inner type of the variant, assuming it is the first field of the variant // Get the inner type of the variant, assuming it is the first field of the variant
@ -94,28 +101,31 @@ pub fn tv_channel_derive(input: TokenStream) -> TokenStream {
} }
fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream { fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream {
// check that the struct is an enum // Ensure the struct is an enum
let variants = if let syn::Data::Enum(data_enum) = &ast.data { let variants = if let syn::Data::Enum(data_enum) = &ast.data {
&data_enum.variants &data_enum.variants
} else { } else {
panic!("#[derive(TvChannel)] is only defined for enums"); panic!("#[derive(TvChannel)] is only defined for enums");
}; };
// check that the enum has at least one variant // Ensure the enum has at least one variant
assert!( assert!(
!variants.is_empty(), !variants.is_empty(),
"#[derive(TvChannel)] requires at least one variant" "#[derive(TvChannel)] requires at least one variant"
); );
let enum_name = &ast.ident;
let variant_names: Vec<_> = variants.iter().map(|v| &v.ident).collect();
// Generate the trait implementation for the TelevisionChannel trait // Generate the trait implementation for the TelevisionChannel trait
// FIXME: fix this
let trait_impl = quote! { let trait_impl = quote! {
impl TelevisionChannel for AvailableChannel { impl TelevisionChannel for #enum_name {
fn find(&mut self, pattern: &str) { fn find(&mut self, pattern: &str) {
match self { match self {
#( #(
AvailableChannel::#variants(_) => { #enum_name::#variant_names(ref mut channel) => {
self.find(pattern); channel.find(pattern);
} }
)* )*
} }
@ -124,8 +134,8 @@ fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream {
fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry> { fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry> {
match self { match self {
#( #(
AvailableChannel::#variants(_) => { #enum_name::#variant_names(ref mut channel) => {
self.results(num_entries, offset) channel.results(num_entries, offset)
} }
)* )*
} }
@ -134,8 +144,8 @@ fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream {
fn get_result(&self, index: u32) -> Option<Entry> { fn get_result(&self, index: u32) -> Option<Entry> {
match self { match self {
#( #(
AvailableChannel::#variants(_) => { #enum_name::#variant_names(ref channel) => {
self.get_result(index) channel.get_result(index)
} }
)* )*
} }
@ -144,8 +154,8 @@ fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream {
fn result_count(&self) -> u32 { fn result_count(&self) -> u32 {
match self { match self {
#( #(
AvailableChannel::#variants(_) => { #enum_name::#variant_names(ref channel) => {
self.result_count() channel.result_count()
} }
)* )*
} }
@ -154,8 +164,8 @@ fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream {
fn total_count(&self) -> u32 { fn total_count(&self) -> u32 {
match self { match self {
#( #(
AvailableChannel::#variants(_) => { #enum_name::#variant_names(ref channel) => {
self.total_count() channel.total_count()
} }
)* )*
} }
@ -164,8 +174,8 @@ fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream {
fn running(&self) -> bool { fn running(&self) -> bool {
match self { match self {
#( #(
AvailableChannel::#variants(_) => { #enum_name::#variant_names(ref channel) => {
self.running() channel.running()
} }
)* )*
} }