use std::ops::Deref; #[allow(unused)] pub enum SplitArg<'a> { Normal(&'a str), Once(&'a str), } impl<'a> SplitArg<'a> { fn once(&self) -> bool { if let Self::Once(_) = self { true } else { false } } } impl<'a> Deref for SplitArg<'a> { type Target = str; fn deref(&self) -> &Self::Target { match self { SplitArg::Normal(s) | SplitArg::Once(s) => s, } } } impl<'a> From<&'a str> for SplitArg<'a> { fn from(value: &'a str) -> Self { SplitArg::Normal(value) } } impl<'a> From<&'a String> for SplitArg<'a> { fn from(value: &'a String) -> Self { SplitArg::Normal(value) } } /// Assuming the input string is in format `({type_name}){path}`, /// it will return `Some(("{type_name}", "{path}"))` pub fn parens(s: &str) -> Option<(String, String)> { let mut chars = s.chars(); match chars.next() { Some(c) => { if c != '(' { return None; } } None => return None, } let mut working = Vec::with_capacity(s.len()); let type_name = loop { if let Some(next) = chars.next() { if next == ')' { break working.into_iter().collect::(); } if !next.is_alphanumeric() { return None; } working.push(next); } else { return None; } }; Some((type_name, chars.collect())) } pub fn tab_or_space<'a, S>(s: S) -> Vec where S: Into>, { let value = s.into(); let mut chars = value.chars(); let mut working: Vec = Vec::with_capacity(value.len()); let mut out: Vec = Vec::new(); let mut match_quote = false; while let Some(c) = chars.next() { match c { '\\' => { working.push(c); if let Some(c) = chars.next() { working.push(c); } } '"' => { match_quote = !match_quote; working.push(c); } '\t' | ' ' => { if match_quote { working.push(c); } else if working.is_empty() { continue; } else { out.push((&working).into_iter().collect()); working.clear(); if value.once() { out.push(chars.collect()); break; } } } _ => working.push(c), } } if !working.is_empty() { out.push(working.into_iter().collect()); } out } pub fn on_first_match(s: &str, match_set: &[char]) -> Option<((String, String), char)> { let mut first: Vec = Vec::with_capacity(s.len()); let mut chars = s.chars(); while let Some(char) = chars.next() { if (&match_set).into_iter().any(|m| m.eq(&char)) { return Some(((first.into_iter().collect(), chars.collect()), char)); } first.push(char); } None } #[cfg(test)] mod test { #[test] fn test_tab_or_space_split() { for (base, expected) in [ ("hello world", vec!["hello", "world"]), ("hello\tworld", vec!["hello", "world"]), ( r#"hello "world! I never made it!" up til now"#, vec!["hello", "\"world! I never made it!\"", "up", "til", "now"], ), ("hello world", vec!["hello", "world"]), ] { assert_eq!(expected, super::tab_or_space(base)) } } }