refactor(cable): split cable related code into separate submodules (#486)

This refactors `television/channels/cable.rs` into two additional
submodules:
- `television/channels/cable/preview.rs`
- `television/channels/cable/prototypes.rs`
This commit is contained in:
Alexandre Pasmantier 2025-04-29 18:07:42 +02:00 committed by GitHub
parent 0514a914b6
commit 4385317e06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 156 additions and 129 deletions

View File

@ -9,7 +9,7 @@ use ratatui::style::Color;
use ratatui::widgets::{Block, BorderType, Borders, ListDirection, Padding};
use ratatui::Terminal;
use television::action::Action;
use television::channels::cable::CableChannelPrototype;
use television::channels::cable::prototypes::CableChannelPrototype;
use television::channels::entry::into_ranges;
use television::channels::entry::{Entry, PreviewType};
use television::channels::OnAir;

View File

@ -2,11 +2,13 @@ use std::path::PathBuf;
use rustc_hash::FxHashMap;
use crate::channels::cable::{CableChannelPrototype, CableChannels};
use anyhow::Result;
use tracing::{debug, error};
use crate::config::get_config_dir;
use crate::{
channels::cable::prototypes::{CableChannelPrototype, CableChannels},
config::get_config_dir,
};
/// Just a proxy struct to deserialize prototypes
#[derive(Debug, serde::Deserialize, Default)]

View File

@ -1,33 +1,20 @@
use rustc_hash::FxHashMap;
use std::{
fmt::{self, Display, Formatter},
ops::Deref,
};
use std::collections::HashSet;
use std::io::{BufRead, BufReader};
use std::process::Stdio;
use anyhow::Result;
use regex::Regex;
use preview::{parse_preview_kind, PreviewKind};
use prototypes::{CableChannelPrototype, DEFAULT_DELIMITER};
use rustc_hash::{FxBuildHasher, FxHashSet};
use tracing::debug;
use crate::channels::entry::{Entry, PreviewCommand, PreviewType};
use crate::channels::OnAir;
use crate::matcher::Matcher;
use crate::matcher::{config::Config, injector::Injector};
use crate::utils::command::shell_command;
use crate::{
cable::ChannelPrototypes,
channels::entry::{Entry, PreviewCommand, PreviewType},
};
#[derive(Debug, Clone, PartialEq)]
pub enum PreviewKind {
Command(PreviewCommand),
Builtin(PreviewType),
None,
}
pub mod preview;
pub mod prototypes;
#[allow(dead_code)]
pub struct Channel {
@ -69,17 +56,6 @@ impl From<CableChannelPrototype> for Channel {
}
}
pub fn parse_preview_kind(command: &PreviewCommand) -> Result<PreviewKind> {
debug!("Parsing preview kind for command: {:?}", command);
let re = Regex::new(r"^\:(\w+)\:$").unwrap();
if let Some(captures) = re.captures(&command.command) {
let preview_type = PreviewType::try_from(&captures[1])?;
Ok(PreviewKind::Builtin(preview_type))
} else {
Ok(PreviewKind::Command(command.clone()))
}
}
impl Channel {
pub fn new(
name: &str,
@ -234,94 +210,3 @@ impl OnAir for Channel {
self.preview_kind != PreviewKind::None
}
}
#[derive(Clone, Debug, serde::Deserialize, PartialEq)]
pub struct CableChannelPrototype {
pub name: String,
pub source_command: String,
#[serde(default)]
pub interactive: bool,
pub preview_command: Option<String>,
#[serde(default = "default_delimiter")]
pub preview_delimiter: Option<String>,
}
impl CableChannelPrototype {
pub fn new(
name: &str,
source_command: &str,
interactive: bool,
preview_command: Option<String>,
preview_delimiter: Option<String>,
) -> Self {
Self {
name: name.to_string(),
source_command: source_command.to_string(),
interactive,
preview_command,
preview_delimiter,
}
}
}
const DEFAULT_PROTOTYPE_NAME: &str = "files";
const DEFAULT_SOURCE_COMMAND: &str = "fd -t f";
const DEFAULT_PREVIEW_COMMAND: &str = ":files:";
impl Default for CableChannelPrototype {
fn default() -> Self {
Self {
name: DEFAULT_PROTOTYPE_NAME.to_string(),
source_command: DEFAULT_SOURCE_COMMAND.to_string(),
interactive: false,
preview_command: Some(DEFAULT_PREVIEW_COMMAND.to_string()),
preview_delimiter: Some(DEFAULT_DELIMITER.to_string()),
}
}
}
pub const DEFAULT_DELIMITER: &str = " ";
#[allow(clippy::unnecessary_wraps)]
fn default_delimiter() -> Option<String> {
Some(DEFAULT_DELIMITER.to_string())
}
impl Display for CableChannelPrototype {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
#[derive(Debug, serde::Deserialize)]
pub struct CableChannels(pub FxHashMap<String, CableChannelPrototype>);
impl Deref for CableChannels {
type Target = FxHashMap<String, CableChannelPrototype>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(unix)]
const DEFAULT_CABLE_CHANNELS_FILE: &str =
include_str!("../../cable/unix-channels.toml");
#[cfg(not(unix))]
const DEFAULT_CABLE_CHANNELS_FILE: &str =
include_str!("../../cable/windows-channels.toml");
impl Default for CableChannels {
/// Fallback to the default cable channels specification (the template file
/// included in the repo).
fn default() -> Self {
let pts =
toml::from_str::<ChannelPrototypes>(DEFAULT_CABLE_CHANNELS_FILE)
.expect("Unable to parse default cable channels");
let mut channels = FxHashMap::default();
for prototype in pts.prototypes {
channels.insert(prototype.name.clone(), prototype);
}
CableChannels(channels)
}
}

View File

@ -0,0 +1,34 @@
use anyhow::Result;
use regex::Regex;
use tracing::debug;
use crate::channels::entry::{PreviewCommand, PreviewType};
#[derive(Debug, Clone, PartialEq)]
pub enum PreviewKind {
Command(PreviewCommand),
Builtin(PreviewType),
None,
}
/// Parses the preview command to determine if it is a built-in (i.e. ":files:") or custom command.
///
/// # Example:
/// ```
/// use television::channels::entry::{PreviewCommand, PreviewType};
/// use television::channels::cable::preview::{parse_preview_kind, PreviewKind};
///
/// let command = PreviewCommand::new("cat {0}", ":");
/// let preview_kind = parse_preview_kind(&command).unwrap();
/// assert_eq!(preview_kind, PreviewKind::Command(command));
/// ```
pub fn parse_preview_kind(command: &PreviewCommand) -> Result<PreviewKind> {
debug!("Parsing preview kind for command: {:?}", command);
let re = Regex::new(r"^\:(\w+)\:$").unwrap();
if let Some(captures) = re.captures(&command.command) {
let preview_type = PreviewType::try_from(&captures[1])?;
Ok(PreviewKind::Builtin(preview_type))
} else {
Ok(PreviewKind::Command(command.clone()))
}
}

View File

@ -0,0 +1,98 @@
use rustc_hash::FxHashMap;
use std::{
fmt::{self, Display, Formatter},
ops::Deref,
};
use crate::cable::ChannelPrototypes;
#[derive(Clone, Debug, serde::Deserialize, PartialEq)]
pub struct CableChannelPrototype {
pub name: String,
pub source_command: String,
#[serde(default)]
pub interactive: bool,
pub preview_command: Option<String>,
#[serde(default = "default_delimiter")]
pub preview_delimiter: Option<String>,
}
impl CableChannelPrototype {
pub fn new(
name: &str,
source_command: &str,
interactive: bool,
preview_command: Option<String>,
preview_delimiter: Option<String>,
) -> Self {
Self {
name: name.to_string(),
source_command: source_command.to_string(),
interactive,
preview_command,
preview_delimiter,
}
}
}
const DEFAULT_PROTOTYPE_NAME: &str = "files";
const DEFAULT_SOURCE_COMMAND: &str = "fd -t f";
const DEFAULT_PREVIEW_COMMAND: &str = ":files:";
impl Default for CableChannelPrototype {
fn default() -> Self {
Self {
name: DEFAULT_PROTOTYPE_NAME.to_string(),
source_command: DEFAULT_SOURCE_COMMAND.to_string(),
interactive: false,
preview_command: Some(DEFAULT_PREVIEW_COMMAND.to_string()),
preview_delimiter: Some(DEFAULT_DELIMITER.to_string()),
}
}
}
pub const DEFAULT_DELIMITER: &str = " ";
#[allow(clippy::unnecessary_wraps)]
fn default_delimiter() -> Option<String> {
Some(DEFAULT_DELIMITER.to_string())
}
impl Display for CableChannelPrototype {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{}", self.name)
}
}
#[derive(Debug, serde::Deserialize)]
pub struct CableChannels(pub FxHashMap<String, CableChannelPrototype>);
impl Deref for CableChannels {
type Target = FxHashMap<String, CableChannelPrototype>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(unix)]
const DEFAULT_CABLE_CHANNELS_FILE: &str =
include_str!("../../../cable/unix-channels.toml");
#[cfg(not(unix))]
const DEFAULT_CABLE_CHANNELS_FILE: &str =
include_str!("../../cable/windows-channels.toml");
impl Default for CableChannels {
/// Fallback to the default cable channels specification (the template file
/// included in the repo).
fn default() -> Self {
let pts =
toml::from_str::<ChannelPrototypes>(DEFAULT_CABLE_CHANNELS_FILE)
.expect("Unable to parse default cable channels");
let mut channels = FxHashMap::default();
for prototype in pts.prototypes {
channels.insert(prototype.name.clone(), prototype);
}
CableChannels(channels)
}
}

View File

@ -1,6 +1,6 @@
use std::collections::HashSet;
use crate::channels::cable::CableChannels;
use crate::channels::cable::prototypes::CableChannels;
use crate::channels::entry::{Entry, PreviewType};
use crate::channels::{OnAir, TelevisionChannel};
use crate::matcher::{config::Config, Matcher};

View File

@ -4,8 +4,13 @@ use std::path::Path;
use anyhow::{anyhow, Result};
use tracing::debug;
use crate::channels::cable::{parse_preview_kind, CableChannels, PreviewKind};
use crate::channels::{cable::CableChannelPrototype, entry::PreviewCommand};
use crate::channels::cable::{
preview::parse_preview_kind, preview::PreviewKind,
prototypes::CableChannels,
};
use crate::channels::{
cable::prototypes::CableChannelPrototype, entry::PreviewCommand,
};
use crate::cli::args::{Cli, Command};
use crate::config::{KeyBindings, DEFAULT_CHANNEL};
use crate::{

View File

@ -6,7 +6,9 @@ use std::process::exit;
use anyhow::Result;
use clap::Parser;
use television::cable;
use television::channels::cable::{CableChannels, PreviewKind};
use television::channels::cable::{
preview::PreviewKind, prototypes::CableChannels,
};
use television::utils::clipboard::CLIPBOARD;
use tracing::{debug, error, info};
@ -186,7 +188,8 @@ pub fn determine_channel(
mod tests {
use rustc_hash::FxHashMap;
use television::{
cable::load_cable_channels, channels::cable::CableChannelPrototype,
cable::load_cable_channels,
channels::cable::prototypes::CableChannelPrototype,
};
use super::*;

View File

@ -3,7 +3,7 @@ use std::{collections::HashSet, path::PathBuf, time::Duration};
use television::{
action::Action,
app::{App, AppOptions},
channels::{cable::CableChannelPrototype, TelevisionChannel},
channels::{cable::prototypes::CableChannelPrototype, TelevisionChannel},
config::default_config_from_file,
};
use tokio::{task::JoinHandle, time::timeout};