mirror of
https://github.com/chatmail/core.git
synced 2026-05-06 06:46:35 +03:00
fix: tolerate empty existing directory in Accounts::new() (#7886)
This commit is contained in:
@@ -57,8 +57,8 @@ pub struct Accounts {
|
|||||||
impl Accounts {
|
impl Accounts {
|
||||||
/// Loads or creates an accounts folder at the given `dir`.
|
/// Loads or creates an accounts folder at the given `dir`.
|
||||||
pub async fn new(dir: PathBuf, writable: bool) -> Result<Self> {
|
pub async fn new(dir: PathBuf, writable: bool) -> Result<Self> {
|
||||||
if writable && !dir.exists() {
|
if writable {
|
||||||
Accounts::create(&dir).await?;
|
Self::ensure_accounts_dir(&dir).await?;
|
||||||
}
|
}
|
||||||
let events = Events::new();
|
let events = Events::new();
|
||||||
Accounts::open(events, dir, writable).await
|
Accounts::open(events, dir, writable).await
|
||||||
@@ -67,10 +67,9 @@ impl Accounts {
|
|||||||
/// Loads or creates an accounts folder at the given `dir`.
|
/// Loads or creates an accounts folder at the given `dir`.
|
||||||
/// Uses an existing events channel.
|
/// Uses an existing events channel.
|
||||||
pub async fn new_with_events(dir: PathBuf, writable: bool, events: Events) -> Result<Self> {
|
pub async fn new_with_events(dir: PathBuf, writable: bool, events: Events) -> Result<Self> {
|
||||||
if writable && !dir.exists() {
|
if writable {
|
||||||
Accounts::create(&dir).await?;
|
Self::ensure_accounts_dir(&dir).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Accounts::open(events, dir, writable).await
|
Accounts::open(events, dir, writable).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,14 +81,20 @@ impl Accounts {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new default structure.
|
/// Ensures the accounts directory and config file exist.
|
||||||
async fn create(dir: &Path) -> Result<()> {
|
/// Creates them if the directory doesn't exist, or if it exists but is empty.
|
||||||
|
/// Errors if the directory exists with files but no config.
|
||||||
|
async fn ensure_accounts_dir(dir: &Path) -> Result<()> {
|
||||||
|
if !dir.exists() {
|
||||||
fs::create_dir_all(dir)
|
fs::create_dir_all(dir)
|
||||||
.await
|
.await
|
||||||
.context("failed to create folder")?;
|
.context("Failed to create folder")?;
|
||||||
|
|
||||||
Config::new(dir).await?;
|
Config::new(dir).await?;
|
||||||
|
} else if !dir.join(CONFIG_NAME).exists() {
|
||||||
|
let mut rd = fs::read_dir(dir).await?;
|
||||||
|
ensure!(rd.next_entry().await?.is_none(), "{dir:?} is not empty");
|
||||||
|
Config::new(dir).await?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -915,6 +920,26 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
|
async fn test_account_new_empty_existing_dir() {
|
||||||
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
let p: PathBuf = dir.path().join("accounts");
|
||||||
|
|
||||||
|
// A non-empty directory without accounts.toml should fail.
|
||||||
|
fs::create_dir_all(&p).await.unwrap();
|
||||||
|
fs::write(p.join("stray_file.txt"), b"hello").await.unwrap();
|
||||||
|
assert!(Accounts::new(p.clone(), true).await.is_err());
|
||||||
|
|
||||||
|
// Clean up to an empty directory.
|
||||||
|
fs::remove_file(p.join("stray_file.txt")).await.unwrap();
|
||||||
|
|
||||||
|
// An empty directory without accounts.toml should succeed.
|
||||||
|
let mut accounts = Accounts::new(p.clone(), true).await.unwrap();
|
||||||
|
assert_eq!(accounts.accounts.len(), 0);
|
||||||
|
let id = accounts.add_account().await.unwrap();
|
||||||
|
assert_eq!(id, 1);
|
||||||
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||||
async fn test_account_new_open_conflict() {
|
async fn test_account_new_open_conflict() {
|
||||||
let dir = tempfile::tempdir().unwrap();
|
let dir = tempfile::tempdir().unwrap();
|
||||||
|
|||||||
Reference in New Issue
Block a user