minspan-0.1.1/.cargo_vcs_info.json0000644000000001120000000000100124720ustar { "git": { "sha1": "77c940dae19c601e62aa8b45c54868bc4b2153ee" } } minspan-0.1.1/.gitignore000064400000000000000000000000230072674642500133030ustar 00000000000000/target Cargo.lock minspan-0.1.1/Cargo.toml0000644000000012110000000000100104710ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "minspan" version = "0.1.1" description = "a package for determining the minimum span of one vector within another" license = "MIT" [dependencies] minspan-0.1.1/Cargo.toml.orig000064400000000000000000000004300072674642500142040ustar 00000000000000[package] name = "minspan" version = "0.1.1" edition = "2018" description = "a package for determining the minimum span of one vector within another" license = "MIT" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] minspan-0.1.1/README.md000064400000000000000000000010450072674642500125770ustar 00000000000000# minspan This is a tiny crate to find the minimal bounds of a string within another one. The needle must be found in its entirety within the haystack, but there may be any number of intervening characters that are just chaff. This is useful for applications like fuzzy-completers, where a shorter match is generally preferable ("curl" matches "curl https://rust-lang.org" better than it matches "colossally urban lapidarians"). The interface is small but under flux, it's possible that a slice is a better return value than bare integer indices. minspan-0.1.1/src/lib.rs000064400000000000000000000057050072674642500132320ustar 00000000000000 // We want to make sure we are getting the shortest match possible // without getting tripped up by pathological cases. pub mod minspan { pub fn span(query: &Vec, history : &Vec) -> Option<(usize,usize)> where A: PartialEq { let mut starting_at : Vec> = query.iter().map(|_| None).collect(); let mut best_complete_solution : Option<(usize,usize)> = None; if query.len() == 0 { return Some((0,0)); } for (bodyindex, bodychr) in history.iter().enumerate() { for (keyindex, keychr) in query.iter().enumerate() { if keychr==bodychr { // we have a match, therefore record it: it ends at bodyindex, // and by construction, starts at starting_at[0] starting_at[keyindex] = if keyindex==0 { // we got nothing yet! set to beginning Some ((bodyindex,bodyindex)) } else { match starting_at[keyindex-1] { // no continuation to be had anyway, might as well break None => break, Some((start,_end)) => Some((start,bodyindex)), } }; // are we finished? if (keyindex + 1) == query.len() { best_complete_solution = match (best_complete_solution, starting_at[keyindex]) { (None, Some((from,to))) => Some((from, to)), // 1+to - from), (Some((currfrom,currto)), Some((from,to))) => Some( if to - from < currto - currfrom { (from,to) } else { (currfrom,currto) }), (_,None) => panic!("this should be impossible"), } } // this doesn't matter except as an optimisation. // debug this later. // // would be nice to have an empty range here but nah,doesn't work. // // if false { // 3keyindex > 0 { // for deletable in (0_usize..(keyindex - 1)).rev() { // min_match = min_match - 1; // match starting_at[deletable] { // Some(deletable_index) => // // if the index in the body is less than min_match, // // it's never going to be part of the smallest match, // // so reset to None // if deletable_index <= min_match { // println!("resetting {:?}, min_match = {:?}", deletable_index, min_match); // starting_at[deletable]=None // }, // None => (), // } // } // } } } } best_complete_solution } } #[cfg(test)] mod tests { use super::*; #[test] fn test_minimal_match() { let wrapper = |needle: &str,haystack: &str| { match minspan::span( &needle.chars().collect(), &haystack.chars().collect()) { Some ((from,to)) => Some(1+to-from), None => None, } }; assert_eq!(wrapper("ab", "ab").unwrap(), 2); assert_eq!(wrapper("a", "ab").unwrap(), 1); assert_eq!(wrapper("ab", "abc").unwrap(), 2); assert_eq!(wrapper("abc", "abcd").unwrap(), 3); assert_eq!(wrapper("curl", "curly").unwrap(), 4); assert_eq!(wrapper("curl", "acccccurlycurrelly").unwrap(), 4); assert_eq!(wrapper("z", "acccccurlycurrelly"), None); } }