mirror of
https://github.com/alexpasmantier/television.git
synced 2025-06-06 11:35:25 +00:00
122 lines
3.6 KiB
Rust
122 lines
3.6 KiB
Rust
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;
|
|
|
|
/// Just a proxy struct to deserialize prototypes
|
|
#[derive(Debug, serde::Deserialize, Default)]
|
|
struct ChannelPrototypes {
|
|
#[serde(rename = "cable_channel")]
|
|
prototypes: Vec<CableChannelPrototype>,
|
|
}
|
|
|
|
const CABLE_FILE_NAME_SUFFIX: &str = "channels";
|
|
const CABLE_FILE_FORMAT: &str = "toml";
|
|
|
|
#[cfg(unix)]
|
|
const DEFAULT_CABLE_CHANNELS: &str =
|
|
include_str!("../cable/unix-channels.toml");
|
|
|
|
#[cfg(not(unix))]
|
|
const DEFAULT_CABLE_CHANNELS: &str =
|
|
include_str!("../cable/windows-channels.toml");
|
|
|
|
const DEFAULT_CABLE_CHANNELS_FILE_NAME: &str = "default_channels.toml";
|
|
|
|
/// Load the cable configuration from the config directory.
|
|
///
|
|
/// Cable is loaded by compiling all files that match the following
|
|
/// pattern in the config directory: `*channels.toml`.
|
|
///
|
|
/// # Example:
|
|
/// ```ignore
|
|
/// config_folder/
|
|
/// ├── cable_channels.toml
|
|
/// ├── my_channels.toml
|
|
/// └── windows_channels.toml
|
|
/// ```
|
|
pub fn load_cable_channels() -> Result<CableChannels> {
|
|
let config_dir = get_config_dir();
|
|
|
|
// list all files in the config directory
|
|
let files = std::fs::read_dir(&config_dir)?;
|
|
|
|
// filter the files that match the pattern
|
|
let mut file_paths: Vec<PathBuf> = files
|
|
.filter_map(|f| f.ok().map(|f| f.path()))
|
|
.filter(|p| is_cable_file_format(p) && p.is_file())
|
|
.collect();
|
|
|
|
debug!("Found cable channel files: {:?}", file_paths);
|
|
|
|
// If no cable provider files are found, write the default provider for the current
|
|
// platform to the config directory
|
|
if file_paths.is_empty() {
|
|
debug!("No user defined cable channels found");
|
|
// write the default cable channels to the config directory
|
|
let default_channels_path =
|
|
config_dir.join(DEFAULT_CABLE_CHANNELS_FILE_NAME);
|
|
std::fs::write(&default_channels_path, DEFAULT_CABLE_CHANNELS)?;
|
|
file_paths.push(default_channels_path);
|
|
}
|
|
|
|
let user_defined_prototypes = file_paths.iter().fold(
|
|
Vec::<CableChannelPrototype>::new(),
|
|
|mut acc, p| {
|
|
match toml::from_str::<ChannelPrototypes>(
|
|
&std::fs::read_to_string(p)
|
|
.expect("Unable to read configuration file"),
|
|
) {
|
|
Ok(prototypes) => acc.extend(prototypes.prototypes),
|
|
Err(e) => {
|
|
error!(
|
|
"Failed to parse cable channel file {:?}: {}",
|
|
p, e
|
|
);
|
|
}
|
|
}
|
|
acc
|
|
},
|
|
);
|
|
|
|
debug!("Loaded cable channels: {:?}", user_defined_prototypes);
|
|
|
|
let mut cable_channels = FxHashMap::default();
|
|
for prototype in user_defined_prototypes {
|
|
cable_channels.insert(prototype.name.clone(), prototype);
|
|
}
|
|
Ok(CableChannels(cable_channels))
|
|
}
|
|
|
|
fn is_cable_file_format<P>(p: P) -> bool
|
|
where
|
|
P: AsRef<std::path::Path>,
|
|
{
|
|
let p = p.as_ref();
|
|
p.file_stem()
|
|
.and_then(|s| s.to_str())
|
|
.map_or(false, |s| s.ends_with(CABLE_FILE_NAME_SUFFIX))
|
|
&& p.extension()
|
|
.and_then(|e| e.to_str())
|
|
.map_or(false, |e| e.to_lowercase() == CABLE_FILE_FORMAT)
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_is_cable_file() {
|
|
let path = std::path::Path::new("cable_channels.toml");
|
|
assert!(is_cable_file_format(path));
|
|
|
|
let path = std::path::Path::new(DEFAULT_CABLE_CHANNELS_FILE_NAME);
|
|
assert!(is_cable_file_format(path));
|
|
}
|
|
}
|