fix: return persisted workspace identity

This commit is contained in:
Keisuke Hirata 2026-06-23 16:29:48 +09:00
parent 31565c9b9e
commit 49c9e19074
No known key found for this signature in database

View File

@ -1,5 +1,5 @@
use std::fs; use std::fs::{self, OpenOptions};
use std::io::ErrorKind; use std::io::{ErrorKind, Write};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use chrono::{SecondsFormat, Utc}; use chrono::{SecondsFormat, Utc};
@ -67,10 +67,6 @@ impl WorkspaceIdentity {
} }
fn init(workspace_root: &Path, path: &Path, created_at: String) -> Result<Self> { fn init(workspace_root: &Path, path: &Path, created_at: String) -> Result<Self> {
if path.exists() {
let raw = fs::read_to_string(path)?;
return Self::parse_str(&raw, path);
}
validate_created_at(&created_at, path)?; validate_created_at(&created_at, path)?;
let display_name = workspace_display_name_from_root(workspace_root, path)?; let display_name = workspace_display_name_from_root(workspace_root, path)?;
let workspace_id = Uuid::now_v7().to_string(); let workspace_id = Uuid::now_v7().to_string();
@ -79,8 +75,7 @@ impl WorkspaceIdentity {
created_at, created_at,
display_name, display_name,
}; };
identity.write_new(path)?; identity.write_new_or_read_existing(path)
Ok(identity)
} }
fn from_file(parsed: WorkspaceIdentityFile, path: &Path) -> Result<Self> { fn from_file(parsed: WorkspaceIdentityFile, path: &Path) -> Result<Self> {
@ -94,7 +89,7 @@ impl WorkspaceIdentity {
}) })
} }
fn write_new(&self, path: &Path) -> Result<()> { fn write_new_or_read_existing(&self, path: &Path) -> Result<Self> {
if let Some(parent) = path.parent() { if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?; fs::create_dir_all(parent)?;
} }
@ -106,15 +101,19 @@ impl WorkspaceIdentity {
.map_err(|error| { .map_err(|error| {
workspace_identity_error(path, format!("failed to encode TOML: {error}")) workspace_identity_error(path, format!("failed to encode TOML: {error}"))
})?; })?;
let tmp = path.with_extension("toml.tmp");
fs::write(&tmp, raw)?; match OpenOptions::new().write(true).create_new(true).open(path) {
if path.exists() { Ok(mut file) => {
let _ = fs::remove_file(&tmp); file.write_all(raw.as_bytes())?;
let raw = fs::read_to_string(path)?; file.sync_all()?;
return Self::parse_str(&raw, path).map(|_| ()); Ok(self.clone())
}
Err(error) if error.kind() == ErrorKind::AlreadyExists => {
let raw = fs::read_to_string(path)?;
Self::parse_str(&raw, path)
}
Err(error) => Err(Error::Io(error)),
} }
fs::rename(tmp, path)?;
Ok(())
} }
} }
@ -248,6 +247,29 @@ mod tests {
assert_eq!(fs::read_to_string(path).unwrap(), raw); assert_eq!(fs::read_to_string(path).unwrap(), raw);
} }
#[test]
fn create_new_race_returns_existing_persisted_identity() {
let temp = tempfile::tempdir().unwrap();
let path = temp.path().join(".yoi/workspace.toml");
fs::create_dir_all(path.parent().unwrap()).unwrap();
let persisted_raw = format!(
"workspace_id = \"{FIXED_WORKSPACE_ID}\"\ncreated_at = \"{FIXED_CREATED_AT}\"\ndisplay_name = \"Persisted Project\"\n"
);
fs::write(&path, &persisted_raw).unwrap();
let generated = WorkspaceIdentity {
workspace_id: "0192f0e8-4d84-7d6e-b000-000000000002".to_string(),
created_at: "2026-06-24T00:00:00Z".to_string(),
display_name: "Generated Project".to_string(),
};
let returned = generated.write_new_or_read_existing(&path).unwrap();
assert_eq!(returned.workspace_id, FIXED_WORKSPACE_ID);
assert_eq!(returned.created_at, FIXED_CREATED_AT);
assert_eq!(returned.display_name, "Persisted Project");
assert_eq!(fs::read_to_string(path).unwrap(), persisted_raw);
}
#[test] #[test]
fn invalid_identity_file_fails_closed_without_rewriting() { fn invalid_identity_file_fails_closed_without_rewriting() {
let temp = tempfile::tempdir().unwrap(); let temp = tempfile::tempdir().unwrap();