use std::io;
use std::path::Path;
use vb6core::error::err_number;
use crate::error::{VBError, VBResult};
use crate::state::file;
use crate::state::file::{
backend::{AccessMode, LockMode, OpenMode},
MAX_FILE_NUMBER, MIN_FILE_NUMBER,
};
use crate::value::VBVariant;
pub fn open_file(
pathname: &VBVariant,
mode: OpenMode,
access: AccessMode,
lock: LockMode,
filenumber: i16,
record_length: i32,
) -> VBResult<()> {
let path_str = pathname.as_string().map_err(|_| {
VBError::with_description(
13, "Type mismatch in Open statement",
)
})?;
if !(MIN_FILE_NUMBER..=MAX_FILE_NUMBER).contains(&filenumber) {
return Err(VBError::with_description(
52, "Bad file name or number",
));
}
if file::is_file_open(filenumber) {
return Err(VBError::with_description(
55, format!("File already open: #{}", filenumber),
));
}
if !(file::MIN_RECORD_NUMBER..=file::MAX_RECORD_NUMBER).contains(&record_length) {
return Err(VBError::with_description(
59, "Bad record length",
));
}
let path = Path::new(&path_str);
if mode == OpenMode::Input && !file::file_exists(path) {
return Err(VBError::with_description(
53, format!("File not found: {}", path_str),
));
}
file::open_file(path, mode, access, lock, record_length, filenumber).map_err(|e| {
VBError::with_description(
match e.kind() {
io::ErrorKind::NotFound => err_number::FILE_NOT_FOUND, io::ErrorKind::PermissionDenied => err_number::PERMISSION_DENIED, io::ErrorKind::AlreadyExists => err_number::FILE_ALREADY_OPEN, _ => err_number::DEVICE_IO_ERROR, },
e.to_string(),
)
})?;
Ok(())
}
pub fn parse_mode(mode: &str) -> VBResult<OpenMode> {
match mode.to_uppercase().as_str() {
"INPUT" => Ok(OpenMode::Input),
"OUTPUT" => Ok(OpenMode::Output),
"APPEND" => Ok(OpenMode::Append),
"RANDOM" => Ok(OpenMode::Random),
"BINARY" => Ok(OpenMode::Binary),
"" => Ok(OpenMode::Random), _ => Err(VBError::with_description(
54, format!("Bad file mode: {}", mode),
)),
}
}
pub fn parse_access(access: &str) -> VBResult<AccessMode> {
match access.to_uppercase().as_str() {
"READ" => Ok(AccessMode::Read),
"WRITE" => Ok(AccessMode::Write),
"READ WRITE" => Ok(AccessMode::ReadWrite),
"READWRITE" => Ok(AccessMode::ReadWrite),
"" => Ok(AccessMode::ReadWrite), _ => Err(VBError::with_description(
54, format!("Bad access mode: {}", access),
)),
}
}
pub fn parse_lock(lock: &str) -> VBResult<LockMode> {
match lock.to_uppercase().as_str() {
"SHARED" => Ok(LockMode::Shared),
"LOCK READ" => Ok(LockMode::LockRead),
"LOCKWRITE" => Ok(LockMode::LockWrite),
"LOCK WRITE" => Ok(LockMode::LockWrite),
"LOCK READ WRITE" => Ok(LockMode::LockReadWrite),
"LOCKREADWRITE" => Ok(LockMode::LockReadWrite),
"" => Ok(LockMode::Shared), _ => Err(VBError::with_description(
54, format!("Bad lock mode: {}", lock),
)),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::file;
use vb6core::error::err_number;
#[test]
fn open_and_close_output_file() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let dir = tempfile::tempdir().unwrap();
crate::state::file::set_root(dir.path());
let path = VBVariant::from_string("test.txt");
open_file(
&path,
OpenMode::Output,
AccessMode::Write,
LockMode::Shared,
1,
0,
)
.unwrap();
assert!(file::is_file_open(1));
file::close_file(1).unwrap();
assert!(!file::is_file_open(1));
let _ = file::close_all_files();
}
#[test]
fn open_rejects_invalid_file_number() {
let _guard = crate::state::test_support::lock_test();
let path = VBVariant::from_string("test.txt");
let result = open_file(
&path,
OpenMode::Output,
AccessMode::Write,
LockMode::Shared,
0, 0,
);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().number,
err_number::BAD_FILE_NAME_OR_NUMBER
);
let result = open_file(
&path,
OpenMode::Output,
AccessMode::Write,
LockMode::Shared,
512, 0,
);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().number,
err_number::BAD_FILE_NAME_OR_NUMBER
);
let _ = file::close_all_files();
}
#[test]
fn open_rejects_file_already_open() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let dir = tempfile::tempdir().unwrap();
crate::state::file::set_root(dir.path());
let path = VBVariant::from_string("test.txt");
open_file(
&path,
OpenMode::Output,
AccessMode::Write,
LockMode::Shared,
1,
0,
)
.unwrap();
let result = open_file(
&path,
OpenMode::Output,
AccessMode::Write,
LockMode::Shared,
1, 0,
);
assert!(result.is_err());
assert_eq!(result.unwrap_err().number, err_number::FILE_ALREADY_OPEN);
let _ = file::close_all_files();
}
#[test]
fn open_rejects_invalid_record_length() {
let _guard = crate::state::test_support::lock_test();
let path = VBVariant::from_string("test.txt");
let result = open_file(
&path,
OpenMode::Random,
AccessMode::ReadWrite,
LockMode::Shared,
1,
-1, );
assert!(result.is_err());
assert_eq!(result.unwrap_err().number, err_number::BAD_RECORD_LENGTH);
let result = open_file(
&path,
OpenMode::Random,
AccessMode::ReadWrite,
LockMode::Shared,
1,
32768, );
assert!(result.is_err());
assert_eq!(result.unwrap_err().number, err_number::BAD_RECORD_LENGTH);
let _ = file::close_all_files();
}
#[test]
fn open_input_rejects_nonexistent_file() {
let _guard = crate::state::test_support::lock_test();
let dir = tempfile::tempdir().unwrap();
crate::state::file::set_root(dir.path());
let path = VBVariant::from_string("nonexistent.txt");
let result = open_file(
&path,
OpenMode::Input,
AccessMode::Read,
LockMode::Shared,
1,
0,
);
assert!(result.is_err());
assert_eq!(result.unwrap_err().number, err_number::FILE_NOT_FOUND);
let _ = file::close_all_files();
}
#[test]
fn parse_mode_works() {
assert_eq!(parse_mode("INPUT").unwrap(), OpenMode::Input);
assert_eq!(parse_mode("OUTPUT").unwrap(), OpenMode::Output);
assert_eq!(parse_mode("APPEND").unwrap(), OpenMode::Append);
assert_eq!(parse_mode("RANDOM").unwrap(), OpenMode::Random);
assert_eq!(parse_mode("BINARY").unwrap(), OpenMode::Binary);
assert_eq!(parse_mode("").unwrap(), OpenMode::Random);
assert!(parse_mode("INVALID").is_err());
}
#[test]
fn parse_access_works() {
assert_eq!(parse_access("READ").unwrap(), AccessMode::Read);
assert_eq!(parse_access("WRITE").unwrap(), AccessMode::Write);
assert_eq!(parse_access("READ WRITE").unwrap(), AccessMode::ReadWrite);
assert_eq!(parse_access("").unwrap(), AccessMode::ReadWrite);
assert!(parse_access("INVALID").is_err());
}
#[test]
fn parse_lock_works() {
assert_eq!(parse_lock("SHARED").unwrap(), LockMode::Shared);
assert_eq!(parse_lock("LOCK READ").unwrap(), LockMode::LockRead);
assert_eq!(parse_lock("LOCK WRITE").unwrap(), LockMode::LockWrite);
assert_eq!(
parse_lock("LOCK READ WRITE").unwrap(),
LockMode::LockReadWrite
);
assert_eq!(parse_lock("").unwrap(), LockMode::Shared);
assert!(parse_lock("INVALID").is_err());
}
}