summaryrefslogtreecommitdiffstats
path: root/src/search
diff options
context:
space:
mode:
Diffstat (limited to 'src/search')
-rw-r--r--src/search/entry.rs57
-rw-r--r--src/search/entry/config.rs90
2 files changed, 147 insertions, 0 deletions
diff --git a/src/search/entry.rs b/src/search/entry.rs
new file mode 100644
index 0000000..6e94b35
--- /dev/null
+++ b/src/search/entry.rs
@@ -0,0 +1,57 @@
+use ignore::{DirEntry, Walk};
+use tracing::error;
+
+use crate::{
+ project::{GitProject, PathProject},
+ Project,
+};
+
+pub use config::Config;
+
+mod config;
+
+pub struct Entry {
+ config: Config,
+ iter: Walk,
+}
+
+impl std::fmt::Debug for Entry {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("SearchPath")
+ .field("config", &self.config)
+ .finish()
+ }
+}
+
+impl Entry {
+ pub fn match_project(&self, dir_entry: DirEntry) -> Option<Box<dyn Project>> {
+ if self.config.git {
+ if let Ok(git) = GitProject::try_from(dir_entry.to_owned()) {
+ return Some(Box::new(git));
+ };
+ };
+
+ if let Some(pattern) = &self.config.pattern {
+ if let Ok(proj) = PathProject::try_from((pattern, dir_entry)) {
+ return Some(Box::new(proj));
+ };
+ };
+
+ None
+ }
+}
+
+impl Iterator for Entry {
+ type Item = Box<dyn Project>;
+
+ #[tracing::instrument]
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.iter.next()? {
+ Ok(dir_entry) => self.match_project(dir_entry),
+ Err(err) => {
+ error!(%err, "Ignoring errored path");
+ self.next()
+ }
+ }
+ }
+}
diff --git a/src/search/entry/config.rs b/src/search/entry/config.rs
new file mode 100644
index 0000000..d325b58
--- /dev/null
+++ b/src/search/entry/config.rs
@@ -0,0 +1,90 @@
+use ignore::WalkBuilder;
+use serde::{Deserialize, Deserializer, Serialize};
+use std::{convert::Infallible, path::PathBuf, str::FromStr};
+
+use super::Entry;
+
+#[derive(Debug, PartialEq, Eq, Clone, Default, Serialize)]
+#[serde(default)]
+pub struct Config {
+ pub path_buf: PathBuf,
+ pub hidden: bool,
+ pub max_depth: Option<usize>,
+ pub git: bool,
+ pub pattern: Option<String>,
+}
+
+impl From<Config> for Entry {
+ fn from(config: Config) -> Self {
+ let iter = WalkBuilder::new(&config.path_buf)
+ .standard_filters(true)
+ .max_depth(config.max_depth)
+ .hidden(!config.hidden)
+ .build();
+ Self { iter, config }
+ }
+}
+
+impl From<PathBuf> for Config {
+ fn from(path_buf: PathBuf) -> Self {
+ Self {
+ path_buf,
+ ..Default::default()
+ }
+ }
+}
+
+impl Config {
+ pub fn new(path_buf: PathBuf) -> Self {
+ Self {
+ path_buf,
+ ..Default::default()
+ }
+ }
+}
+
+impl FromStr for Config {
+ type Err = Infallible;
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ s.parse().map(PathBuf::into)
+ }
+}
+
+// Custom deserialize impl to accept either string or struct
+impl<'de> Deserialize<'de> for Config {
+ fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ #[derive(Deserialize)]
+ #[serde(untagged)]
+ enum Variants {
+ String(String),
+ Struct {
+ path_buf: PathBuf,
+ hidden: bool,
+ max_depth: Option<usize>,
+ git: bool,
+ pattern: Option<String>,
+ },
+ }
+
+ match Variants::deserialize(deserializer)? {
+ Variants::String(s) => s.parse().map_err(serde::de::Error::custom),
+ Variants::Struct {
+ path_buf,
+ hidden,
+ max_depth,
+ git,
+ pattern,
+ } => Ok(Self {
+ path_buf,
+ hidden,
+ max_depth,
+ git,
+ pattern,
+ }),
+ }
+ }
+}