use std::collections::BTreeMap;
use crate::naming::CharacterSet;
use crate::vocab::{AudioFormat, StemFormat, VideoCoverRetention, WebpEncodeSettings};
use super::shape::{AreasConfig, Settings};
#[derive(Debug, Default)]
pub struct FlagOverrides {
pub token: Option<String>,
pub settings: Settings,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EffectiveSettings {
pub token: Option<String>,
pub stored_token: Option<String>,
pub token_command: Option<String>,
pub account_id: Option<String>,
pub format: AudioFormat,
pub concurrency: u32,
pub retries: u32,
pub min_newest: u32,
pub animated_covers: bool,
pub raw_animated_cover: bool,
pub video_cover_retention: VideoCoverRetention,
pub animated_cover_webp: WebpEncodeSettings,
pub details_sidecar: bool,
pub lyrics_sidecar: bool,
pub lrc_sidecar: bool,
pub video_mp4: bool,
pub download_stems: bool,
pub stem_format: StemFormat,
pub naming_template: String,
pub character_set: CharacterSet,
pub areas: Option<AreasConfig>,
pub album_overrides: BTreeMap<String, String>,
pub lead_tracks: Vec<String>,
pub number_singletons: bool,
}
impl EffectiveSettings {
pub fn requires_ffmpeg(&self) -> bool {
matches!(self.format, AudioFormat::Flac | AudioFormat::Alac) || self.animated_covers
}
}
pub fn animated_covers_flag_overridden(flag: Option<bool>, effective_animated: bool) -> bool {
flag == Some(true) && !effective_animated
}
#[cfg(test)]
mod tests {
use super::*;
use crate::config::Config;
use crate::config::fixtures::{no_env, no_flags};
#[test]
fn audio_format_display_roundtrip() {
for fmt in [
AudioFormat::Mp3,
AudioFormat::Flac,
AudioFormat::Wav,
AudioFormat::Alac,
] {
let s = fmt.to_string();
assert_eq!(s.parse::<AudioFormat>().unwrap(), fmt);
}
}
#[test]
fn audio_format_ext() {
assert_eq!(AudioFormat::Mp3.ext(), "mp3");
assert_eq!(AudioFormat::Flac.ext(), "flac");
assert_eq!(AudioFormat::Wav.ext(), "wav");
assert_eq!(AudioFormat::Alac.ext(), "m4a");
}
fn base_settings(format: AudioFormat) -> EffectiveSettings {
let toml = "[accounts.a]\n";
let cfg = Config::from_toml(toml).unwrap();
let mut eff = cfg.resolve("a", None, &no_env(), &no_flags()).unwrap();
eff.format = format;
eff
}
#[test]
fn requires_ffmpeg_truth_table() {
for (label, format, animated, raw, want) in [
("flac, no covers", AudioFormat::Flac, false, false, true),
("flac, animated", AudioFormat::Flac, true, false, true),
("alac, no covers", AudioFormat::Alac, false, false, true),
("mp3, no covers", AudioFormat::Mp3, false, false, false),
("mp3, animated webp", AudioFormat::Mp3, true, false, true),
(
"mp3, both webp and raw mp4",
AudioFormat::Mp3,
true,
true,
true,
),
("mp3, raw mp4 only", AudioFormat::Mp3, false, true, false),
] {
let mut eff = base_settings(format);
eff.animated_covers = animated;
eff.raw_animated_cover = raw;
assert_eq!(eff.requires_ffmpeg(), want, "{label}");
}
}
#[test]
fn animated_covers_flag_override_truth_table() {
for (label, flag, effective, want) in [
("neither/mp4 drops the flag", Some(true), false, true),
("webp/both honours the flag", Some(true), true, false),
("absent flag, effective false", None, false, false),
("absent flag, effective true", None, true, false),
] {
assert_eq!(
animated_covers_flag_overridden(flag, effective),
want,
"{label}"
);
}
}
}