use crate::error::{VBError, VBResult};
use crate::state::file;
use crate::state::file::{MAX_FILE_NUMBER, MIN_FILE_NUMBER};
use crate::value::VBVariant;
pub fn input_statement(file_number: i16) -> VBResult<Vec<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 mut values = Vec::new();
let mut in_quotes = false;
let mut current_value = String::new();
loop {
let mut buf = [0u8; 1];
let bytes_read = file::read_file(file_number, &mut buf)?;
if bytes_read == 0 {
if !current_value.is_empty() || in_quotes {
values.push(parse_value(¤t_value));
}
break;
}
let ch = buf[0] as char;
if ch == '"' {
in_quotes = !in_quotes;
} else if ch == ',' && !in_quotes {
values.push(parse_value(¤t_value));
current_value.clear();
} else if ch == '\n' && !in_quotes {
let trimmed = current_value.trim_end_matches('\r');
if !trimmed.is_empty() {
values.push(parse_value(trimmed));
}
break;
} else {
current_value.push(ch);
}
}
Ok(values)
}
fn parse_value(s: &str) -> VBVariant {
let trimmed = s.trim();
if trimmed.is_empty() {
return VBVariant::Empty;
}
if let Ok(long_val) = trimmed.parse::<i32>() {
return VBVariant::Long(long_val);
}
if let Ok(double_val) = trimmed.parse::<f64>() {
return VBVariant::Double(double_val);
}
match trimmed.to_uppercase().as_str() {
"TRUE" => return VBVariant::Boolean(true),
"FALSE" => return VBVariant::Boolean(false),
_ => {}
}
VBVariant::from_string(trimmed)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::state::file::{self, AccessMode, LockMode, OpenMode};
use std::io::Write;
use vb6core::error::err_number;
#[test]
fn input_reads_comma_separated_values() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let dir = tempfile::tempdir().unwrap();
file::set_root(dir.path());
let mut test_file = std::fs::File::create(dir.path().join("test.txt")).unwrap();
writeln!(test_file, "Hello,42,3.141592653589793").unwrap();
drop(test_file);
let path = std::path::PathBuf::from("test.txt");
file::open_file(
&path,
OpenMode::Input,
AccessMode::Read,
LockMode::Shared,
0,
1,
)
.unwrap();
let values = input_statement(1).unwrap();
assert_eq!(values.len(), 3);
assert_eq!(values[0], VBVariant::from_string("Hello"));
assert_eq!(values[1], VBVariant::Long(42));
assert_eq!(values[2], VBVariant::Double(std::f64::consts::PI));
let _ = file::close_all_files();
}
#[test]
fn input_handles_quoted_strings() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let dir = tempfile::tempdir().unwrap();
file::set_root(dir.path());
let mut test_file = std::fs::File::create(dir.path().join("test.txt")).unwrap();
writeln!(test_file, "\"Hello, World\",42").unwrap();
drop(test_file);
let path = std::path::PathBuf::from("test.txt");
file::open_file(
&path,
OpenMode::Input,
AccessMode::Read,
LockMode::Shared,
0,
1,
)
.unwrap();
let values = input_statement(1).unwrap();
assert_eq!(values.len(), 2);
assert_eq!(values[0], VBVariant::from_string("Hello, World"));
assert_eq!(values[1], VBVariant::Long(42));
let _ = file::close_all_files();
}
#[test]
fn input_rejects_invalid_file_number() {
let _guard = crate::state::test_support::lock_test();
let result = input_statement(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 input_rejects_closed_file() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let result = input_statement(1);
assert!(result.is_err());
assert_eq!(
result.unwrap_err().number,
err_number::BAD_FILE_NAME_OR_NUMBER
);
let _ = file::close_all_files();
}
}