// yt - A fully featured command line YouTube client // // Copyright (C) 2024 Benedikt Peetz // SPDX-License-Identifier: GPL-3.0-or-later // // This file is part of Yt. // // You should have received a copy of the License along with this program. // If not, see . use crate::config::{DownloadConfig, PathsConfig, SelectConfig, WatchConfig}; use super::{ default::{create_path, download, paths, select, update, watch}, Config, UpdateConfig, }; use std::{fs::read_to_string, path::PathBuf}; use anyhow::{Context, Result}; use toml::Table; use bytes::Bytes; macro_rules! get { ($default:path, $config:expr, $get_fn:ident, $key_one:expr, $($keys:expr),*) => { try_get!{@default $default, $config, $get_fn, $key_one, $($keys),*} .with_context(|| format!("Failed to parse '{}' as a '{}'", stringify!($key_one), stringify!($get_fn)))? }; (@path_if_none $config:expr, $option_default:expr, $default:path, $key_one:expr, $($keys:expr),*) => { { let maybe_download_dir = try_get! {@option $config, as_str, $key_one, $($keys),*}; let down_dir = if let Some(dir) = maybe_download_dir { PathBuf::from(dir) } else { if let Some(path) = $option_default { path } else { $default() .with_context(|| format!("Failed to get default path for: '{}.{}'", stringify!($key_one), stringify!($($keys),*)))? } }; create_path(down_dir)? } }; (@path $config:expr, $default:path, $key_one:expr, $($keys:expr),*) => { get! {@path_if_none $config, None, $default, $key_one, $($keys),*} }; } macro_rules! try_get { (@option $config:expr, $get_fn:ident, $key_one:expr, $($keys:expr),*) => { $config.get($key_one).map(|val| { try_get! {@option val, $get_fn, $($keys),*} }).flatten().flatten() }; (@option $config:expr, $get_fn:ident, $key_one:expr) => { $config.get($key_one).map(|val| val.$get_fn()) }; (@default $default:path, $config:expr, $get_fn:ident, $key_one:expr, $($keys:expr),*) => { if let Some(a) = $config.get($key_one) { try_get! {@default $default, a, $get_fn, $($keys),*} } else { Some($default()) } }; (@default $default:path, $config:expr, $get_fn:ident, $key_one:expr) => { if let Some(a) = $config.get($key_one) { a.$get_fn() } else { Some($default()) } }; } impl Config { pub fn from_config_file( db_path: Option, config_path: Option, ) -> Result { let config_file_path = config_path .map(|val| Ok(val)) .unwrap_or_else(|| -> Result<_> { paths::config_path() })?; let config: Table = read_to_string(config_file_path)? .parse() .context("Failed to parse the config file as toml")?; Ok(Self { select: SelectConfig { playback_speed: get! {select::playback_speed, config, as_float, "select", "playback_speed"}, subtitle_langs: get! {select::subtitle_langs, config, as_str, "select", "subtitle_langs"} .to_owned(), }, watch: WatchConfig { local_comments_length: get! {watch::local_comments_length, config, as_integer, "watch", "local_comments_length"} as usize, }, update: UpdateConfig { max_backlog: get! {update::max_backlog, config, as_integer, "update", "max_backlog"} as u32, }, paths: PathsConfig { download_dir: get! {@path config, paths::download_dir, "paths", "download_dir"}, mpv_config_path: get! {@path config, paths::mpv_config_path, "paths", "mpv_config_path"}, mpv_input_path: get! {@path config, paths::mpv_input_path, "paths", "mpv_input_path"}, database_path: get! {@path_if_none config, db_path, paths::database_path, "paths", "database_path"}, last_selection_path: get! {@path config, paths::last_selection_path, "paths", "last_selection_path"}, }, download: DownloadConfig { max_cache_size: { let bytes_str = get! {download::max_cache_size, config, as_str, "download", "max_cache_path"}; let number: Bytes = bytes_str .parse() .context("Failed to parse max_cache_size")?; number.as_u64() }, }, }) } }