Add FileIoErr::AlreadyExists + some Windows fixes

This commit is contained in:
Richard Feldman 2024-07-04 13:54:54 -04:00
parent f6016c1009
commit 2fe1604652
No known key found for this signature in database
GPG key ID: F1F21AA5B1D9E43B
2 changed files with 51 additions and 12 deletions

View file

@ -1,11 +1,11 @@
use crate::native_path::NativePath; use crate::native_path::NativePath;
use core::{ffi::CStr, fmt, mem::MaybeUninit}; use core::{fmt, mem::MaybeUninit};
#[cfg(windows)] #[cfg(windows)]
use widestring::U16CString; use widestring::U16CStr;
#[cfg(unix)] #[cfg(unix)]
use core::ffi::{c_char, c_int}; use core::ffi::{c_char, c_int, CStr};
#[cfg(unix)] #[cfg(unix)]
pub struct File { pub struct File {
@ -96,6 +96,14 @@ pub enum FileIoErr {
#[cfg(windows)] #[cfg(windows)]
/// ERROR_ACCESS_DENIED: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- /// ERROR_ACCESS_DENIED: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
AccessDenied = 5, AccessDenied = 5,
#[cfg(unix)]
/// EEXIST: https://www.man7.org/linux/man-pages/man3/errno.3.html
AlreadyExists = 17,
#[cfg(windows)]
/// ERROR_FILE_EXISTS: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499-
AlreadyExists = 80,
} }
impl fmt::Debug for FileIoErr { impl fmt::Debug for FileIoErr {
@ -106,6 +114,7 @@ impl fmt::Debug for FileIoErr {
FileIoErr::AccessDenied => write!(f, "AccessDenied ({})", *self as i32), FileIoErr::AccessDenied => write!(f, "AccessDenied ({})", *self as i32),
#[cfg(windows)] #[cfg(windows)]
FileIoErr::AccessDenied => write!(f, "AccessDenied ({})", *self as i32), FileIoErr::AccessDenied => write!(f, "AccessDenied ({})", *self as i32),
FileIoErr::AlreadyExists => write!(f, "AlreadyExists ({})", *self as i32),
} }
} }
} }
@ -126,16 +135,16 @@ impl FileIoErr {
fn __errno_location() -> *mut FileIoErr; fn __errno_location() -> *mut FileIoErr;
} }
unsafe { errno = *__errno_location() } unsafe { *__errno_location() }
} }
#[cfg(windows)] #[cfg(windows)]
fn most_recent() -> FileIoErr { fn most_recent() -> FileIoErr {
extern "system" { extern "system" {
fn GetLastError() -> u32; fn GetLastError() -> FileIoErr;
} }
unsafe { FileIoErr(GetLastError()) } unsafe { GetLastError() }
} }
} }
@ -182,9 +191,13 @@ impl File {
#[cfg(windows)] #[cfg(windows)]
impl File { impl File {
const GENERIC_READ: u32 = 0x80000000;
const GENERIC_WRITE: u32 = 0x40000000; const GENERIC_WRITE: u32 = 0x40000000;
const FILE_SHARE_READ: u32 = 1;
const FILE_SHARE_WRITE: u32 = 2;
const CREATE_NEW: u32 = 1;
const CREATE_ALWAYS: u32 = 2; const CREATE_ALWAYS: u32 = 2;
const FILE_SHARE_WRITE: u32 = 0x00000002; const OPEN_EXISTING: u32 = 3;
pub fn open(path: &NativePath) -> Result<Self, FileIoErr> { pub fn open(path: &NativePath) -> Result<Self, FileIoErr> {
let handle = unsafe { let handle = unsafe {
@ -202,7 +215,7 @@ impl File {
if handle != -1 { if handle != -1 {
Ok(File { handle }) Ok(File { handle })
} else { } else {
Err(FileIoErr::errno()) Err(FileIoErr::most_recent())
} }
} }
@ -222,7 +235,7 @@ impl File {
if handle != -1 { if handle != -1 {
Ok(File { handle }) Ok(File { handle })
} else { } else {
Err(FileIoErr::errno()) Err(FileIoErr::most_recent())
} }
} }
} }
@ -451,7 +464,7 @@ mod tests {
use core::ffi::CStr; use core::ffi::CStr;
#[cfg(windows)] #[cfg(windows)]
use widestring::U16CString; use widestring::U16CStr;
#[cfg(unix)] #[cfg(unix)]
fn str_to_cstr(s: &str) -> &CStr { fn str_to_cstr(s: &str) -> &CStr {
@ -556,4 +569,30 @@ mod tests {
"Data read from the file does not match data written to the file" "Data read from the file does not match data written to the file"
); );
} }
#[test]
fn create_file_that_already_exists() {
let path = mock_path("roc_test_already_exists\0");
// Create the file for the first time
let file_result = File::create(path);
assert!(
file_result.is_ok(),
"Failed to create the file: roc_test_already_exists"
);
// Attempt to create the same file again
let file_result = File::create(path);
assert_eq!(
file_result.map(|_| ()),
Err(FileIoErr::AlreadyExists),
"File should already exist: roc_test_already_exists"
);
// Remove the file now that we're done with it
assert!(
File::remove(path),
"Failed to remove the file: roc_test_already_exists"
);
}
} }

View file

@ -34,8 +34,8 @@ impl<'a> From<&'a CStr> for &'a NativePath {
#[cfg(windows)] #[cfg(windows)]
impl<'a> From<&'a U16CStr> for &'a NativePath { impl<'a> From<&'a U16CStr> for &'a NativePath {
fn from(inner: &'a U16CStr) -> Self { fn from(u16_c_str: &'a U16CStr) -> Self {
// Safety: Self is repr(transparent) // Safety: Self is repr(transparent)
unsafe { mem::transmute(c_str) } unsafe { mem::transmute(u16_c_str) }
} }
} }