use super::Found;
use super::position::PositionIndex;
pub(crate) fn locate(text: &str, values: Vec<String>) -> Vec<Found> {
let index = PositionIndex::new(text);
let mut cursor = 0;
values
.into_iter()
.map(|value| {
let position = text
.get(cursor..)
.and_then(|rest| rest.find(&value))
.map(|offset| {
let start = cursor + offset;
cursor = start + value.len();
index.at(start)
});
Found { value, position }
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
fn values(items: &[&str]) -> Vec<String> {
items.iter().map(|item| (*item).to_string()).collect()
}
#[test]
fn a_value_is_found_where_it_appears() {
let found = locate("key = \"hello\"\n", values(&["hello"]));
let position = found[0].position.expect("a position");
assert_eq!((position.line, position.column), (1, 8));
}
#[test]
fn repeated_values_take_successive_occurrences() {
let text = "a: same\nb: other\nc: same\n";
let found = locate(text, values(&["same", "other", "same"]));
let lines: Vec<usize> = found
.iter()
.map(|item| item.position.expect("a position").line)
.collect();
assert_eq!(lines, [1, 2, 3]);
}
#[test]
fn a_value_the_source_does_not_spell_gets_no_position() {
let found = locate(r#"{"a":"first\nsecond"}"#, values(&["first\nsecond"]));
assert_eq!(found[0].value, "first\nsecond");
assert!(found[0].position.is_none());
}
#[test]
fn a_miss_does_not_move_the_cursor() {
let text = "one\nthree\n";
let found = locate(text, values(&["one", "two", "three"]));
assert_eq!(found[0].position.expect("a position").line, 1);
assert!(found[1].position.is_none());
assert_eq!(found[2].position.expect("a position").line, 2);
}
#[test]
fn the_search_never_goes_backwards() {
let text = "early\nlate\n";
let found = locate(text, values(&["late", "early"]));
assert_eq!(found[0].position.expect("a position").line, 2);
assert!(found[1].position.is_none());
}
#[test]
fn a_column_after_a_multibyte_character_is_counted_in_utf16() {
let found = locate("café = \"x\"\n", values(&["x"]));
assert_eq!(found[0].position.expect("a position").column, 9);
}
#[test]
fn nothing_to_locate_is_nothing_returned() {
assert!(locate("anything", Vec::new()).is_empty());
}
#[test]
fn an_offset_of_zero_is_a_position_like_any_other() {
let found = locate("hello world", values(&["hello"]));
let position = found[0].position.expect("a position");
assert_eq!((position.line, position.column), (1, 1));
}
}