use vb6core::error::{err_number, VBError, VBResult};
use crate::value::{VBLong, VBString};
const BYTES_PER_CHAR: i32 = 2;
pub fn midb_statement(
stringvar: &VBString,
start: &VBLong,
length: Option<&VBLong>,
string: &VBString,
) -> VBResult<VBString> {
let start = start.as_i32();
if start < 1 {
return Err(VBError::with_description(
err_number::INVALID_PROCEDURE_CALL,
"Invalid start position",
));
}
let max_replace = match length {
Some(n) => {
let n = n.as_i32();
if n < 0 {
return Err(VBError::with_description(
err_number::INVALID_PROCEDURE_CALL,
"Invalid length",
));
}
(n / BYTES_PER_CHAR) as usize
}
None => usize::MAX,
};
let mut target: Vec<char> = stringvar.as_str().chars().collect();
let offset = ((start - 1) / BYTES_PER_CHAR) as usize;
if offset >= target.len() {
return Ok(stringvar.clone());
}
let replacement: Vec<char> = string.as_str().chars().collect();
let take = max_replace
.min(replacement.len())
.min(target.len() - offset);
target.splice(offset..offset + take, replacement[..take].iter().copied());
Ok(VBString::from(target.into_iter().collect::<String>()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn replaces_bytes_with_explicit_length() {
assert_eq!(
midb_statement(
&VBString::from("ABCDEFGH"),
&VBLong::from(3),
Some(&VBLong::from(4)),
&VBString::from("12")
)
.unwrap(),
VBString::from("A12DEFGH")
);
}
#[test]
fn omitted_length_replaces_to_the_end() {
assert_eq!(
midb_statement(
&VBString::from("ABCDEFGH"),
&VBLong::from(3),
None,
&VBString::from("1234")
)
.unwrap(),
VBString::from("A1234FGH")
);
}
#[test]
fn truncates_replacement_longer_than_the_byte_length() {
assert_eq!(
midb_statement(
&VBString::from("ABCDEFGH"),
&VBLong::from(3),
Some(&VBLong::from(6)),
&VBString::from("12345")
)
.unwrap(),
VBString::from("A123EFGH")
);
}
#[test]
fn shorter_replacement_keeps_the_tail() {
assert_eq!(
midb_statement(
&VBString::from("Test"),
&VBLong::from(1),
Some(&VBLong::from(2)),
&VBString::from("X")
)
.unwrap(),
VBString::from("Xest")
);
}
#[test]
fn start_beyond_the_length_is_a_noop() {
assert_eq!(
midb_statement(
&VBString::from("abc"),
&VBLong::from(99),
None,
&VBString::from("xyz")
)
.unwrap(),
VBString::from("abc")
);
}
#[test]
fn rejects_invalid_start_and_length() {
assert_eq!(
midb_statement(
&VBString::from("abc"),
&VBLong::from(0),
None,
&VBString::from("x")
)
.unwrap_err()
.number,
err_number::INVALID_PROCEDURE_CALL
);
assert_eq!(
midb_statement(
&VBString::from("abc"),
&VBLong::from(1),
Some(&VBLong::from(-2)),
&VBString::from("x")
)
.unwrap_err()
.number,
err_number::INVALID_PROCEDURE_CALL
);
}
}