feat: group global configuration under settings
This commit is contained in:
+104
-15
@@ -344,11 +344,18 @@ pub fn set_category_pinned(path: &Path, name: &str, pinned: bool) -> io::Result<
|
||||
if !categories.contains_key(name) {
|
||||
return Err(invalid(format!("Unknown category: {name:?}")));
|
||||
}
|
||||
if !document.contains_key("pinned_categories") {
|
||||
document.insert("pinned_categories", value(Array::new()));
|
||||
if !document.contains_key("settings") {
|
||||
document.insert("settings", Item::Table(Table::new()));
|
||||
}
|
||||
let array = document["pinned_categories"]
|
||||
.as_array_mut()
|
||||
let settings = document["settings"]
|
||||
.as_table_like_mut()
|
||||
.expect("settings were validated");
|
||||
if !settings.contains_key("pinned_categories") {
|
||||
settings.insert("pinned_categories", value(Array::new()));
|
||||
}
|
||||
let array = settings
|
||||
.get_mut("pinned_categories")
|
||||
.and_then(Item::as_array_mut)
|
||||
.expect("pins were validated");
|
||||
if let Some(index) = pins.iter().position(|pin| pin == name) {
|
||||
if !pinned {
|
||||
@@ -367,7 +374,16 @@ pub fn set_category_pinned(path: &Path, name: &str, pinned: bool) -> io::Result<
|
||||
}
|
||||
|
||||
fn parse_pins(document: &DocumentMut) -> io::Result<Vec<String>> {
|
||||
let Some(item) = document.get("pinned_categories") else {
|
||||
if document.contains_key("pinned_categories") {
|
||||
return Err(invalid("Move top-level pinned_categories into [settings]"));
|
||||
}
|
||||
let Some(settings) = document.get("settings") else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
let settings = settings
|
||||
.as_table_like()
|
||||
.ok_or_else(|| invalid("settings must be a TOML table"))?;
|
||||
let Some(item) = settings.get("pinned_categories") else {
|
||||
return Ok(Vec::new());
|
||||
};
|
||||
let array = item
|
||||
@@ -636,11 +652,82 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn settings_can_move_between_sections_and_pin_edits_preserve_their_order() {
|
||||
let directory = TestDirectory::new();
|
||||
let path = directory.0.join("config.toml");
|
||||
let sections = [
|
||||
(
|
||||
"settings",
|
||||
"[settings]\npinned_categories = ['A']\ncustom = 42\n",
|
||||
),
|
||||
(
|
||||
"channels",
|
||||
"[channels]\n'@example' = 'UC4QobU6STFB0P71PMvOGN5A'\n",
|
||||
),
|
||||
("categories", "[categories]\nA = []\nB = ['@example']\n"),
|
||||
];
|
||||
for order in [
|
||||
[0, 1, 2],
|
||||
[0, 2, 1],
|
||||
[1, 0, 2],
|
||||
[1, 2, 0],
|
||||
[2, 0, 1],
|
||||
[2, 1, 0],
|
||||
] {
|
||||
let original = order
|
||||
.iter()
|
||||
.map(|index| sections[*index].1)
|
||||
.collect::<String>();
|
||||
fs::write(&path, original).unwrap();
|
||||
let config = load(&path).unwrap();
|
||||
assert_eq!(config.pinned_categories, ["A"]);
|
||||
assert_eq!(
|
||||
config.categories["B"],
|
||||
["https://www.youtube.com/channel/UC4QobU6STFB0P71PMvOGN5A"]
|
||||
);
|
||||
assert_eq!(
|
||||
set_category_pinned(&path, "B", true)
|
||||
.unwrap()
|
||||
.pinned_categories,
|
||||
["A", "B"]
|
||||
);
|
||||
assert_eq!(
|
||||
set_category_pinned(&path, "A", false)
|
||||
.unwrap()
|
||||
.pinned_categories,
|
||||
["B"]
|
||||
);
|
||||
let saved = load(&path).unwrap();
|
||||
assert_eq!(saved.pinned_categories, ["B"]);
|
||||
assert_eq!(saved.categories, config.categories);
|
||||
assert_eq!(saved.channels, config.channels);
|
||||
let contents = fs::read_to_string(&path).unwrap();
|
||||
let document = contents.parse::<DocumentMut>().unwrap();
|
||||
assert_eq!(document["settings"]["custom"].as_integer(), Some(42));
|
||||
assert_eq!(
|
||||
document.iter().map(|(name, _)| name).collect::<Vec<_>>(),
|
||||
order
|
||||
.iter()
|
||||
.map(|index| sections[*index].0)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
}
|
||||
for original in [
|
||||
"[categories]\nA = []\n",
|
||||
"[settings]\ncustom = 42\n[categories]\nA = []\n",
|
||||
] {
|
||||
fs::write(&path, original).unwrap();
|
||||
set_category_pinned(&path, "A", true).unwrap();
|
||||
assert_eq!(load(&path).unwrap().pinned_categories, ["A"]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn category_pins_persist_without_changing_memberships_or_manual_preferences() {
|
||||
let directory = TestDirectory::new();
|
||||
let path = directory.0.join("config.toml");
|
||||
fs::write(&path, "# favorites\npinned_categories = ['Japanese'] # pin note\n[other]\nvalue = 42\n[categories]\nJapanese = []\nChinese = []\n").unwrap();
|
||||
fs::write(&path, "# favorites\n[settings]\npinned_categories = ['Japanese'] # pin note\n[other]\nvalue = 42\n[categories]\nJapanese = []\nChinese = []\n").unwrap();
|
||||
let pinned = set_category_pinned(&path, "Chinese", true).unwrap();
|
||||
assert_eq!(pinned.pinned_categories, ["Japanese", "Chinese"]);
|
||||
let tags = BTreeMap::from([("Japanese".into(), true)]);
|
||||
@@ -662,9 +749,11 @@ mod tests {
|
||||
assert!(set_category_pinned(&path, "Missing", true).is_err());
|
||||
assert_eq!(fs::read_to_string(&path).unwrap(), content);
|
||||
for invalid in [
|
||||
"pinned_categories = true",
|
||||
"pinned_categories = [4]",
|
||||
"pinned_categories = ['A', ' A ']",
|
||||
"[settings]\npinned_categories = true",
|
||||
"[settings]\npinned_categories = [4]",
|
||||
"[settings]\npinned_categories = ['A', ' A ']",
|
||||
"settings = []",
|
||||
"pinned_categories = ['A']",
|
||||
] {
|
||||
fs::write(&path, format!("{invalid}\n[categories]\nA = []\n")).unwrap();
|
||||
let before = fs::read(&path).unwrap();
|
||||
@@ -688,7 +777,7 @@ mod tests {
|
||||
let direct_url = format!("https://www.youtube.com/channel/{direct}");
|
||||
let manual_url = "https://www.youtube.com/@manual".to_owned();
|
||||
let original = format!(
|
||||
"# manual preferences\npinned_categories = ['Source'] # pin note\nplayer = 'mpv'\n[channels]\n'@alias' = '{first}' # alias mapping\n'@second' = '{second_url}'\n[other]\nvalue = 42\n[categories]\n' Source ' = [\n '@alias', # source channel\n '{first_url}',\n '{second}',\n 'https://youtube.com/@manual',\n '@manual',\n '{direct}', # direct channel\n] # source note\nKeep = ['@alias', '@unrelated'] # keep note\nDrop = [\n '@manual', # removed channel note\n 'https://youtube.com/@alias',\n '@unrelated', # unrelated note\n]\nUntouched = ['@unrelated']\n"
|
||||
"# manual preferences\n[settings]\npinned_categories = ['Source'] # pin note\nplayer = 'mpv'\n[channels]\n'@alias' = '{first}' # alias mapping\n'@second' = '{second_url}'\n[other]\nvalue = 42\n[categories]\n' Source ' = [\n '@alias', # source channel\n '{first_url}',\n '{second}',\n 'https://youtube.com/@manual',\n '@manual',\n '{direct}', # direct channel\n] # source note\nKeep = ['@alias', '@unrelated'] # keep note\nDrop = [\n '@manual', # removed channel note\n 'https://youtube.com/@alias',\n '@unrelated', # unrelated note\n]\nUntouched = ['@unrelated']\n"
|
||||
);
|
||||
fs::write(&path, &original).unwrap();
|
||||
let opened = load(&path).unwrap();
|
||||
@@ -737,11 +826,11 @@ mod tests {
|
||||
before["channels"].to_string()
|
||||
);
|
||||
assert_eq!(
|
||||
document["pinned_categories"].to_string(),
|
||||
before["pinned_categories"].to_string()
|
||||
document["settings"]["pinned_categories"].to_string(),
|
||||
before["settings"]["pinned_categories"].to_string()
|
||||
);
|
||||
assert_eq!(document["other"]["value"].as_integer(), Some(42));
|
||||
assert_eq!(document["player"].as_str(), Some("mpv"));
|
||||
assert_eq!(document["settings"]["player"].as_str(), Some("mpv"));
|
||||
let new = document["categories"]["New"].as_array().unwrap();
|
||||
let raw: Vec<_> = new.iter().map(|entry| entry.as_str().unwrap()).collect();
|
||||
assert_eq!(raw, ["@manual", direct, "@alias", "@second"]);
|
||||
@@ -828,7 +917,7 @@ mod tests {
|
||||
"[categories]\nSource = ['@manual']\n' Source ' = []\n",
|
||||
"channels = true\n[categories]\nSource = ['@manual']\n",
|
||||
"[channels]\n'@manual' = '@different'\n[categories]\nSource = ['@manual']\n",
|
||||
"pinned_categories = [7]\n[categories]\nSource = ['@manual']\n",
|
||||
"[settings]\npinned_categories = [7]\n[categories]\nSource = ['@manual']\n",
|
||||
] {
|
||||
fs::write(&path, contents).unwrap();
|
||||
assert!(set_category_tags(&path, "Source", &expected, &tags).is_err());
|
||||
@@ -861,7 +950,7 @@ mod tests {
|
||||
let path = directory.0.join("config.toml");
|
||||
let id = "UC4QobU6STFB0P71PMvOGN5A";
|
||||
let registry = format!(
|
||||
"pinned_categories = ['Source']\n[channels]\n'@mapped' = '{id}'\n[categories]\n"
|
||||
"[settings]\npinned_categories = ['Source']\n[channels]\n'@mapped' = '{id}'\n[categories]\n"
|
||||
);
|
||||
for source in ["'@mapped', '@manual'", "'UCabcdefghijklmnopqrstuv'"] {
|
||||
let original = format!("{registry}Source = [{source}]\n");
|
||||
|
||||
Reference in New Issue
Block a user