use crate::constraints::Constraint;
use crate::glob_detect::has_wildcards;
#[inline]
fn is_filename_constraint_token(token: &str) -> bool {
let bytes = token.as_bytes();
if bytes.last() == Some(&b'/') {
return false;
}
if has_wildcards(token) {
return false;
}
let filename = token.rsplit('/').next().unwrap_or(token);
match filename.rfind('.') {
Some(dot_pos) => {
let ext = &filename[dot_pos + 1..];
!ext.is_empty()
&& ext.len() <= 10
&& ext.as_bytes()[0].is_ascii_alphabetic()
&& ext.bytes().all(|b| b.is_ascii_alphanumeric())
}
None => false,
}
}
pub trait ParserConfig {
fn enable_glob(&self) -> bool {
true
}
fn enable_extension(&self) -> bool {
true
}
fn enable_exclude(&self) -> bool {
true
}
fn enable_path_segments(&self) -> bool {
true
}
fn enable_type_filter(&self) -> bool {
true
}
fn enable_git_status(&self) -> bool {
true
}
fn enable_location(&self) -> bool {
true
}
fn is_glob_pattern(&self, token: &str) -> bool {
has_wildcards(token)
}
fn parse_custom<'a>(&self, _input: &'a str) -> Option<Constraint<'a>> {
None
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct FileSearchConfig;
impl ParserConfig for FileSearchConfig {
fn parse_custom<'a>(&self, token: &'a str) -> Option<Constraint<'a>> {
if is_filename_constraint_token(token) {
Some(Constraint::FilePath(token))
} else {
None
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct GrepConfig;
impl ParserConfig for GrepConfig {
fn enable_path_segments(&self) -> bool {
true
}
fn enable_git_status(&self) -> bool {
false
}
fn enable_location(&self) -> bool {
false
}
fn is_glob_pattern(&self, token: &str) -> bool {
if !has_wildcards(token) {
return false;
}
let bytes = token.as_bytes();
if bytes.contains(&b'/') {
return true;
}
if let Some(open) = bytes.iter().position(|&b| b == b'{')
&& let Some(close) = bytes.iter().rposition(|&b| b == b'}')
{
let inner = &bytes[open + 1..close];
if inner.contains(&b',') && inner.iter().any(|b| b.is_ascii_alphabetic()) {
return true;
}
}
false
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct AiGrepConfig;
#[derive(Debug, Clone, Copy, Default)]
pub struct DirSearchConfig;
impl ParserConfig for AiGrepConfig {
fn enable_path_segments(&self) -> bool {
true
}
fn enable_git_status(&self) -> bool {
false
}
fn enable_location(&self) -> bool {
false
}
fn is_glob_pattern(&self, token: &str) -> bool {
if GrepConfig.is_glob_pattern(token) {
return true;
}
if !has_wildcards(token) {
return false;
}
let bytes = token.as_bytes();
if bytes.len() >= 3
&& bytes[0] == b'*'
&& bytes[bytes.len() - 1] == b'*'
&& bytes[1..bytes.len() - 1].iter().all(|&b| b != b'*')
{
return true;
}
false
}
fn parse_custom<'a>(&self, token: &'a str) -> Option<Constraint<'a>> {
if is_filename_constraint_token(token) {
Some(Constraint::FilePath(token))
} else {
None
}
}
}
impl ParserConfig for DirSearchConfig {
fn enable_path_segments(&self) -> bool {
false
}
fn enable_extension(&self) -> bool {
false
}
fn enable_type_filter(&self) -> bool {
false
}
fn enable_git_status(&self) -> bool {
false
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct MixedSearchConfig;
impl ParserConfig for MixedSearchConfig {
fn enable_path_segments(&self) -> bool {
false
}
}