Files
RustBox/src/db/special_folders.rs
T
Mateusz Gruszczyński 698e3c4a1b
RustBox CI / rustbox (push) Canceled after 0s
v0.1.1
2026-10-31 00:37:16 +01:00

126 lines
4.1 KiB
Rust

use crate::db::pool::{Connection, OptionalExtension, params};
use serde::{Deserialize, Serialize};
const ROLES: &[&str] = &["sent", "drafts", "trash", "junk", "archive"];
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct SpecialFolders {
pub sent: Option<String>,
pub drafts: Option<String>,
pub trash: Option<String>,
pub junk: Option<String>,
pub archive: Option<String>,
}
fn valid_role(role: &str) -> bool {
ROLES.contains(&role)
}
pub fn get_role(conn: &Connection, role: &str) -> Result<Option<String>, String> {
if !valid_role(role) {
return Err(format!("Invalid special folder role: {role}"));
}
conn.query_row(crate::dbq!(db_special_folders::Q_001), [role], |row| {
row.get(0)
})
.optional()
.map_err(|e| format!("Failed to read special folder {role}: {e}"))
}
pub fn get_all(conn: &Connection) -> Result<SpecialFolders, String> {
Ok(SpecialFolders {
sent: get_role(conn, "sent")?,
drafts: get_role(conn, "drafts")?,
trash: get_role(conn, "trash")?,
junk: get_role(conn, "junk")?,
archive: get_role(conn, "archive")?,
})
}
pub fn set_role(conn: &Connection, role: &str, folder_name: Option<&str>) -> Result<(), String> {
if !valid_role(role) {
return Err(format!("Invalid special folder role: {role}"));
}
match folder_name.map(str::trim).filter(|value| !value.is_empty()) {
Some(folder) => {
let exists: bool = conn
.query_row(crate::dbq!(db_special_folders::Q_002), [folder], |row| {
row.get(0)
})
.map_err(|e| format!("Failed to validate folder {folder}: {e}"))?;
if !exists {
return Err(format!("Folder does not exist: {folder}"));
}
conn.execute(
crate::dbq!(db_special_folders::Q_003),
params![role, folder],
)
.map_err(|e| format!("Failed to set special folder {role}: {e}"))?;
}
None => {
conn.execute(crate::dbq!(db_special_folders::Q_004), [role])
.map_err(|e| format!("Failed to clear special folder {role}: {e}"))?;
}
}
Ok(())
}
pub fn update_all(conn: &Connection, folders: &SpecialFolders) -> Result<SpecialFolders, String> {
set_role(conn, "sent", folders.sent.as_deref())?;
set_role(conn, "drafts", folders.drafts.as_deref())?;
set_role(conn, "trash", folders.trash.as_deref())?;
set_role(conn, "junk", folders.junk.as_deref())?;
set_role(conn, "archive", folders.archive.as_deref())?;
get_all(conn)
}
pub fn rename_folder_reference(
conn: &Connection,
old_name: &str,
new_name: &str,
) -> Result<(), String> {
conn.execute(
crate::dbq!(db_special_folders::Q_005),
params![new_name, old_name],
)
.map_err(|e| format!("Failed to update special folder reference: {e}"))?;
Ok(())
}
pub fn clear_folder_reference(conn: &Connection, folder_name: &str) -> Result<(), String> {
conn.execute(crate::dbq!(db_special_folders::Q_006), [folder_name])
.map_err(|e| format!("Failed to clear special folder reference: {e}"))?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::db::folders;
use crate::db::pool::open_test_db;
#[test]
fn special_folders_roundtrip() {
let conn = open_test_db();
for name in ["Sent Items", "Drafts", "Deleted", "Junk", "Archive"] {
folders::insert_folder_if_new(&conn, name, Some("/"), "").unwrap();
}
let value = SpecialFolders {
sent: Some("Sent Items".into()),
drafts: Some("Drafts".into()),
trash: Some("Deleted".into()),
junk: Some("Junk".into()),
archive: Some("Archive".into()),
};
let saved = update_all(&conn, &value).unwrap();
assert_eq!(saved.sent.as_deref(), Some("Sent Items"));
assert_eq!(saved.trash.as_deref(), Some("Deleted"));
}
#[test]
fn rejects_unknown_folder() {
let conn = open_test_db();
assert!(set_role(&conn, "sent", Some("Missing")).is_err());
}
}