// 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 bytes::Bytes; macro_rules! get { ($default:path, $config:expr, $key_one:ident, $($keys:ident),*) => { { let maybe_value = get!{@option $config, $key_one, $($keys),*}; if let Some(value) = maybe_value { value } else { $default().to_owned() } } }; (@option $config:expr, $key_one:ident, $($keys:ident),*) => { if let Some(key) = $config.$key_one.clone() { get!{@option key, $($keys),*} } else { None } }; (@option $config:expr, $key_one:ident) => { $config.$key_one }; (@path_if_none $config:expr, $option_default:expr, $default:path, $key_one:ident, $($keys:ident),*) => { { let maybe_download_dir: Option = get! {@option $config, $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:ident, $($keys:ident),*) => { get! {@path_if_none $config, None, $default, $key_one, $($keys),*} }; } impl Config { pub fn from_config_file( db_path: Option, config_path: Option, ) -> Result { let config_file_path = config_path .map(Ok) .unwrap_or_else(|| -> Result<_> { paths::config_path() })?; let config: super::definitions::ConfigFile = toml::from_str(&read_to_string(config_file_path).unwrap_or("".to_owned())) .context("Failed to parse the config file as toml")?; Ok(Self { select: SelectConfig { playback_speed: get! {select::playback_speed, config, select, playback_speed}, subtitle_langs: get! {select::subtitle_langs, config, select, subtitle_langs}, }, watch: WatchConfig { local_comments_length: get! {watch::local_comments_length, config, watch, local_comments_length}, }, update: UpdateConfig { max_backlog: get! {update::max_backlog, config, update, max_backlog}, }, 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: String = get! {download::max_cache_size, config, download, max_cache_size}; let number: Bytes = bytes_str .parse() .context("Failed to parse max_cache_size")?; number }, }, }) } }