use std::path::PathBuf;
use std::time::Duration;
use crate::contract::Match;
use crate::runtime::Result;
use crate::runtime::shell::{self as engine, DEFAULT_TIMEOUT};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum SearchEngine {
#[default]
Linear,
Auto,
Pcre2,
}
#[derive(Debug, Clone)]
pub struct SearchRequest {
pub pattern: String,
pub paths: Vec<String>,
pub fixed: bool,
pub ignore_case: bool,
pub smart_case: bool,
pub word: bool,
pub quiet: bool,
pub invert: bool,
pub globs: Vec<String>,
pub iglobs: Vec<String>,
pub types: Vec<String>,
pub not_types: Vec<String>,
pub before: u32,
pub after: u32,
pub context: u32,
pub max_count: u32,
pub max_depth: u32,
pub hidden: bool,
pub no_ignore: bool,
pub follow: bool,
pub no_index: bool,
pub engine: SearchEngine,
pub multiline: bool,
pub multiline_dotall: bool,
pub unicode: Option<bool>,
pub extra_flags: Vec<String>,
pub cwd: Option<PathBuf>,
pub timeout: Duration,
}
impl SearchRequest {
#[must_use]
pub fn new(pattern: impl Into<String>) -> Self {
Self {
pattern: pattern.into(),
paths: Vec::new(),
fixed: false,
ignore_case: false,
smart_case: false,
word: false,
quiet: false,
invert: false,
globs: Vec::new(),
iglobs: Vec::new(),
types: Vec::new(),
not_types: Vec::new(),
before: 0,
after: 0,
context: 0,
max_count: 0,
max_depth: 0,
hidden: false,
no_ignore: false,
follow: false,
no_index: false,
engine: SearchEngine::Linear,
multiline: false,
multiline_dotall: false,
unicode: None,
extra_flags: Vec::new(),
cwd: None,
timeout: DEFAULT_TIMEOUT,
}
}
#[must_use]
pub fn path(mut self, p: impl Into<String>) -> Self {
self.paths.push(p.into());
self
}
#[must_use]
pub fn paths<I, S>(mut self, it: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.paths.extend(it.into_iter().map(Into::into));
self
}
#[must_use]
pub fn glob(mut self, g: impl Into<String>) -> Self {
self.globs.push(g.into());
self
}
#[must_use]
pub fn globs<I, S>(mut self, it: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.globs.extend(it.into_iter().map(Into::into));
self
}
#[must_use]
pub fn type_(mut self, t: impl Into<String>) -> Self {
self.types.push(t.into());
self
}
#[must_use]
pub fn not_type(mut self, t: impl Into<String>) -> Self {
self.not_types.push(t.into());
self
}
#[must_use]
pub fn iglob(mut self, g: impl Into<String>) -> Self {
self.iglobs.push(g.into());
self
}
#[must_use]
pub fn fixed(mut self) -> Self {
self.fixed = true;
self
}
#[must_use]
pub fn ignore_case(mut self) -> Self {
self.ignore_case = true;
self
}
#[must_use]
pub fn smart_case(mut self) -> Self {
self.smart_case = true;
self
}
#[must_use]
pub fn word(mut self) -> Self {
self.word = true;
self
}
#[must_use]
pub fn quiet(mut self) -> Self {
self.quiet = true;
self
}
#[must_use]
pub fn invert(mut self) -> Self {
self.invert = true;
self
}
#[must_use]
pub fn hidden(mut self) -> Self {
self.hidden = true;
self
}
#[must_use]
pub fn no_ignore(mut self) -> Self {
self.no_ignore = true;
self
}
#[must_use]
pub fn follow(mut self) -> Self {
self.follow = true;
self
}
#[must_use]
pub fn no_index(mut self) -> Self {
self.no_index = true;
self
}
#[must_use]
pub fn engine(mut self, e: SearchEngine) -> Self {
self.engine = e;
self
}
#[must_use]
pub fn multiline(mut self) -> Self {
self.multiline = true;
self
}
#[must_use]
pub fn multiline_dotall(mut self) -> Self {
self.multiline_dotall = true;
self
}
#[must_use]
pub fn unicode(mut self, on: bool) -> Self {
self.unicode = Some(on);
self
}
#[must_use]
pub fn before(mut self, n: u32) -> Self {
self.before = n;
self
}
#[must_use]
pub fn after(mut self, n: u32) -> Self {
self.after = n;
self
}
#[must_use]
pub fn context(mut self, n: u32) -> Self {
self.context = n;
self
}
#[must_use]
pub fn max_count(mut self, n: u32) -> Self {
self.max_count = n;
self
}
#[must_use]
pub fn max_depth(mut self, n: u32) -> Self {
self.max_depth = n;
self
}
#[must_use]
pub fn flag(mut self, f: impl Into<String>) -> Self {
self.extra_flags.push(f.into());
self
}
#[must_use]
pub fn cwd(mut self, dir: impl Into<PathBuf>) -> Self {
self.cwd = Some(dir.into());
self
}
#[must_use]
pub fn timeout(mut self, d: Duration) -> Self {
self.timeout = d;
self
}
#[must_use]
pub fn to_argv(&self) -> Vec<String> {
let mut argv: Vec<String> = Vec::new();
let mut push = |flag: &str| argv.push(flag.to_owned());
if self.fixed {
push("-F");
}
if self.ignore_case {
push("-i");
}
if self.smart_case {
push("-S");
}
if self.word {
push("-w");
}
if self.quiet {
push("-q");
}
if self.invert {
push("-v");
}
if self.hidden {
push("--hidden");
}
if self.no_ignore {
push("--no-ignore");
}
if self.follow {
push("-L");
}
if self.no_index {
push("--no-index");
}
match self.engine {
SearchEngine::Auto => {
argv.push("--engine".to_owned());
argv.push("auto".to_owned());
},
SearchEngine::Pcre2 => argv.push("-P".to_owned()),
SearchEngine::Linear => {},
}
if self.multiline || self.multiline_dotall {
argv.push("-U".to_owned());
}
if self.multiline_dotall {
argv.push("--multiline-dotall".to_owned());
}
if let Some(on) = self.unicode {
let prefix = if on { "" } else { "no-" };
argv.push(format!("--{prefix}unicode"));
argv.push(format!("--{prefix}pcre2-unicode"));
}
for g in &self.globs {
argv.push("-g".to_owned());
argv.push(g.clone());
}
for g in &self.iglobs {
argv.push("--iglob".to_owned());
argv.push(g.clone());
}
for t in &self.types {
argv.push("-t".to_owned());
argv.push(t.clone());
}
for t in &self.not_types {
argv.push("-T".to_owned());
argv.push(t.clone());
}
if self.before > 0 {
argv.push("-B".to_owned());
argv.push(self.before.to_string());
}
if self.after > 0 {
argv.push("-A".to_owned());
argv.push(self.after.to_string());
}
if self.context > 0 && self.before == 0 && self.after == 0 {
argv.push("-C".to_owned());
argv.push(self.context.to_string());
}
if self.max_count > 0 {
argv.push("-m".to_owned());
argv.push(self.max_count.to_string());
}
if self.max_depth > 0 {
argv.push("--max-depth".to_owned());
argv.push(self.max_depth.to_string());
}
argv.extend(self.extra_flags.iter().cloned());
argv
}
pub fn run(&self) -> Result<Vec<Match>> {
engine::run(self)
}
pub fn files(&self) -> Result<Vec<String>> {
engine::files(self)
}
pub fn count(&self) -> Result<usize> {
engine::count(self)
}
}
impl<S: Into<String>> From<S> for SearchRequest {
fn from(pattern: S) -> Self {
Self::new(pattern)
}
}