hlctl/src/split.rs

146 lines
3.6 KiB
Rust

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::<String>();
}
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<String>
where
S: Into<SplitArg<'a>>,
{
let value = s.into();
let mut chars = value.chars();
let mut working: Vec<char> = Vec::with_capacity(value.len());
let mut out: Vec<String> = 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<char> = 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))
}
}
}