pub use magellan::common::extract_context_safe;
pub use magellan::common::extract_symbol_content_safe;
pub use magellan::common::safe_str_slice;
use crate::error::LlmError;
pub fn safe_extract_snippet(source: &[u8], start: usize, end: usize) -> Result<String, LlmError> {
extract_symbol_content_safe(source, start, end).ok_or_else(|| {
LlmError::SearchFailed {
reason: format!(
"Failed to extract snippet at byte range {}..{} (invalid UTF-8 boundary or out of bounds)",
start, end
),
}
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_safe_extract_snippet_ascii() {
let source = b"fn test() { return 42; }";
let result = safe_extract_snippet(source, 0, 15);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "fn test() { ret");
}
#[test]
fn test_safe_extract_snippet_accented_latin() {
let source = "fn café() { return 42; }";
let bytes = source.as_bytes();
let result = safe_extract_snippet(bytes, 3, 8);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "café");
}
#[test]
fn test_safe_extract_snippet_emoji() {
let source = "fn test() { // 🎉 }";
let bytes = source.as_bytes();
let result = safe_extract_snippet(bytes, 0, 19);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "fn test() { // 🎉");
}
#[test]
fn test_safe_extract_snippet_cjk() {
let source = "fn test() { // 中文 }";
let bytes = source.as_bytes();
let result = safe_extract_snippet(bytes, 0, 18);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "fn test() { // 中");
}
#[test]
fn test_safe_extract_snippet_boundary_splitting() {
let source = "fn café()";
let bytes = source.as_bytes();
let result = safe_extract_snippet(bytes, 0, 6);
assert!(result.is_ok());
assert_eq!(result.unwrap(), "fn caf");
}
#[test]
fn test_safe_extract_snippet_out_of_bounds() {
let source = b"short";
let result = safe_extract_snippet(source, 0, 100);
assert!(result.is_err());
}
#[test]
fn test_extract_symbol_content_safe_direct() {
let source = "fn test() { return 42; }";
let bytes = source.as_bytes();
let result = extract_symbol_content_safe(bytes, 0, 11);
assert!(result.is_some());
assert_eq!(result.unwrap(), "fn test() {");
}
#[test]
fn test_safe_str_slice() {
let source = "fn test() { return 42; }";
let result = safe_str_slice(source, 0, 11);
assert_eq!(result, Some("fn test() {"));
}
#[test]
fn test_safe_str_slice_multi_byte() {
let source = "café";
let result = safe_str_slice(source, 0, 3);
assert_eq!(result, Some("caf"));
}
#[test]
fn test_safe_str_slice_invalid_boundary() {
let source = "café"; let result = safe_str_slice(source, 0, 4);
assert_eq!(result, None);
}
#[test]
fn test_extract_symbol_content_safe_adjusts_end_boundary() {
let source = "café";
let bytes = source.as_bytes();
let result = extract_symbol_content_safe(bytes, 0, 4);
assert_eq!(result, Some("caf".to_string()));
}
#[test]
fn test_extract_symbol_content_safe_start_at_boundary() {
let source = "a🎉b"; let bytes = source.as_bytes();
let result = extract_symbol_content_safe(bytes, 1, 5);
assert_eq!(result, Some("🎉".to_string()));
}
#[test]
fn test_extract_symbol_content_safe_start_splits_char_returns_none() {
let source = "a🎉b"; let bytes = source.as_bytes();
let result = extract_symbol_content_safe(bytes, 2, 5);
assert_eq!(result, None);
}
}