feat: browse individual saved channels
This commit is contained in:
@@ -27,6 +27,23 @@ impl Config {
|
||||
channels.dedup();
|
||||
channels.into_iter().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn saved_channels(&self) -> Vec<(String, String)> {
|
||||
let mut labels = BTreeMap::new();
|
||||
// Mappings are sorted by handle, so aliases use the first readable label.
|
||||
for (handle, identity) in &self.channels {
|
||||
labels.entry(identity).or_insert(handle);
|
||||
}
|
||||
for identity in self.categories.values().flatten() {
|
||||
labels.entry(identity).or_insert(identity);
|
||||
}
|
||||
let mut channels: Vec<_> = labels
|
||||
.into_iter()
|
||||
.map(|(identity, label)| (label.clone(), identity.clone()))
|
||||
.collect();
|
||||
channels.sort_unstable();
|
||||
channels
|
||||
}
|
||||
}
|
||||
|
||||
pub fn config_path() -> io::Result<PathBuf> {
|
||||
@@ -1097,6 +1114,72 @@ mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_channels_use_readable_aliases_and_include_every_subscription_once() {
|
||||
let directory = TestDirectory::new();
|
||||
let path = directory.0.join("config.toml");
|
||||
let mapped_id = "UC4QobU6STFB0P71PMvOGN5A";
|
||||
let untagged_id = "UCabcdefghijklmnopqrstuv";
|
||||
let legacy_id = "UCzyxwvutsrqponmlkjihgfe";
|
||||
let mapped = format!("https://www.youtube.com/channel/{mapped_id}");
|
||||
let untagged = format!("https://www.youtube.com/channel/{untagged_id}");
|
||||
let legacy = format!("https://www.youtube.com/channel/{legacy_id}");
|
||||
fs::write(
|
||||
&path,
|
||||
format!(
|
||||
"[channels]\n'@zAlias' = '{mapped_id}'\n'https://youtube.com/@aAlias' = '{mapped}'\n'@untagged' = '{untagged_id}'\n[categories]\nFirst = ['@zAlias', '{mapped_id}', '@manual', '{legacy_id}']\nSecond = ['@aAlias', 'https://youtube.com/@manual', '{legacy}']\n"
|
||||
),
|
||||
)
|
||||
.unwrap();
|
||||
let config = load(&path).unwrap();
|
||||
let expected = vec![
|
||||
("@aAlias".to_owned(), mapped.clone()),
|
||||
("@untagged".to_owned(), untagged.clone()),
|
||||
(
|
||||
"https://www.youtube.com/@manual".to_owned(),
|
||||
"https://www.youtube.com/@manual".to_owned(),
|
||||
),
|
||||
(legacy.clone(), legacy.clone()),
|
||||
];
|
||||
assert_eq!(config.saved_channels(), expected);
|
||||
assert_eq!(load(&path).unwrap().saved_channels(), expected);
|
||||
let mut identities: Vec<_> = config
|
||||
.saved_channels()
|
||||
.into_iter()
|
||||
.map(|(_, identity)| identity)
|
||||
.collect();
|
||||
identities.sort_unstable();
|
||||
assert_eq!(identities, config.subscriptions());
|
||||
assert!(
|
||||
config
|
||||
.categories
|
||||
.values()
|
||||
.flatten()
|
||||
.all(|id| id != &untagged)
|
||||
);
|
||||
assert_eq!(config.channels["@zAlias"], mapped);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn saved_channels_support_empty_and_registry_only_configs() {
|
||||
assert!(Config::default().saved_channels().is_empty());
|
||||
let directory = TestDirectory::new();
|
||||
let path = directory.0.join("config.toml");
|
||||
let identity = "https://www.youtube.com/channel/UC4QobU6STFB0P71PMvOGN5A";
|
||||
fs::write(
|
||||
&path,
|
||||
format!("[channels]\n'@zAlias' = '{identity}'\n'@aAlias' = '{identity}'\n"),
|
||||
)
|
||||
.unwrap();
|
||||
let config = load(&path).unwrap();
|
||||
assert!(config.categories.is_empty());
|
||||
assert_eq!(
|
||||
config.saved_channels(),
|
||||
vec![("@aAlias".to_owned(), identity.to_owned())]
|
||||
);
|
||||
assert_eq!(config.subscriptions(), [identity]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn subscriptions_include_untagged_registry_and_manual_channels_after_tag_removal() {
|
||||
let directory = TestDirectory::new();
|
||||
|
||||
Reference in New Issue
Block a user