use crate::error::{VBError, VBResult};
use crate::state::file;
use crate::value::{VBLong, VBVariant};
pub fn tab(file_number: i16, column: &VBLong) -> VBResult<VBVariant> {
let target = column.as_i32();
if target < 1 {
return Err(VBError::invalid_procedure_call());
}
let current = file::get_print_column(file_number);
let target = target as usize;
let spaces = if current >= target {
file::reset_print_column(file_number);
target - 1 } else {
target - current
};
file::advance_print_column(file_number, spaces);
Ok(VBVariant::from_string(" ".repeat(spaces)))
}
pub fn tab_next_zone(file_number: i16) -> VBResult<VBVariant> {
let current = file::get_print_column(file_number);
let zone_width = file::zone_width();
let next_zone = ((current / zone_width) + 1) * zone_width;
let spaces = next_zone - current;
file::advance_print_column(file_number, spaces);
Ok(VBVariant::from_string(" ".repeat(spaces)))
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::err_number;
use crate::state::file::{self, AccessMode, LockMode, OpenMode};
fn setup_test_file(file_number: i16) -> tempfile::TempDir {
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.txt");
file::open_file(
&path,
OpenMode::Output,
AccessMode::Write,
LockMode::Shared,
0,
file_number,
)
.unwrap();
dir
}
#[test]
fn tab_at_start() {
let _dir = setup_test_file(1);
let result = tab(1, &VBLong::from(10)).unwrap();
assert_eq!(result, VBVariant::from_string(" "));
assert_eq!(file::get_print_column(1), 10);
}
#[test]
fn tab_after_content() {
let _dir = setup_test_file(1);
file::advance_print_column(1, 5); let result = tab(1, &VBLong::from(15)).unwrap();
assert_eq!(result, VBVariant::from_string(" "));
assert_eq!(file::get_print_column(1), 15);
}
#[test]
fn tab_wraps_to_new_line() {
let _dir = setup_test_file(1);
file::advance_print_column(1, 20); let result = tab(1, &VBLong::from(10)).unwrap();
assert_eq!(result, VBVariant::from_string(" "));
assert_eq!(file::get_print_column(1), 10);
}
#[test]
fn tab_rejects_zero() {
let _dir = setup_test_file(1);
assert_eq!(
tab(1, &VBLong::from(0)).unwrap_err().number,
err_number::INVALID_PROCEDURE_CALL
);
}
#[test]
fn tab_rejects_negative() {
let _dir = setup_test_file(1);
assert_eq!(
tab(1, &VBLong::from(-5)).unwrap_err().number,
err_number::INVALID_PROCEDURE_CALL
);
}
#[test]
fn tab_next_zone_from_start() {
let _dir = setup_test_file(1);
let result = tab_next_zone(1).unwrap();
assert_eq!(result, VBVariant::from_string(" ".repeat(13)));
assert_eq!(file::get_print_column(1), 14);
}
#[test]
fn tab_next_zone_from_middle() {
let _dir = setup_test_file(1);
file::advance_print_column(1, 5);
let result = tab_next_zone(1).unwrap();
assert_eq!(result, VBVariant::from_string(" ".repeat(8)));
assert_eq!(file::get_print_column(1), 14);
}
#[test]
fn conversion_error() {
let non_numeric = VBVariant::from_string("abc");
let result = VBLong::try_from(&non_numeric);
assert!(result.is_err());
assert_eq!(result.unwrap_err().number, err_number::TYPE_MISMATCH);
}
#[test]
fn tab_separate_files() {
let _guard = crate::state::test_support::lock_test();
let _ = file::close_all_files();
let dir = tempfile::tempdir().unwrap();
file::set_root(dir.path());
let path1 = std::path::PathBuf::from("test1.txt");
let path2 = std::path::PathBuf::from("test2.txt");
file::open_file(
&path1,
OpenMode::Output,
AccessMode::Write,
LockMode::Shared,
0,
1,
)
.unwrap();
file::open_file(
&path2,
OpenMode::Output,
AccessMode::Write,
LockMode::Shared,
0,
2,
)
.unwrap();
file::advance_print_column(1, 5);
file::advance_print_column(2, 10);
let _ = tab(1, &VBLong::from(20)).unwrap();
let _ = tab(2, &VBLong::from(30)).unwrap();
assert_eq!(file::get_print_column(1), 20);
assert_eq!(file::get_print_column(2), 30);
}
}