use std::path::{Path, PathBuf};
pub(in crate::shell::acceptance) fn rust_files(root: &Path) -> Vec<PathBuf> {
let mut found = Vec::new();
let Ok(entries) = std::fs::read_dir(root) else {
return found;
};
for entry in entries.flatten() {
let path = entry.path();
if path.is_dir() {
found.extend(rust_files(&path));
} else if path.extension().is_some_and(|e| e == "rs") {
found.push(path);
}
}
found
}
pub(in crate::shell::acceptance) fn sites(
skeleton: &str,
patterns: &[&str],
) -> Vec<(usize, String)> {
let mut found = Vec::new();
for pattern in patterns {
let mut from = 0;
while let Some(hit) = skeleton.get(from..).and_then(|rest| rest.find(pattern)) {
let at = from + hit + pattern.len() - 1;
found.push((at, (*pattern).to_owned()));
from = at + 1;
}
}
found
}
pub(in crate::shell::acceptance) fn args_of(skeleton: &str, open: usize) -> &str {
balance(skeleton.as_bytes(), open)
.and_then(|end| skeleton.get(open + 1..end - 1))
.unwrap_or("")
}
pub(super) fn chain_of(skeleton: &str, open: usize) -> Vec<(String, Option<usize>)> {
let bytes = skeleton.as_bytes();
let mut names = Vec::new();
let Some(mut i) = balance(bytes, open) else {
return names;
};
loop {
while bytes.get(i) == Some(&b' ') {
i += 1;
}
if bytes.get(i) != Some(&b'.') {
return names;
}
i += 1;
let mut name = String::new();
while let Some(c) = bytes
.get(i)
.filter(|c| c.is_ascii_alphanumeric() || **c == b'_')
{
name.push(char::from(*c));
i += 1;
}
let called = (bytes.get(i) == Some(&b'(')).then_some(i);
names.push((name, called));
if let Some(at) = called {
match balance(bytes, at) {
Some(next) => i = next,
None => return names,
}
}
}
}
fn balance(bytes: &[u8], open: usize) -> Option<usize> {
let mut depth = 0usize;
for (i, c) in bytes.iter().enumerate().skip(open) {
match c {
b'(' => depth += 1,
b')' => {
depth -= 1;
if depth == 0 {
return Some(i + 1);
}
}
_ => {}
}
}
None
}