1use crate::sync;
4pub use error::{Error, Result};
5pub use ignore_rules::IgnoreRules;
6pub use std::hash::Hash;
7pub use sync::{PathSync, PathSyncSingleton};
8
9pub use crate::notify::{make_watcher, PathEvent, RecommendedWatcher};
10
11use std::{fmt::Debug, path::PathBuf};
12
13use crate::error;
14use crate::ignore_rules;
15
16#[derive(Debug, Clone)]
18pub enum MatchResult {
19 NoMatch,
21 Ignore,
23 Whitelist,
25}
26
27#[derive(Debug, Clone, Hash, PartialEq, Eq)]
29pub enum PatternRelativity {
30 Anywhere,
32 RelativeTo {
34 directory: String,
36 },
37}
38
39#[derive(Debug, Clone, Hash, PartialEq, Eq)]
41pub enum PathKind {
42 Any,
44 Directory,
46}
47
48#[derive(Debug, Clone, Eq, PartialEq, Hash)]
50pub enum PatternEffect {
51 Ignore,
53 Whitelist,
55}
56
57#[derive(Debug, Clone, Hash, PartialEq, Eq)]
60pub enum Source {
61 Global,
63
64 File {
66 path: PathBuf,
68 line: usize,
70 },
71
72 CommandLine {
74 current_dir: PathBuf,
76 },
77}
78
79#[derive(Debug)]
84pub struct Pattern {
85 pub glob: String,
87 pub original: String,
89 pub source: Source,
91 pub effect: PatternEffect,
93 pub relativity: PatternRelativity,
95 pub path_kind: PathKind,
97}
98
99impl Pattern {
100 pub fn new(source: Source, original: &str) -> Self {
102 let original = original.to_owned();
103 let current_dir = match &source {
104 Source::Global => "".to_string(),
105 Source::File { path, .. } => {
106 let path = path
107 .parent()
108 .expect("Pattern source file doesn't have parent")
109 .to_string_lossy()
110 .to_string();
111 if path.starts_with('/') {
112 path
113 } else {
114 format!("/{path}")
115 }
116 }
117 Source::CommandLine { current_dir } => current_dir.to_string_lossy().to_string(),
118 };
119
120 let begin_exclamation = original.starts_with('!');
125 let mut line = if begin_exclamation || original.starts_with(r"\!") {
126 original[1..].to_owned()
127 } else {
128 original.to_owned()
129 };
130
131 if !line.ends_with("\\ ") {
134 line = line.trim_end().to_string();
135 }
136
137 let end_slash = line.ends_with('/');
138 if end_slash {
139 line = line[..line.len() - 1].to_string()
140 }
141
142 let begin_slash = line.starts_with('/');
143 let non_final_slash = if !line.is_empty() {
144 line[..line.len() - 1].chars().any(|c| c == '/')
145 } else {
146 false
147 };
148
149 if begin_slash {
150 line = line[1..].to_string();
151 }
152
153 let current_dir = if current_dir.ends_with('/') {
154 ¤t_dir[..current_dir.len() - 1]
155 } else {
156 ¤t_dir
157 };
158
159 let effect = if begin_exclamation {
160 PatternEffect::Whitelist
161 } else {
162 PatternEffect::Ignore
163 };
164
165 let path_kind = if end_slash {
166 PathKind::Directory
167 } else {
168 PathKind::Any
169 };
170
171 let relativity = if non_final_slash {
172 PatternRelativity::RelativeTo {
173 directory: current_dir.to_owned(),
174 }
175 } else {
176 PatternRelativity::Anywhere
177 };
178
179 let glob = transform_pattern_for_glob(&line, relativity.clone(), path_kind.clone());
180
181 Pattern {
182 glob,
183 original,
184 source,
185 effect,
186 relativity,
187 path_kind,
188 }
189 }
190}
191
192fn transform_pattern_for_glob(
193 original: &str,
194 relativity: PatternRelativity,
195 path_kind: PathKind,
196) -> String {
197 let anything_anywhere = |p| format!("**/{p}");
198 let anything_relative = |p, directory| format!("{directory}/**/{p}");
199 let directory_anywhere = |p| format!("**/{p}/**");
200 let directory_relative = |p, directory| format!("{directory}/**/{p}/**");
201
202 match (path_kind, relativity) {
203 (PathKind::Any, PatternRelativity::Anywhere) => anything_anywhere(original),
204 (PathKind::Any, PatternRelativity::RelativeTo { directory }) => {
205 anything_relative(original, directory)
206 }
207 (PathKind::Directory, PatternRelativity::Anywhere) => directory_anywhere(original),
208 (PathKind::Directory, PatternRelativity::RelativeTo { directory }) => {
209 directory_relative(original, directory)
210 }
211 }
212}
213
214pub fn build_pattern_list(patterns: Vec<String>, source: Source) -> Vec<Pattern> {
216 patterns
217 .iter()
218 .map(|p| Pattern::new(source.clone(), p))
219 .collect()
220}