use crate::error::{VBError, VBResult};
use crate::state::file;
use crate::state::file::{MAX_FILE_NUMBER, MIN_FILE_NUMBER};
use crate::value::VBVariant;
use vb6core::error::err_number;
pub fn get_statement(
file_number: i16,
record_number: Option<i64>,
varname: VBVariant,
) -> VBResult<VBVariant> {
if !(MIN_FILE_NUMBER..=MAX_FILE_NUMBER).contains(&file_number) {
return Err(VBError::with_description(
52, format!("Bad file name or number: {}", file_number),
));
}
if !file::is_file_open(file_number) {
return Err(VBError::with_description(
52, format!("File not open: #{}", file_number),
));
}
let file = file::get_file(file_number).ok_or_else(|| {
VBError::with_description(err_number::BAD_FILE_NAME_OR_NUMBER, "File not open")
})?;
let position = if let Some(rec) = record_number {
if rec <= 0 {
return Err(VBError::with_description(
63, "Bad record number",
));
}
rec
} else {
file.position + 1
};
let bytes_to_read = match &varname {
VBVariant::Long(_) => 4,
VBVariant::Integer(_) => 2,
VBVariant::Byte(_) => 1,
VBVariant::Double(_) => 8,
VBVariant::Single(_) => 4,
VBVariant::Currency(_) => 8,
VBVariant::Boolean(_) => 2,
VBVariant::Date(_) => 8,
VBVariant::String(_s) => {
let mut len_buf = [0u8; 2];
file::seek_file(file_number, position)?;
file::read_file(file_number, &mut len_buf)?;
let str_len = u16::from_le_bytes(len_buf) as usize;
let mut str_buf = vec![0u8; str_len];
file::read_file(file_number, &mut str_buf)?;
let s = String::from_utf8_lossy(&str_buf).to_string();
return Ok(VBVariant::from_string(s));
}
_ => {
return Err(VBError::with_description(
13, "Type mismatch in Get statement",
));
}
};
let mut buf = vec![0u8; bytes_to_read];
file::seek_file(file_number, position)?;
let bytes_read = file::read_file(file_number, &mut buf)?;
if bytes_read < bytes_to_read {
return Err(VBError::with_description(
62, "Input past end of file",
));
}
let result = match &varname {
VBVariant::Long(_) => {
let val = i32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
VBVariant::Long(val)
}
VBVariant::Integer(_) => {
let val = i16::from_le_bytes([buf[0], buf[1]]);
VBVariant::Integer(val)
}
VBVariant::Byte(_) => VBVariant::Byte(buf[0]),
VBVariant::Double(_) => {
let bits = u64::from_le_bytes([
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
]);
VBVariant::Double(f64::from_bits(bits))
}
VBVariant::Single(_) => {
let bits = u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]);
VBVariant::Single(f32::from_bits(bits))
}
VBVariant::Currency(_) => {
let val = i64::from_le_bytes([
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
]);
VBVariant::Currency(val)
}
VBVariant::Boolean(_) => {
let val = i16::from_le_bytes([buf[0], buf[1]]);
VBVariant::Boolean(val != 0)
}
VBVariant::Date(_) => {
let bits = u64::from_le_bytes([
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
]);
VBVariant::Date(f64::from_bits(bits))
}
_ => unreachable!(),
};
Ok(result)
}
pub fn get_record_length(file_number: i16) -> VBResult<i32> {
let file = file::get_file(file_number).ok_or_else(|| {
VBError::with_description(err_number::BAD_FILE_NAME_OR_NUMBER, "File not open")
})?;
Ok(file.record_length)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::file::{self, AccessMode, LockMode, OpenMode};
use vb6core::error::err_number;
#[test]
fn get_long_from_binary_file() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let dir = tempfile::tempdir().unwrap();
file::set_root(dir.path());
let path = std::path::PathBuf::from("test.bin");
file::open_file(
&path,
OpenMode::Binary,
AccessMode::ReadWrite,
LockMode::Shared,
0,
1,
)
.unwrap();
let data = 42i32.to_le_bytes();
file::write_file(1, &data).unwrap();
let result = get_statement(1, Some(1), VBVariant::Long(0)).unwrap();
assert_eq!(result, VBVariant::Long(42));
let _ = file::close_all_files();
}
#[test]
fn get_string_from_binary_file() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let dir = tempfile::tempdir().unwrap();
file::set_root(dir.path());
let path = std::path::PathBuf::from("test.bin");
file::open_file(
&path,
OpenMode::Binary,
AccessMode::ReadWrite,
LockMode::Shared,
0,
1,
)
.unwrap();
let s = "Hello";
let len = (s.len() as u16).to_le_bytes();
file::write_file(1, &len).unwrap();
file::write_file(1, s.as_bytes()).unwrap();
let result = get_statement(1, Some(1), VBVariant::from_string("")).unwrap();
assert_eq!(result, VBVariant::from_string("Hello"));
let _ = file::close_all_files();
}
#[test]
fn get_rejects_invalid_file_number() {
let _guard = crate::state::test_support::lock_test();
let result = get_statement(0, Some(1), VBVariant::Long(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 get_rejects_closed_file() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let result = get_statement(1, Some(1), VBVariant::Long(0));
assert!(result.is_err());
assert_eq!(
result.unwrap_err().number,
err_number::BAD_FILE_NAME_OR_NUMBER
);
let _ = file::close_all_files();
}
}