mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-03 01:50:12 +00:00
refactor: drop TelevisionChannel enum and all associated macros (#498)
This drops the `TelevisionChannel` enum which served as a unified interface for builtin and cable channels as well as all related macros and the `television-derive` package. This simplifies the code quite a lot and will improve overall maintainability.
This commit is contained in:
parent
58d73dbeba
commit
2b2654b6aa
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -1424,7 +1424,6 @@ dependencies = [
|
|||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"serde",
|
"serde",
|
||||||
"signal-hook",
|
"signal-hook",
|
||||||
"television-derive",
|
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml",
|
"toml",
|
||||||
@ -1434,15 +1433,6 @@ dependencies = [
|
|||||||
"winapi-util",
|
"winapi-util",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "television-derive"
|
|
||||||
version = "0.0.27"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.19.1"
|
version = "3.19.1"
|
||||||
|
@ -31,8 +31,6 @@ build = "build.rs"
|
|||||||
path = "television/lib.rs"
|
path = "television/lib.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
television-derive = { path = "television-derive", version = "0.0.27" }
|
|
||||||
|
|
||||||
anyhow = "1.0"
|
anyhow = "1.0"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
directories = "6.0"
|
directories = "6.0"
|
||||||
|
@ -8,13 +8,12 @@ use ratatui::prelude::{Line, Style};
|
|||||||
use ratatui::style::Color;
|
use ratatui::style::Color;
|
||||||
use ratatui::widgets::{Block, BorderType, Borders, ListDirection, Padding};
|
use ratatui::widgets::{Block, BorderType, Borders, ListDirection, Padding};
|
||||||
use ratatui::Terminal;
|
use ratatui::Terminal;
|
||||||
use television::channels::cable::prototypes::CableChannelPrototypes;
|
use television::channels::cable::prototypes::Cable;
|
||||||
use television::{
|
use television::{
|
||||||
action::Action,
|
action::Action,
|
||||||
channels::{
|
channels::{
|
||||||
cable::prototypes::CableChannelPrototype,
|
cable::prototypes::ChannelPrototype,
|
||||||
entry::{into_ranges, Entry},
|
entry::{into_ranges, Entry},
|
||||||
OnAir,
|
|
||||||
},
|
},
|
||||||
config::{Config, ConfigEnv},
|
config::{Config, ConfigEnv},
|
||||||
screen::{colors::ResultsColorscheme, results::build_results_list},
|
screen::{colors::ResultsColorscheme, results::build_results_list},
|
||||||
@ -22,6 +21,7 @@ use television::{
|
|||||||
};
|
};
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
pub fn draw_results_list(c: &mut Criterion) {
|
pub fn draw_results_list(c: &mut Criterion) {
|
||||||
// FIXME: there's probably a way to have this as a benchmark asset
|
// FIXME: there's probably a way to have this as a benchmark asset
|
||||||
// possible as a JSON file and to load it for the benchmark using Serde
|
// possible as a JSON file and to load it for the benchmark using Serde
|
||||||
@ -458,6 +458,7 @@ pub fn draw_results_list(c: &mut Criterion) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::missing_panics_doc)]
|
||||||
pub fn draw(c: &mut Criterion) {
|
pub fn draw(c: &mut Criterion) {
|
||||||
let width = 250;
|
let width = 250;
|
||||||
let height = 80;
|
let height = 80;
|
||||||
@ -472,7 +473,7 @@ pub fn draw(c: &mut Criterion) {
|
|||||||
let backend = TestBackend::new(width, height);
|
let backend = TestBackend::new(width, height);
|
||||||
let terminal = Terminal::new(backend).unwrap();
|
let terminal = Terminal::new(backend).unwrap();
|
||||||
let (tx, _) = tokio::sync::mpsc::unbounded_channel();
|
let (tx, _) = tokio::sync::mpsc::unbounded_channel();
|
||||||
let channel = CableChannelPrototype::default();
|
let channel = ChannelPrototype::default();
|
||||||
// Wait for the channel to finish loading
|
// Wait for the channel to finish loading
|
||||||
let mut tv = Television::new(
|
let mut tv = Television::new(
|
||||||
tx,
|
tx,
|
||||||
@ -482,7 +483,7 @@ pub fn draw(c: &mut Criterion) {
|
|||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
false,
|
false,
|
||||||
CableChannelPrototypes::default(),
|
Cable::default(),
|
||||||
);
|
);
|
||||||
tv.find("television");
|
tv.find("television");
|
||||||
for _ in 0..5 {
|
for _ in 0..5 {
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "television-derive"
|
|
||||||
version = "0.0.27"
|
|
||||||
edition = "2021"
|
|
||||||
description = "The revolution will be televised."
|
|
||||||
license = "MIT"
|
|
||||||
authors = ["Alexandre Pasmantier <alex.pasmant@gmail.com>"]
|
|
||||||
repository = "https://github.com/alexpasmantier/television"
|
|
||||||
homepage = "https://github.com/alexpasmantier/television"
|
|
||||||
keywords = ["search", "fuzzy", "preview", "tui", "terminal"]
|
|
||||||
categories = [
|
|
||||||
"command-line-utilities",
|
|
||||||
"command-line-interface",
|
|
||||||
"concurrency",
|
|
||||||
"development-tools",
|
|
||||||
]
|
|
||||||
rust-version = "1.83"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
proc-macro2 = "1.0.93"
|
|
||||||
quote = "1.0.38"
|
|
||||||
syn = "2.0.96"
|
|
||||||
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
proc-macro = true
|
|
@ -1,166 +0,0 @@
|
|||||||
use proc_macro::TokenStream;
|
|
||||||
use quote::quote;
|
|
||||||
|
|
||||||
/// This macro generates the `OnAir` trait implementation for the given enum.
|
|
||||||
///
|
|
||||||
/// The `OnAir` trait is used to interact with the different television channels
|
|
||||||
/// and forwards the method calls to the corresponding channel variants.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```ignore
|
|
||||||
/// use television-derive::Broadcast;
|
|
||||||
/// use television::channels::{TelevisionChannel, OnAir};
|
|
||||||
/// use television::channels::{files, text};
|
|
||||||
///
|
|
||||||
/// #[derive(Broadcast)]
|
|
||||||
/// enum TelevisionChannel {
|
|
||||||
/// Files(files::Channel),
|
|
||||||
/// Text(text::Channel),
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// let mut channel = TelevisionChannel::Files(files::Channel::default());
|
|
||||||
///
|
|
||||||
/// // Use the `OnAir` trait methods directly on TelevisionChannel
|
|
||||||
/// channel.find("pattern");
|
|
||||||
/// let results = channel.results(10, 0);
|
|
||||||
/// let result = channel.get_result(0);
|
|
||||||
/// let result_count = channel.result_count();
|
|
||||||
/// let total_count = channel.total_count();
|
|
||||||
/// let running = channel.running();
|
|
||||||
/// channel.shutdown();
|
|
||||||
/// ```
|
|
||||||
#[proc_macro_derive(Broadcast)]
|
|
||||||
pub fn tv_channel_derive(input: TokenStream) -> TokenStream {
|
|
||||||
// Construct a representation of Rust code as a syntax tree
|
|
||||||
// that we can manipulate
|
|
||||||
let ast = syn::parse(input).unwrap();
|
|
||||||
|
|
||||||
// Build the trait implementation
|
|
||||||
impl_tv_channel(&ast)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn impl_tv_channel(ast: &syn::DeriveInput) -> TokenStream {
|
|
||||||
// Ensure the struct is an enum
|
|
||||||
let variants = if let syn::Data::Enum(data_enum) = &ast.data {
|
|
||||||
&data_enum.variants
|
|
||||||
} else {
|
|
||||||
panic!("#[derive(OnAir)] is only defined for enums");
|
|
||||||
};
|
|
||||||
|
|
||||||
// Ensure the enum has at least one variant
|
|
||||||
assert!(
|
|
||||||
!variants.is_empty(),
|
|
||||||
"#[derive(OnAir)] 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
|
|
||||||
let trait_impl = quote! {
|
|
||||||
impl OnAir for #enum_name {
|
|
||||||
fn find(&mut self, pattern: &str) {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref mut channel) => {
|
|
||||||
channel.find(pattern);
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry> {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref mut channel) => {
|
|
||||||
channel.results(num_entries, offset)
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_result(&self, index: u32) -> Option<Entry> {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref channel) => {
|
|
||||||
channel.get_result(index)
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn selected_entries(&self) -> &FxHashSet<Entry> {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref channel) => {
|
|
||||||
channel.selected_entries()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toggle_selection(&mut self, entry: &Entry) {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref mut channel) => {
|
|
||||||
channel.toggle_selection(entry)
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn result_count(&self) -> u32 {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref channel) => {
|
|
||||||
channel.result_count()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn total_count(&self) -> u32 {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref channel) => {
|
|
||||||
channel.total_count()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn running(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref channel) => {
|
|
||||||
channel.running()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shutdown(&self) {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref channel) => {
|
|
||||||
channel.shutdown()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn supports_preview(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
#(
|
|
||||||
#enum_name::#variant_names(ref channel) => {
|
|
||||||
channel.supports_preview()
|
|
||||||
}
|
|
||||||
)*
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
trait_impl.into()
|
|
||||||
}
|
|
@ -4,10 +4,8 @@ use anyhow::Result;
|
|||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tracing::{debug, trace};
|
use tracing::{debug, trace};
|
||||||
|
|
||||||
use crate::channels::cable::prototypes::{
|
use crate::channels::cable::prototypes::{Cable, ChannelPrototype};
|
||||||
CableChannelPrototype, CableChannelPrototypes,
|
use crate::channels::entry::Entry;
|
||||||
};
|
|
||||||
use crate::channels::{entry::Entry, OnAir};
|
|
||||||
use crate::config::{default_tick_rate, Config};
|
use crate::config::{default_tick_rate, Config};
|
||||||
use crate::keymap::Keymap;
|
use crate::keymap::Keymap;
|
||||||
use crate::render::UiState;
|
use crate::render::UiState;
|
||||||
@ -138,11 +136,11 @@ const ACTION_BUF_SIZE: usize = 8;
|
|||||||
|
|
||||||
impl App {
|
impl App {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
channel_prototype: CableChannelPrototype,
|
channel_prototype: ChannelPrototype,
|
||||||
config: Config,
|
config: Config,
|
||||||
input: Option<String>,
|
input: Option<String>,
|
||||||
options: AppOptions,
|
options: AppOptions,
|
||||||
cable_channels: &CableChannelPrototypes,
|
cable_channels: &Cable,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
let (action_tx, action_rx) = mpsc::unbounded_channel();
|
||||||
let (render_tx, render_rx) = mpsc::unbounded_channel();
|
let (render_tx, render_rx) = mpsc::unbounded_channel();
|
||||||
@ -160,7 +158,7 @@ impl App {
|
|||||||
options.no_remote,
|
options.no_remote,
|
||||||
options.no_help,
|
options.no_help,
|
||||||
options.exact,
|
options.exact,
|
||||||
CableChannelPrototypes((*cable_channels).clone()),
|
cable_channels.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
@ -6,17 +6,15 @@ use anyhow::Result;
|
|||||||
use tracing::{debug, error};
|
use tracing::{debug, error};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
channels::cable::prototypes::{
|
channels::cable::prototypes::{Cable, ChannelPrototype},
|
||||||
CableChannelPrototype, CableChannelPrototypes,
|
|
||||||
},
|
|
||||||
config::get_config_dir,
|
config::get_config_dir,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Just a proxy struct to deserialize prototypes
|
/// Just a proxy struct to deserialize prototypes
|
||||||
#[derive(Debug, serde::Deserialize, Default)]
|
#[derive(Debug, serde::Deserialize, Default)]
|
||||||
pub struct SerializedChannelPrototypes {
|
pub struct CableSpec {
|
||||||
#[serde(rename = "cable_channel")]
|
#[serde(rename = "cable_channel")]
|
||||||
pub prototypes: Vec<CableChannelPrototype>,
|
pub prototypes: Vec<ChannelPrototype>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CABLE_FILE_NAME_SUFFIX: &str = "channels";
|
const CABLE_FILE_NAME_SUFFIX: &str = "channels";
|
||||||
@ -42,7 +40,7 @@ const DEFAULT_CABLE_CHANNELS: &str =
|
|||||||
/// ├── my_channels.toml
|
/// ├── my_channels.toml
|
||||||
/// └── windows_channels.toml
|
/// └── windows_channels.toml
|
||||||
/// ```
|
/// ```
|
||||||
pub fn load_cable_channels() -> Result<CableChannelPrototypes> {
|
pub fn load_cable() -> Result<Cable> {
|
||||||
let config_dir = get_config_dir();
|
let config_dir = get_config_dir();
|
||||||
|
|
||||||
// list all files in the config directory
|
// list all files in the config directory
|
||||||
@ -60,13 +58,13 @@ pub fn load_cable_channels() -> Result<CableChannelPrototypes> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let default_prototypes =
|
let default_prototypes =
|
||||||
toml::from_str::<SerializedChannelPrototypes>(DEFAULT_CABLE_CHANNELS)
|
toml::from_str::<CableSpec>(DEFAULT_CABLE_CHANNELS)
|
||||||
.expect("Failed to parse default cable channels");
|
.expect("Failed to parse default cable channels");
|
||||||
|
|
||||||
let prototypes = file_paths.iter().fold(
|
let prototypes = file_paths.iter().fold(
|
||||||
Vec::<CableChannelPrototype>::new(),
|
Vec::<ChannelPrototype>::new(),
|
||||||
|mut acc, p| {
|
|mut acc, p| {
|
||||||
match toml::from_str::<SerializedChannelPrototypes>(
|
match toml::from_str::<CableSpec>(
|
||||||
&std::fs::read_to_string(p)
|
&std::fs::read_to_string(p)
|
||||||
.expect("Unable to read configuration file"),
|
.expect("Unable to read configuration file"),
|
||||||
) {
|
) {
|
||||||
@ -97,7 +95,7 @@ pub fn load_cable_channels() -> Result<CableChannelPrototypes> {
|
|||||||
{
|
{
|
||||||
cable_channels.insert(prototype.name.clone(), prototype);
|
cable_channels.insert(prototype.name.clone(), prototype);
|
||||||
}
|
}
|
||||||
Ok(CableChannelPrototypes(cable_channels))
|
Ok(Cable(cable_channels))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn is_cable_file_format<P>(p: P) -> bool
|
fn is_cable_file_format<P>(p: P) -> bool
|
||||||
|
@ -2,11 +2,11 @@ use std::collections::HashSet;
|
|||||||
use std::io::{BufRead, BufReader};
|
use std::io::{BufRead, BufReader};
|
||||||
use std::process::Stdio;
|
use std::process::Stdio;
|
||||||
|
|
||||||
use prototypes::{CableChannelPrototype, DEFAULT_DELIMITER};
|
use prototypes::{ChannelPrototype, DEFAULT_DELIMITER};
|
||||||
use rustc_hash::{FxBuildHasher, FxHashSet};
|
use rustc_hash::{FxBuildHasher, FxHashSet};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::channels::{entry::Entry, preview::PreviewCommand, OnAir};
|
use crate::channels::{entry::Entry, preview::PreviewCommand};
|
||||||
use crate::matcher::Matcher;
|
use crate::matcher::Matcher;
|
||||||
use crate::matcher::{config::Config, injector::Injector};
|
use crate::matcher::{config::Config, injector::Injector};
|
||||||
use crate::utils::command::shell_command;
|
use crate::utils::command::shell_command;
|
||||||
@ -34,8 +34,8 @@ impl Default for Channel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<CableChannelPrototype> for Channel {
|
impl From<ChannelPrototype> for Channel {
|
||||||
fn from(prototype: CableChannelPrototype) -> Self {
|
fn from(prototype: ChannelPrototype) -> Self {
|
||||||
Self::new(
|
Self::new(
|
||||||
&prototype.name,
|
&prototype.name,
|
||||||
&prototype.source_command,
|
&prototype.source_command,
|
||||||
@ -77,6 +77,59 @@ impl Channel {
|
|||||||
crawl_handle,
|
crawl_handle,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn find(&mut self, pattern: &str) {
|
||||||
|
self.matcher.find(pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry> {
|
||||||
|
self.matcher.tick();
|
||||||
|
self.matcher
|
||||||
|
.results(num_entries, offset)
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| {
|
||||||
|
let path = item.matched_string;
|
||||||
|
Entry::new(path).with_name_match_indices(&item.match_indices)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_result(&self, index: u32) -> Option<Entry> {
|
||||||
|
self.matcher.get_result(index).map(|item| {
|
||||||
|
let path = item.matched_string;
|
||||||
|
Entry::new(path)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn selected_entries(&self) -> &FxHashSet<Entry> {
|
||||||
|
&self.selected_entries
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn toggle_selection(&mut self, entry: &Entry) {
|
||||||
|
if self.selected_entries.contains(entry) {
|
||||||
|
self.selected_entries.remove(entry);
|
||||||
|
} else {
|
||||||
|
self.selected_entries.insert(entry.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn result_count(&self) -> u32 {
|
||||||
|
self.matcher.matched_item_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn total_count(&self) -> u32 {
|
||||||
|
self.matcher.total_item_count
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn running(&self) -> bool {
|
||||||
|
self.matcher.status.running || !self.crawl_handle.is_finished()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn shutdown(&self) {}
|
||||||
|
|
||||||
|
pub fn supports_preview(&self) -> bool {
|
||||||
|
self.preview_command.is_some()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::unused_async)]
|
#[allow(clippy::unused_async)]
|
||||||
@ -123,58 +176,3 @@ async fn load_candidates(
|
|||||||
}
|
}
|
||||||
let _ = child.wait();
|
let _ = child.wait();
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OnAir for Channel {
|
|
||||||
fn find(&mut self, pattern: &str) {
|
|
||||||
self.matcher.find(pattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry> {
|
|
||||||
self.matcher.tick();
|
|
||||||
self.matcher
|
|
||||||
.results(num_entries, offset)
|
|
||||||
.into_iter()
|
|
||||||
.map(|item| {
|
|
||||||
let path = item.matched_string;
|
|
||||||
Entry::new(path).with_name_match_indices(&item.match_indices)
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_result(&self, index: u32) -> Option<Entry> {
|
|
||||||
self.matcher.get_result(index).map(|item| {
|
|
||||||
let path = item.matched_string;
|
|
||||||
Entry::new(path)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn selected_entries(&self) -> &FxHashSet<Entry> {
|
|
||||||
&self.selected_entries
|
|
||||||
}
|
|
||||||
|
|
||||||
fn toggle_selection(&mut self, entry: &Entry) {
|
|
||||||
if self.selected_entries.contains(entry) {
|
|
||||||
self.selected_entries.remove(entry);
|
|
||||||
} else {
|
|
||||||
self.selected_entries.insert(entry.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn result_count(&self) -> u32 {
|
|
||||||
self.matcher.matched_item_count
|
|
||||||
}
|
|
||||||
|
|
||||||
fn total_count(&self) -> u32 {
|
|
||||||
self.matcher.total_item_count
|
|
||||||
}
|
|
||||||
|
|
||||||
fn running(&self) -> bool {
|
|
||||||
self.matcher.status.running || !self.crawl_handle.is_finished()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn shutdown(&self) {}
|
|
||||||
|
|
||||||
fn supports_preview(&self) -> bool {
|
|
||||||
self.preview_command.is_some()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,9 +4,7 @@ use std::{
|
|||||||
ops::Deref,
|
ops::Deref,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{cable::CableSpec, channels::preview::PreviewCommand};
|
||||||
cable::SerializedChannelPrototypes, channels::preview::PreviewCommand,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// A prototype for a cable channel.
|
/// A prototype for a cable channel.
|
||||||
///
|
///
|
||||||
@ -40,7 +38,7 @@ use crate::{
|
|||||||
/// preview_command = "cat {}"
|
/// preview_command = "cat {}"
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(Clone, Debug, serde::Deserialize, PartialEq)]
|
#[derive(Clone, Debug, serde::Deserialize, PartialEq)]
|
||||||
pub struct CableChannelPrototype {
|
pub struct ChannelPrototype {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub source_command: String,
|
pub source_command: String,
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@ -54,7 +52,7 @@ pub struct CableChannelPrototype {
|
|||||||
const STDIN_CHANNEL_NAME: &str = "stdin";
|
const STDIN_CHANNEL_NAME: &str = "stdin";
|
||||||
const STDIN_SOURCE_COMMAND: &str = "cat";
|
const STDIN_SOURCE_COMMAND: &str = "cat";
|
||||||
|
|
||||||
impl CableChannelPrototype {
|
impl ChannelPrototype {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
name: &str,
|
name: &str,
|
||||||
source_command: &str,
|
source_command: &str,
|
||||||
@ -102,9 +100,9 @@ impl CableChannelPrototype {
|
|||||||
const DEFAULT_PROTOTYPE_NAME: &str = "files";
|
const DEFAULT_PROTOTYPE_NAME: &str = "files";
|
||||||
pub const DEFAULT_DELIMITER: &str = " ";
|
pub const DEFAULT_DELIMITER: &str = " ";
|
||||||
|
|
||||||
impl Default for CableChannelPrototype {
|
impl Default for ChannelPrototype {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
CableChannelPrototypes::default()
|
Cable::default()
|
||||||
.get(DEFAULT_PROTOTYPE_NAME)
|
.get(DEFAULT_PROTOTYPE_NAME)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@ -118,7 +116,7 @@ fn default_delimiter() -> Option<String> {
|
|||||||
Some(DEFAULT_DELIMITER.to_string())
|
Some(DEFAULT_DELIMITER.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for CableChannelPrototype {
|
impl Display for ChannelPrototype {
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", self.name)
|
write!(f, "{}", self.name)
|
||||||
}
|
}
|
||||||
@ -129,13 +127,11 @@ impl Display for CableChannelPrototype {
|
|||||||
/// This is used to store cable channel prototypes throughout the application
|
/// This is used to store cable channel prototypes throughout the application
|
||||||
/// in a way that facilitates answering questions like "what's the prototype
|
/// in a way that facilitates answering questions like "what's the prototype
|
||||||
/// for `files`?" or "does this channel exist?".
|
/// for `files`?" or "does this channel exist?".
|
||||||
#[derive(Debug, serde::Deserialize)]
|
#[derive(Debug, serde::Deserialize, Clone)]
|
||||||
pub struct CableChannelPrototypes(
|
pub struct Cable(pub FxHashMap<String, ChannelPrototype>);
|
||||||
pub FxHashMap<String, CableChannelPrototype>,
|
|
||||||
);
|
|
||||||
|
|
||||||
impl Deref for CableChannelPrototypes {
|
impl Deref for Cable {
|
||||||
type Target = FxHashMap<String, CableChannelPrototype>;
|
type Target = FxHashMap<String, ChannelPrototype>;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
&self.0
|
&self.0
|
||||||
@ -153,18 +149,16 @@ const DEFAULT_CABLE_CHANNELS_FILE: &str =
|
|||||||
const DEFAULT_CABLE_CHANNELS_FILE: &str =
|
const DEFAULT_CABLE_CHANNELS_FILE: &str =
|
||||||
include_str!("../../../cable/windows-channels.toml");
|
include_str!("../../../cable/windows-channels.toml");
|
||||||
|
|
||||||
impl Default for CableChannelPrototypes {
|
impl Default for Cable {
|
||||||
/// Fallback to the default cable channels specification (the template file
|
/// Fallback to the default cable channels specification (the template file
|
||||||
/// included in the repo).
|
/// included in the repo).
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let s = toml::from_str::<SerializedChannelPrototypes>(
|
let s = toml::from_str::<CableSpec>(DEFAULT_CABLE_CHANNELS_FILE)
|
||||||
DEFAULT_CABLE_CHANNELS_FILE,
|
.expect("Unable to parse default cable channels");
|
||||||
)
|
|
||||||
.expect("Unable to parse default cable channels");
|
|
||||||
let mut prototypes = FxHashMap::default();
|
let mut prototypes = FxHashMap::default();
|
||||||
for prototype in s.prototypes {
|
for prototype in s.prototypes {
|
||||||
prototypes.insert(prototype.name.clone(), prototype);
|
prototypes.insert(prototype.name.clone(), prototype);
|
||||||
}
|
}
|
||||||
CableChannelPrototypes(prototypes)
|
Cable(prototypes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,144 +1,4 @@
|
|||||||
use crate::channels::entry::Entry;
|
|
||||||
use anyhow::Result;
|
|
||||||
use cable::prototypes::CableChannelPrototype;
|
|
||||||
use rustc_hash::FxHashSet;
|
|
||||||
use television_derive::Broadcast;
|
|
||||||
|
|
||||||
pub mod cable;
|
pub mod cable;
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
pub mod preview;
|
pub mod preview;
|
||||||
pub mod remote_control;
|
pub mod remote_control;
|
||||||
|
|
||||||
/// The interface that all television channels must implement.
|
|
||||||
///
|
|
||||||
/// # Note
|
|
||||||
/// The `OnAir` trait requires the `Send` trait to be implemented as well.
|
|
||||||
/// This is necessary to allow the channels to be used with the tokio
|
|
||||||
/// runtime, which requires `Send` in order to be able to send tasks between
|
|
||||||
/// worker threads safely.
|
|
||||||
///
|
|
||||||
/// # Methods
|
|
||||||
/// - `find`: Find entries that match the given pattern. This method does not
|
|
||||||
/// return anything and instead typically stores the results internally for
|
|
||||||
/// later retrieval allowing to perform the search in the background while
|
|
||||||
/// incrementally polling the results.
|
|
||||||
/// ```ignore
|
|
||||||
/// fn find(&mut self, pattern: &str);
|
|
||||||
/// ```
|
|
||||||
/// - `results`: Get the results of the search (at a given point in time, see
|
|
||||||
/// above). This method returns a specific portion of entries that match the
|
|
||||||
/// search pattern. The `num_entries` parameter specifies the number of
|
|
||||||
/// entries to return and the `offset` parameter specifies the starting index
|
|
||||||
/// of the entries to return.
|
|
||||||
/// ```ignore
|
|
||||||
/// fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry>;
|
|
||||||
/// ```
|
|
||||||
/// - `get_result`: Get a specific result by its index.
|
|
||||||
/// ```ignore
|
|
||||||
/// fn get_result(&self, index: u32) -> Option<Entry>;
|
|
||||||
/// ```
|
|
||||||
/// - `result_count`: Get the number of results currently available.
|
|
||||||
/// ```ignore
|
|
||||||
/// fn result_count(&self) -> u32;
|
|
||||||
/// ```
|
|
||||||
/// - `total_count`: Get the total number of entries currently available (e.g.
|
|
||||||
/// the haystack).
|
|
||||||
/// ```ignore
|
|
||||||
/// fn total_count(&self) -> u32;
|
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
pub trait OnAir: Send {
|
|
||||||
/// Find entries that match the given pattern.
|
|
||||||
///
|
|
||||||
/// This method does not return anything and instead typically stores the
|
|
||||||
/// results internally for later retrieval allowing to perform the search
|
|
||||||
/// in the background while incrementally polling the results with
|
|
||||||
/// `results`.
|
|
||||||
fn find(&mut self, pattern: &str);
|
|
||||||
|
|
||||||
/// Get the results of the search (that are currently available).
|
|
||||||
fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry>;
|
|
||||||
|
|
||||||
/// Get a specific result by its index.
|
|
||||||
fn get_result(&self, index: u32) -> Option<Entry>;
|
|
||||||
|
|
||||||
/// Get the currently selected entries.
|
|
||||||
fn selected_entries(&self) -> &FxHashSet<Entry>;
|
|
||||||
|
|
||||||
/// Toggles selection for the entry under the cursor.
|
|
||||||
fn toggle_selection(&mut self, entry: &Entry);
|
|
||||||
|
|
||||||
/// Get the number of results currently available.
|
|
||||||
fn result_count(&self) -> u32;
|
|
||||||
|
|
||||||
/// Get the total number of entries currently available.
|
|
||||||
fn total_count(&self) -> u32;
|
|
||||||
|
|
||||||
/// Check if the channel is currently running.
|
|
||||||
fn running(&self) -> bool;
|
|
||||||
|
|
||||||
/// Turn off
|
|
||||||
fn shutdown(&self);
|
|
||||||
|
|
||||||
/// Whether this channel supports previewing entries.
|
|
||||||
fn supports_preview(&self) -> bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The available television channels.
|
|
||||||
///
|
|
||||||
/// Each channel is represented by a variant of the enum and should implement
|
|
||||||
/// the `OnAir` trait.
|
|
||||||
///
|
|
||||||
/// # Important
|
|
||||||
/// When adding a new channel, make sure to add a new variant to this enum and
|
|
||||||
/// implement the `OnAir` trait for it.
|
|
||||||
///
|
|
||||||
/// # Derive
|
|
||||||
/// ## `CliChannel`
|
|
||||||
/// The `CliChannel` derive macro generates the necessary glue code to
|
|
||||||
/// automatically create the corresponding `CliTvChannel` enum with unit
|
|
||||||
/// variants that can be used to select the channel from the command line.
|
|
||||||
/// It also generates the necessary glue code to automatically create a channel
|
|
||||||
/// instance from the selected CLI enum variant.
|
|
||||||
///
|
|
||||||
/// ## `Broadcast`
|
|
||||||
/// The `Broadcast` derive macro generates the necessary glue code to
|
|
||||||
/// automatically forward method calls to the corresponding channel variant.
|
|
||||||
/// This allows to use the `OnAir` trait methods directly on the `TelevisionChannel`
|
|
||||||
/// enum. In a more straightforward way, it implements the `OnAir` trait for the
|
|
||||||
/// `TelevisionChannel` enum.
|
|
||||||
///
|
|
||||||
/// ## `UnitChannel`
|
|
||||||
/// This macro generates an enum with unit variants that can be used instead
|
|
||||||
/// of carrying the actual channel instances around. It also generates the necessary
|
|
||||||
/// glue code to automatically create a channel instance from the selected enum variant.
|
|
||||||
#[allow(dead_code, clippy::module_name_repetitions)]
|
|
||||||
#[derive(Broadcast)]
|
|
||||||
pub enum TelevisionChannel {
|
|
||||||
/// The remote control channel.
|
|
||||||
///
|
|
||||||
/// This channel allows to switch between different channels.
|
|
||||||
RemoteControl(remote_control::RemoteControl),
|
|
||||||
/// A custom channel.
|
|
||||||
///
|
|
||||||
/// This channel allows to search through custom data.
|
|
||||||
Cable(cable::Channel),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TelevisionChannel {
|
|
||||||
pub fn zap(&self, channel_name: &str) -> Result<CableChannelPrototype> {
|
|
||||||
match self {
|
|
||||||
TelevisionChannel::RemoteControl(remote_control) => {
|
|
||||||
remote_control.zap(channel_name)
|
|
||||||
}
|
|
||||||
TelevisionChannel::Cable(_) => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn name(&self) -> String {
|
|
||||||
match self {
|
|
||||||
TelevisionChannel::Cable(channel) => channel.name.clone(),
|
|
||||||
TelevisionChannel::RemoteControl(_) => String::from("Remote"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use super::{cable::prototypes::DEFAULT_DELIMITER, entry::Entry};
|
use super::{cable::prototypes::DEFAULT_DELIMITER, entry::Entry};
|
||||||
use crate::channels::cable::prototypes::CableChannelPrototype;
|
use crate::channels::cable::prototypes::ChannelPrototype;
|
||||||
use lazy_regex::{regex, Lazy, Regex};
|
use lazy_regex::{regex, Lazy, Regex};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
@ -65,8 +65,8 @@ impl PreviewCommand {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&CableChannelPrototype> for Option<PreviewCommand> {
|
impl From<&ChannelPrototype> for Option<PreviewCommand> {
|
||||||
fn from(value: &CableChannelPrototype) -> Self {
|
fn from(value: &ChannelPrototype) -> Self {
|
||||||
if let Some(command) = value.preview_command.as_ref() {
|
if let Some(command) = value.preview_command.as_ref() {
|
||||||
let delimiter = value
|
let delimiter = value
|
||||||
.preview_delimiter
|
.preview_delimiter
|
||||||
|
@ -1,32 +1,23 @@
|
|||||||
use std::collections::HashSet;
|
use crate::channels::cable::prototypes::Cable;
|
||||||
|
|
||||||
use crate::channels::cable::prototypes::CableChannelPrototypes;
|
|
||||||
use crate::channels::entry::Entry;
|
use crate::channels::entry::Entry;
|
||||||
use crate::channels::OnAir;
|
|
||||||
use crate::matcher::{config::Config, Matcher};
|
use crate::matcher::{config::Config, Matcher};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use devicons::FileIcon;
|
use devicons::FileIcon;
|
||||||
use rustc_hash::{FxBuildHasher, FxHashSet};
|
|
||||||
|
|
||||||
use super::cable::prototypes::CableChannelPrototype;
|
use super::cable::prototypes::ChannelPrototype;
|
||||||
|
|
||||||
pub struct RemoteControl {
|
pub struct RemoteControl {
|
||||||
matcher: Matcher<String>,
|
matcher: Matcher<String>,
|
||||||
cable_channels: Option<CableChannelPrototypes>,
|
cable_channels: Option<Cable>,
|
||||||
selected_entries: FxHashSet<Entry>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const NUM_THREADS: usize = 1;
|
const NUM_THREADS: usize = 1;
|
||||||
|
|
||||||
impl RemoteControl {
|
impl RemoteControl {
|
||||||
pub fn new(cable_channels: Option<CableChannelPrototypes>) -> Self {
|
pub fn new(cable_channels: Option<Cable>) -> Self {
|
||||||
let matcher = Matcher::new(Config::default().n_threads(NUM_THREADS));
|
let matcher = Matcher::new(Config::default().n_threads(NUM_THREADS));
|
||||||
let injector = matcher.injector();
|
let injector = matcher.injector();
|
||||||
for c in cable_channels
|
for c in cable_channels.as_ref().unwrap_or(&Cable::default()).keys() {
|
||||||
.as_ref()
|
|
||||||
.unwrap_or(&CableChannelPrototypes::default())
|
|
||||||
.keys()
|
|
||||||
{
|
|
||||||
let () = injector.push(c.clone(), |e, cols| {
|
let () = injector.push(c.clone(), |e, cols| {
|
||||||
cols[0] = e.to_string().into();
|
cols[0] = e.to_string().into();
|
||||||
});
|
});
|
||||||
@ -34,11 +25,10 @@ impl RemoteControl {
|
|||||||
RemoteControl {
|
RemoteControl {
|
||||||
matcher,
|
matcher,
|
||||||
cable_channels,
|
cable_channels,
|
||||||
selected_entries: HashSet::with_hasher(FxBuildHasher),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn zap(&self, channel_name: &str) -> Result<CableChannelPrototype> {
|
pub fn zap(&self, channel_name: &str) -> Result<ChannelPrototype> {
|
||||||
match self
|
match self
|
||||||
.cable_channels
|
.cable_channels
|
||||||
.as_ref()
|
.as_ref()
|
||||||
@ -69,12 +59,12 @@ const CABLE_ICON: FileIcon = FileIcon {
|
|||||||
color: "#000000",
|
color: "#000000",
|
||||||
};
|
};
|
||||||
|
|
||||||
impl OnAir for RemoteControl {
|
impl RemoteControl {
|
||||||
fn find(&mut self, pattern: &str) {
|
pub fn find(&mut self, pattern: &str) {
|
||||||
self.matcher.find(pattern);
|
self.matcher.find(pattern);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry> {
|
pub fn results(&mut self, num_entries: u32, offset: u32) -> Vec<Entry> {
|
||||||
self.matcher.tick();
|
self.matcher.tick();
|
||||||
self.matcher
|
self.matcher
|
||||||
.results(num_entries, offset)
|
.results(num_entries, offset)
|
||||||
@ -88,35 +78,28 @@ impl OnAir for RemoteControl {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_result(&self, index: u32) -> Option<Entry> {
|
pub fn get_result(&self, index: u32) -> Option<Entry> {
|
||||||
self.matcher.get_result(index).map(|item| {
|
self.matcher.get_result(index).map(|item| {
|
||||||
let path = item.matched_string;
|
let path = item.matched_string;
|
||||||
Entry::new(path).with_icon(TV_ICON)
|
Entry::new(path).with_icon(TV_ICON)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn selected_entries(&self) -> &FxHashSet<Entry> {
|
pub fn result_count(&self) -> u32 {
|
||||||
&self.selected_entries
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
fn toggle_selection(&mut self, entry: &Entry) {}
|
|
||||||
|
|
||||||
fn result_count(&self) -> u32 {
|
|
||||||
self.matcher.matched_item_count
|
self.matcher.matched_item_count
|
||||||
}
|
}
|
||||||
|
|
||||||
fn total_count(&self) -> u32 {
|
pub fn total_count(&self) -> u32 {
|
||||||
self.matcher.total_item_count
|
self.matcher.total_item_count
|
||||||
}
|
}
|
||||||
|
|
||||||
fn running(&self) -> bool {
|
pub fn running(&self) -> bool {
|
||||||
self.matcher.status.running
|
self.matcher.status.running
|
||||||
}
|
}
|
||||||
|
|
||||||
fn shutdown(&self) {}
|
pub fn shutdown(&self) {}
|
||||||
|
|
||||||
fn supports_preview(&self) -> bool {
|
pub fn supports_preview(&self) -> bool {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,9 +4,7 @@ use std::path::Path;
|
|||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::channels::cable::prototypes::{
|
use crate::channels::cable::prototypes::{Cable, ChannelPrototype};
|
||||||
CableChannelPrototype, CableChannelPrototypes,
|
|
||||||
};
|
|
||||||
use crate::channels::preview::PreviewCommand;
|
use crate::channels::preview::PreviewCommand;
|
||||||
use crate::cli::args::{Cli, Command};
|
use crate::cli::args::{Cli, Command};
|
||||||
use crate::config::{KeyBindings, DEFAULT_CHANNEL};
|
use crate::config::{KeyBindings, DEFAULT_CHANNEL};
|
||||||
@ -20,7 +18,7 @@ pub mod args;
|
|||||||
#[allow(clippy::struct_excessive_bools)]
|
#[allow(clippy::struct_excessive_bools)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PostProcessedCli {
|
pub struct PostProcessedCli {
|
||||||
pub channel: CableChannelPrototype,
|
pub channel: ChannelPrototype,
|
||||||
pub preview_command: Option<PreviewCommand>,
|
pub preview_command: Option<PreviewCommand>,
|
||||||
pub no_preview: bool,
|
pub no_preview: bool,
|
||||||
pub tick_rate: Option<f64>,
|
pub tick_rate: Option<f64>,
|
||||||
@ -41,7 +39,7 @@ pub struct PostProcessedCli {
|
|||||||
impl Default for PostProcessedCli {
|
impl Default for PostProcessedCli {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
channel: CableChannelPrototype::default(),
|
channel: ChannelPrototype::default(),
|
||||||
preview_command: None,
|
preview_command: None,
|
||||||
no_preview: false,
|
no_preview: false,
|
||||||
tick_rate: None,
|
tick_rate: None,
|
||||||
@ -80,10 +78,10 @@ impl From<Cli> for PostProcessedCli {
|
|||||||
offset_expr: None,
|
offset_expr: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut channel: CableChannelPrototype;
|
let mut channel: ChannelPrototype;
|
||||||
let working_directory: Option<String>;
|
let working_directory: Option<String>;
|
||||||
|
|
||||||
let cable_channels = cable::load_cable_channels().unwrap_or_default();
|
let cable_channels = cable::load_cable().unwrap_or_default();
|
||||||
if cli.channel.is_none() {
|
if cli.channel.is_none() {
|
||||||
channel = cable_channels
|
channel = cable_channels
|
||||||
.get(DEFAULT_CHANNEL)
|
.get(DEFAULT_CHANNEL)
|
||||||
@ -178,8 +176,8 @@ fn parse_keybindings_literal(
|
|||||||
|
|
||||||
pub fn parse_channel(
|
pub fn parse_channel(
|
||||||
channel: &str,
|
channel: &str,
|
||||||
cable_channels: &CableChannelPrototypes,
|
cable_channels: &Cable,
|
||||||
) -> Result<CableChannelPrototype> {
|
) -> Result<ChannelPrototype> {
|
||||||
// try to parse the channel as a cable channel
|
// try to parse the channel as a cable channel
|
||||||
match cable_channels
|
match cable_channels
|
||||||
.iter()
|
.iter()
|
||||||
@ -191,7 +189,7 @@ pub fn parse_channel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_channels() {
|
pub fn list_channels() {
|
||||||
for c in cable::load_cable_channels().unwrap_or_default().keys() {
|
for c in cable::load_cable().unwrap_or_default().keys() {
|
||||||
println!("\t{c}");
|
println!("\t{c}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -223,8 +221,8 @@ pub fn guess_channel_from_prompt(
|
|||||||
prompt: &str,
|
prompt: &str,
|
||||||
command_mapping: &FxHashMap<String, String>,
|
command_mapping: &FxHashMap<String, String>,
|
||||||
fallback_channel: &str,
|
fallback_channel: &str,
|
||||||
cable_channels: &CableChannelPrototypes,
|
cable_channels: &Cable,
|
||||||
) -> Result<CableChannelPrototype> {
|
) -> Result<ChannelPrototype> {
|
||||||
debug!("Guessing channel from prompt: {}", prompt);
|
debug!("Guessing channel from prompt: {}", prompt);
|
||||||
// git checkout -qf
|
// git checkout -qf
|
||||||
// --- -------- --- <---------
|
// --- -------- --- <---------
|
||||||
@ -317,7 +315,7 @@ mod tests {
|
|||||||
|
|
||||||
let post_processed_cli: PostProcessedCli = cli.into();
|
let post_processed_cli: PostProcessedCli = cli.into();
|
||||||
|
|
||||||
let expected = CableChannelPrototype {
|
let expected = ChannelPrototype {
|
||||||
preview_delimiter: Some(":".to_string()),
|
preview_delimiter: Some(":".to_string()),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
@ -350,10 +348,7 @@ mod tests {
|
|||||||
|
|
||||||
let post_processed_cli: PostProcessedCli = cli.into();
|
let post_processed_cli: PostProcessedCli = cli.into();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(post_processed_cli.channel, ChannelPrototype::default(),);
|
||||||
post_processed_cli.channel,
|
|
||||||
CableChannelPrototype::default(),
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
post_processed_cli.working_directory,
|
post_processed_cli.working_directory,
|
||||||
Some(".".to_string())
|
Some(".".to_string())
|
||||||
@ -388,7 +383,7 @@ mod tests {
|
|||||||
|
|
||||||
/// Returns a tuple containing a command mapping and a fallback channel.
|
/// Returns a tuple containing a command mapping and a fallback channel.
|
||||||
fn guess_channel_from_prompt_setup<'a>(
|
fn guess_channel_from_prompt_setup<'a>(
|
||||||
) -> (FxHashMap<String, String>, &'a str, CableChannelPrototypes) {
|
) -> (FxHashMap<String, String>, &'a str, Cable) {
|
||||||
let mut command_mapping = FxHashMap::default();
|
let mut command_mapping = FxHashMap::default();
|
||||||
command_mapping.insert("vim".to_string(), "files".to_string());
|
command_mapping.insert("vim".to_string(), "files".to_string());
|
||||||
command_mapping.insert("export".to_string(), "env".to_string());
|
command_mapping.insert("export".to_string(), "env".to_string());
|
||||||
@ -396,7 +391,7 @@ mod tests {
|
|||||||
(
|
(
|
||||||
command_mapping,
|
command_mapping,
|
||||||
"env",
|
"env",
|
||||||
cable::load_cable_channels().unwrap_or_default(),
|
cable::load_cable().unwrap_or_default(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,9 +6,7 @@ use std::process::exit;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use television::cable;
|
use television::cable;
|
||||||
use television::channels::cable::prototypes::{
|
use television::channels::cable::prototypes::{Cable, ChannelPrototype};
|
||||||
CableChannelPrototype, CableChannelPrototypes,
|
|
||||||
};
|
|
||||||
use television::utils::clipboard::CLIPBOARD;
|
use television::utils::clipboard::CLIPBOARD;
|
||||||
use tracing::{debug, error, info};
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
@ -43,7 +41,7 @@ async fn main() -> Result<()> {
|
|||||||
let mut config = Config::new(&ConfigEnv::init()?)?;
|
let mut config = Config::new(&ConfigEnv::init()?)?;
|
||||||
|
|
||||||
debug!("Loading cable channels...");
|
debug!("Loading cable channels...");
|
||||||
let cable_channels = cable::load_cable_channels().unwrap_or_default();
|
let cable_channels = cable::load_cable().unwrap_or_default();
|
||||||
|
|
||||||
// optionally handle subcommands
|
// optionally handle subcommands
|
||||||
debug!("Handling subcommands...");
|
debug!("Handling subcommands...");
|
||||||
@ -154,11 +152,11 @@ pub fn determine_channel(
|
|||||||
args: PostProcessedCli,
|
args: PostProcessedCli,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
readable_stdin: bool,
|
readable_stdin: bool,
|
||||||
cable_channels: &CableChannelPrototypes,
|
cable_channels: &Cable,
|
||||||
) -> Result<CableChannelPrototype> {
|
) -> Result<ChannelPrototype> {
|
||||||
if readable_stdin {
|
if readable_stdin {
|
||||||
debug!("Using stdin channel");
|
debug!("Using stdin channel");
|
||||||
Ok(CableChannelPrototype::stdin(args.preview_command))
|
Ok(ChannelPrototype::stdin(args.preview_command))
|
||||||
} else if let Some(prompt) = args.autocomplete_prompt {
|
} else if let Some(prompt) = args.autocomplete_prompt {
|
||||||
debug!("Using autocomplete prompt: {:?}", prompt);
|
debug!("Using autocomplete prompt: {:?}", prompt);
|
||||||
let channel_prototype = guess_channel_from_prompt(
|
let channel_prototype = guess_channel_from_prompt(
|
||||||
@ -179,8 +177,7 @@ pub fn determine_channel(
|
|||||||
mod tests {
|
mod tests {
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use television::{
|
use television::{
|
||||||
cable::load_cable_channels,
|
cable::load_cable, channels::cable::prototypes::ChannelPrototype,
|
||||||
channels::cable::prototypes::CableChannelPrototype,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -189,11 +186,11 @@ mod tests {
|
|||||||
args: &PostProcessedCli,
|
args: &PostProcessedCli,
|
||||||
config: &Config,
|
config: &Config,
|
||||||
readable_stdin: bool,
|
readable_stdin: bool,
|
||||||
expected_channel: &CableChannelPrototype,
|
expected_channel: &ChannelPrototype,
|
||||||
cable_channels: Option<CableChannelPrototypes>,
|
cable_channels: Option<Cable>,
|
||||||
) {
|
) {
|
||||||
let channels: CableChannelPrototypes = cable_channels
|
let channels: Cable =
|
||||||
.unwrap_or_else(|| load_cable_channels().unwrap_or_default());
|
cable_channels.unwrap_or_else(|| load_cable().unwrap_or_default());
|
||||||
let channel =
|
let channel =
|
||||||
determine_channel(args.clone(), config, readable_stdin, &channels)
|
determine_channel(args.clone(), config, readable_stdin, &channels)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -208,7 +205,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
/// Test that the channel is stdin when stdin is readable
|
/// Test that the channel is stdin when stdin is readable
|
||||||
async fn test_determine_channel_readable_stdin() {
|
async fn test_determine_channel_readable_stdin() {
|
||||||
let channel = CableChannelPrototype::default();
|
let channel = ChannelPrototype::default();
|
||||||
let args = PostProcessedCli {
|
let args = PostProcessedCli {
|
||||||
channel,
|
channel,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -218,9 +215,7 @@ mod tests {
|
|||||||
&args,
|
&args,
|
||||||
&config,
|
&config,
|
||||||
true,
|
true,
|
||||||
&CableChannelPrototype::new(
|
&ChannelPrototype::new("stdin", "cat", false, None, None, None),
|
||||||
"stdin", "cat", false, None, None, None,
|
|
||||||
),
|
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -228,9 +223,8 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_determine_channel_autocomplete_prompt() {
|
async fn test_determine_channel_autocomplete_prompt() {
|
||||||
let autocomplete_prompt = Some("cd".to_string());
|
let autocomplete_prompt = Some("cd".to_string());
|
||||||
let expected_channel = CableChannelPrototype::new(
|
let expected_channel =
|
||||||
"dirs", "ls {}", false, None, None, None,
|
ChannelPrototype::new("dirs", "ls {}", false, None, None, None);
|
||||||
);
|
|
||||||
let args = PostProcessedCli {
|
let args = PostProcessedCli {
|
||||||
autocomplete_prompt,
|
autocomplete_prompt,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -263,7 +257,7 @@ mod tests {
|
|||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_determine_channel_standard_case() {
|
async fn test_determine_channel_standard_case() {
|
||||||
let channel =
|
let channel =
|
||||||
CableChannelPrototype::new("dirs", "", false, None, None, None);
|
ChannelPrototype::new("dirs", "", false, None, None, None);
|
||||||
let args = PostProcessedCli {
|
let args = PostProcessedCli {
|
||||||
channel,
|
channel,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
@ -273,7 +267,7 @@ mod tests {
|
|||||||
&args,
|
&args,
|
||||||
&config,
|
&config,
|
||||||
false,
|
false,
|
||||||
&CableChannelPrototype::new("dirs", "", false, None, None, None),
|
&ChannelPrototype::new("dirs", "", false, None, None, None),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::channels::cable::prototypes::CableChannelPrototype;
|
use crate::channels::cable::prototypes::ChannelPrototype;
|
||||||
use crate::preview::{Preview, PreviewContent};
|
use crate::preview::{Preview, PreviewContent};
|
||||||
use crate::utils::command::shell_command;
|
use crate::utils::command::shell_command;
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -130,8 +130,8 @@ pub fn try_preview(
|
|||||||
in_flight_previews.lock().remove(&entry.name);
|
in_flight_previews.lock().remove(&entry.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&CableChannelPrototype> for Option<Previewer> {
|
impl From<&ChannelPrototype> for Option<Previewer> {
|
||||||
fn from(value: &CableChannelPrototype) -> Self {
|
fn from(value: &ChannelPrototype) -> Self {
|
||||||
Option::<PreviewCommand>::from(value).map(Previewer::new)
|
Option::<PreviewCommand>::from(value).map(Previewer::new)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,12 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
action::Action,
|
action::Action,
|
||||||
cable::load_cable_channels,
|
|
||||||
channels::{
|
channels::{
|
||||||
cable::{
|
cable::{
|
||||||
prototypes::{CableChannelPrototype, CableChannelPrototypes},
|
prototypes::{Cable, ChannelPrototype},
|
||||||
Channel as CableChannel,
|
Channel as CableChannel,
|
||||||
},
|
},
|
||||||
entry::Entry,
|
entry::Entry,
|
||||||
remote_control::RemoteControl,
|
remote_control::RemoteControl,
|
||||||
OnAir, TelevisionChannel,
|
|
||||||
},
|
},
|
||||||
config::{Config, Theme},
|
config::{Config, Theme},
|
||||||
draw::{ChannelState, Ctx, TvState},
|
draw::{ChannelState, Ctx, TvState},
|
||||||
@ -48,7 +46,7 @@ pub struct Television {
|
|||||||
action_tx: UnboundedSender<Action>,
|
action_tx: UnboundedSender<Action>,
|
||||||
pub config: Config,
|
pub config: Config,
|
||||||
pub channel: CableChannel,
|
pub channel: CableChannel,
|
||||||
pub remote_control: Option<TelevisionChannel>,
|
pub remote_control: Option<RemoteControl>,
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub current_pattern: String,
|
pub current_pattern: String,
|
||||||
pub matching_mode: MatchingMode,
|
pub matching_mode: MatchingMode,
|
||||||
@ -70,13 +68,13 @@ impl Television {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
action_tx: UnboundedSender<Action>,
|
action_tx: UnboundedSender<Action>,
|
||||||
channel_prototype: CableChannelPrototype,
|
channel_prototype: ChannelPrototype,
|
||||||
mut config: Config,
|
mut config: Config,
|
||||||
input: Option<String>,
|
input: Option<String>,
|
||||||
no_remote: bool,
|
no_remote: bool,
|
||||||
no_help: bool,
|
no_help: bool,
|
||||||
exact: bool,
|
exact: bool,
|
||||||
cable_channels: CableChannelPrototypes,
|
cable_channels: Cable,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let mut results_picker = Picker::new(input.clone());
|
let mut results_picker = Picker::new(input.clone());
|
||||||
if config.ui.input_bar_position == InputPosition::Bottom {
|
if config.ui.input_bar_position == InputPosition::Bottom {
|
||||||
@ -108,9 +106,7 @@ impl Television {
|
|||||||
let remote_control = if no_remote {
|
let remote_control = if no_remote {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
Some(TelevisionChannel::RemoteControl(RemoteControl::new(Some(
|
Some(RemoteControl::new(Some(cable_channels)))
|
||||||
cable_channels,
|
|
||||||
))))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if no_help {
|
if no_help {
|
||||||
@ -150,13 +146,6 @@ impl Television {
|
|||||||
self.ui_state = ui_state;
|
self.ui_state = ui_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_remote_control(&mut self) {
|
|
||||||
let cable_channels = load_cable_channels().unwrap_or_default();
|
|
||||||
self.remote_control = Some(TelevisionChannel::RemoteControl(
|
|
||||||
RemoteControl::new(Some(cable_channels)),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn dump_context(&self) -> Ctx {
|
pub fn dump_context(&self) -> Ctx {
|
||||||
let channel_state = ChannelState::new(
|
let channel_state = ChannelState::new(
|
||||||
self.channel.name.clone(),
|
self.channel.name.clone(),
|
||||||
@ -189,10 +178,7 @@ impl Television {
|
|||||||
self.channel.name.clone()
|
self.channel.name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn change_channel(
|
pub fn change_channel(&mut self, channel_prototype: ChannelPrototype) {
|
||||||
&mut self,
|
|
||||||
channel_prototype: CableChannelPrototype,
|
|
||||||
) {
|
|
||||||
self.preview_state.reset();
|
self.preview_state.reset();
|
||||||
self.preview_state.enabled =
|
self.preview_state.enabled =
|
||||||
channel_prototype.preview_command.is_some();
|
channel_prototype.preview_command.is_some();
|
||||||
@ -213,7 +199,9 @@ impl Television {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
Mode::RemoteControl => {
|
Mode::RemoteControl => {
|
||||||
self.remote_control.as_mut().unwrap().find(pattern);
|
if let Some(rc) = self.remote_control.as_mut() {
|
||||||
|
rc.find(pattern);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -244,11 +232,9 @@ impl Television {
|
|||||||
}
|
}
|
||||||
Mode::RemoteControl => {
|
Mode::RemoteControl => {
|
||||||
if let Some(i) = self.rc_picker.selected() {
|
if let Some(i) = self.rc_picker.selected() {
|
||||||
return self
|
if let Some(rc) = &self.remote_control {
|
||||||
.remote_control
|
return rc.get_result(i.try_into().unwrap());
|
||||||
.as_ref()
|
}
|
||||||
.unwrap()
|
|
||||||
.get_result(i.try_into().unwrap());
|
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -491,12 +477,10 @@ impl Television {
|
|||||||
match self.mode {
|
match self.mode {
|
||||||
Mode::Channel => {
|
Mode::Channel => {
|
||||||
self.mode = Mode::RemoteControl;
|
self.mode = Mode::RemoteControl;
|
||||||
self.init_remote_control();
|
|
||||||
}
|
}
|
||||||
Mode::RemoteControl => {
|
Mode::RemoteControl => {
|
||||||
// this resets the RC picker
|
// this resets the RC picker
|
||||||
self.reset_picker_input();
|
self.reset_picker_input();
|
||||||
self.init_remote_control();
|
|
||||||
self.remote_control.as_mut().unwrap().find(EMPTY_STRING);
|
self.remote_control.as_mut().unwrap().find(EMPTY_STRING);
|
||||||
self.reset_picker_selection();
|
self.reset_picker_selection();
|
||||||
self.mode = Mode::Channel;
|
self.mode = Mode::Channel;
|
||||||
@ -528,7 +512,6 @@ impl Television {
|
|||||||
.remote_control
|
.remote_control
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
// FIXME: is the TelevisionChannel enum still worth it?
|
|
||||||
.zap(entry.name.as_str())?;
|
.zap(entry.name.as_str())?;
|
||||||
// this resets the RC picker
|
// this resets the RC picker
|
||||||
self.reset_picker_selection();
|
self.reset_picker_selection();
|
||||||
|
26
tests/app.rs
26
tests/app.rs
@ -3,9 +3,7 @@ use std::{collections::HashSet, path::PathBuf, time::Duration};
|
|||||||
use television::{
|
use television::{
|
||||||
action::Action,
|
action::Action,
|
||||||
app::{App, AppOptions},
|
app::{App, AppOptions},
|
||||||
channels::cable::prototypes::{
|
channels::cable::prototypes::{Cable, ChannelPrototype},
|
||||||
CableChannelPrototype, CableChannelPrototypes,
|
|
||||||
},
|
|
||||||
config::default_config_from_file,
|
config::default_config_from_file,
|
||||||
};
|
};
|
||||||
use tokio::{task::JoinHandle, time::timeout};
|
use tokio::{task::JoinHandle, time::timeout};
|
||||||
@ -23,19 +21,19 @@ const DEFAULT_TIMEOUT: Duration = Duration::from_millis(100);
|
|||||||
/// The app is started in a separate task and can be interacted with by sending
|
/// The app is started in a separate task and can be interacted with by sending
|
||||||
/// actions to the action channel.
|
/// actions to the action channel.
|
||||||
fn setup_app(
|
fn setup_app(
|
||||||
channel_prototype: Option<CableChannelPrototype>,
|
channel_prototype: Option<ChannelPrototype>,
|
||||||
select_1: bool,
|
select_1: bool,
|
||||||
exact: bool,
|
exact: bool,
|
||||||
) -> (
|
) -> (
|
||||||
JoinHandle<television::app::AppOutput>,
|
JoinHandle<television::app::AppOutput>,
|
||||||
tokio::sync::mpsc::UnboundedSender<Action>,
|
tokio::sync::mpsc::UnboundedSender<Action>,
|
||||||
) {
|
) {
|
||||||
let chan: CableChannelPrototype = channel_prototype.unwrap_or_else(|| {
|
let chan: ChannelPrototype = channel_prototype.unwrap_or_else(|| {
|
||||||
let target_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
let target_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||||
.join("tests")
|
.join("tests")
|
||||||
.join("target_dir");
|
.join("target_dir");
|
||||||
std::env::set_current_dir(&target_dir).unwrap();
|
std::env::set_current_dir(&target_dir).unwrap();
|
||||||
CableChannelPrototype::default()
|
ChannelPrototype::default()
|
||||||
});
|
});
|
||||||
let mut config = default_config_from_file().unwrap();
|
let mut config = default_config_from_file().unwrap();
|
||||||
// this speeds up the tests
|
// this speeds up the tests
|
||||||
@ -49,13 +47,7 @@ fn setup_app(
|
|||||||
false,
|
false,
|
||||||
config.application.tick_rate,
|
config.application.tick_rate,
|
||||||
);
|
);
|
||||||
let mut app = App::new(
|
let mut app = App::new(chan, config, input, options, &Cable::default());
|
||||||
chan,
|
|
||||||
config,
|
|
||||||
input,
|
|
||||||
options,
|
|
||||||
&CableChannelPrototypes::default(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// retrieve the app's action channel handle in order to send a quit action
|
// retrieve the app's action channel handle in order to send a quit action
|
||||||
let tx = app.action_tx.clone();
|
let tx = app.action_tx.clone();
|
||||||
@ -220,8 +212,8 @@ async fn test_app_exact_search_positive() {
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 3)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 3)]
|
||||||
async fn test_app_exits_when_select_1_and_only_one_result() {
|
async fn test_app_exits_when_select_1_and_only_one_result() {
|
||||||
let prototype = CableChannelPrototype::new(
|
let prototype = ChannelPrototype::new(
|
||||||
"cable",
|
"some_channel",
|
||||||
"echo file1.txt",
|
"echo file1.txt",
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
@ -258,8 +250,8 @@ async fn test_app_exits_when_select_1_and_only_one_result() {
|
|||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 3)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 3)]
|
||||||
async fn test_app_does_not_exit_when_select_1_and_more_than_one_result() {
|
async fn test_app_does_not_exit_when_select_1_and_more_than_one_result() {
|
||||||
let prototype = CableChannelPrototype::new(
|
let prototype = ChannelPrototype::new(
|
||||||
"cable",
|
"some_channel",
|
||||||
"echo 'file1.txt\nfile2.txt'",
|
"echo 'file1.txt\nfile2.txt'",
|
||||||
false,
|
false,
|
||||||
None,
|
None,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user