use crate::error::{VBError, VBResult};
use crate::state::file;
use crate::value::VBVariant;
use vb6core::error::err_number;
pub fn lock_file(file_number: VBVariant, record_range: VBVariant) -> VBResult<()> {
let file_num = match file_number {
VBVariant::Integer(n) => n,
VBVariant::Long(n) => n as i16,
VBVariant::Byte(n) => n as i16,
_ => {
return Err(VBError::with_description(
err_number::TYPE_MISMATCH,
"Type mismatch in Lock statement",
));
}
};
if !(file::MIN_FILE_NUMBER..=file::MAX_FILE_NUMBER).contains(&file_num) {
return Err(VBError::with_description(
err_number::BAD_FILE_NAME_OR_NUMBER,
"Bad file name or number",
));
}
if !file::is_file_open(file_num) {
return Err(VBError::with_description(
err_number::BAD_FILE_NAME_OR_NUMBER,
"File not open",
));
}
let range = parse_record_range(&record_range)?;
file::lock_file(file_num, range).map_err(|e| {
VBError::with_description(
match e.kind() {
io::ErrorKind::PermissionDenied => err_number::FILE_ALREADY_OPEN,
io::ErrorKind::NotFound => err_number::BAD_FILE_NAME_OR_NUMBER,
_ => err_number::DEVICE_IO_ERROR,
},
e.to_string(),
)
})?;
Ok(())
}
pub(crate) fn parse_record_range(range: &VBVariant) -> VBResult<Option<(i32, i32)>> {
match range {
VBVariant::Empty => Ok(None),
VBVariant::Long(n) => {
let n = *n;
if n < 1 {
return Err(VBError::with_description(
err_number::BAD_RECORD_NUMBER,
"Bad record number",
));
}
Ok(Some((n, n)))
}
VBVariant::Integer(n) => {
let n = *n as i32;
if n < 1 {
return Err(VBError::with_description(
err_number::BAD_RECORD_NUMBER,
"Bad record number",
));
}
Ok(Some((n, n)))
}
_ => Err(VBError::with_description(
err_number::TYPE_MISMATCH,
"Type mismatch in Lock statement",
)),
}
}
use std::io;
#[cfg(test)]
mod tests {
use super::*;
use crate::state::file::{self};
use vb6core::error::err_number;
#[test]
fn lock_entire_file() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.txt");
std::fs::write(&path, "Hello").unwrap();
file::open_file(
&path,
file::OpenMode::Random,
file::AccessMode::ReadWrite,
file::LockMode::Shared,
1,
1,
)
.unwrap();
lock_file(VBVariant::Integer(1), VBVariant::Empty).unwrap();
let _ = file::unlock_file(1, None);
let _ = file::close_all_files();
}
#[test]
fn lock_record_range() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.txt");
std::fs::write(&path, "Hello").unwrap();
file::open_file(
&path,
file::OpenMode::Random,
file::AccessMode::ReadWrite,
file::LockMode::Shared,
1,
1,
)
.unwrap();
lock_file(VBVariant::Integer(1), VBVariant::Long(5)).unwrap();
let _ = file::unlock_file(1, Some((5, 5)));
let _ = file::close_all_files();
}
#[test]
fn lock_rejects_bad_file_number() {
let _guard = crate::state::test_support::lock_test();
let result = lock_file(VBVariant::Integer(999), VBVariant::Empty);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().number,
err_number::BAD_FILE_NAME_OR_NUMBER
);
}
#[test]
fn lock_rejects_non_numeric() {
let _guard = crate::state::test_support::lock_test();
let result = lock_file(VBVariant::from_string("abc"), VBVariant::Empty);
assert!(result.is_err());
assert_eq!(result.unwrap_err().number, err_number::TYPE_MISMATCH);
}
}