diff --git a/src/core/utils/string.rs b/src/core/utils/string.rs index 1a5b6b72..1f2a6572 100644 --- a/src/core/utils/string.rs +++ b/src/core/utils/string.rs @@ -2,6 +2,25 @@ use crate::Result; pub const EMPTY: &str = ""; +/// Find the common prefix from a collection of strings and return a slice +/// ``` +/// use conduit_core::utils::string::common_prefix; +/// let input = ["conduwuit", "conduit", "construct"]; +/// common_prefix(&input) == "con"; +/// ``` +#[must_use] +pub fn common_prefix<'a>(choice: &'a [&str]) -> &'a str { + choice.first().map_or(EMPTY, move |best| { + choice.iter().skip(1).fold(*best, |best, choice| { + &best[0..choice + .chars() + .zip(best.chars()) + .take_while(|&(a, b)| a == b) + .count()] + }) + }) +} + #[inline] #[must_use] pub fn split_once_infallible<'a>(input: &'a str, delim: &'_ str) -> (&'a str, &'a str) { diff --git a/src/core/utils/tests.rs b/src/core/utils/tests.rs index b226bd41..f5cd0a07 100644 --- a/src/core/utils/tests.rs +++ b/src/core/utils/tests.rs @@ -35,3 +35,30 @@ fn increment_wrap() { let res = u64::from_be_bytes(bytes); assert_eq!(res, 0); } + +#[test] +fn common_prefix() { + use utils::string; + + let input = ["conduwuit", "conduit", "construct"]; + let output = string::common_prefix(&input); + assert_eq!(output, "con"); +} + +#[test] +fn common_prefix_empty() { + use utils::string; + + let input = ["abcdefg", "hijklmn", "opqrstu"]; + let output = string::common_prefix(&input); + assert_eq!(output, ""); +} + +#[test] +fn common_prefix_none() { + use utils::string; + + let input = []; + let output = string::common_prefix(&input); + assert_eq!(output, ""); +}