grass/dev/strategy/discovery/
local.rs1use std::fs;
2
3use crate::dev::{
4 config::GrassConfig,
5 public::api::RepositoryLocation,
6 strategy::{
7 discovery::DiscoveryStrategyError,
8 path::{PathStrategy, PathStrategyError},
9 },
10};
11
12use super::{BoxedIterator, DiscoveryExists, DiscoveryStrategy, Result};
13
14pub struct LocalDiscoveryStrategy<'a, T>
15where
16 T: PathStrategy,
17{
18 config: &'a GrassConfig,
19 path_strategy: &'a T,
20}
21
22impl<'a, T> LocalDiscoveryStrategy<'a, T>
23where
24 T: PathStrategy,
25{
26 pub fn new(config: &'a GrassConfig, path_strategy: &'a T) -> Self {
27 LocalDiscoveryStrategy {
28 config,
29 path_strategy,
30 }
31 }
32}
33
34impl<'a, T> DiscoveryStrategy for LocalDiscoveryStrategy<'a, T>
35where
36 T: PathStrategy,
37{
38 fn check_repository_exists<U>(&self, repository: U) -> Result<DiscoveryExists>
39 where
40 U: Into<RepositoryLocation>,
41 {
42 let repository_dir = self.path_strategy.get_directory(repository)?;
43
44 match repository_dir.is_dir() {
45 true => Ok(DiscoveryExists::Exists),
46 false => Ok(DiscoveryExists::RepositoryNotFound),
47 }
48 }
49
50 fn check_category_exists<U>(&self, category: U) -> Result<DiscoveryExists>
51 where
52 U: AsRef<str>,
53 {
54 match self.config.get_by_category(category) {
55 Some(_) => Ok(DiscoveryExists::Exists),
56 None => Ok(DiscoveryExists::CategoryNotFound),
57 }
58 }
59
60 fn list_repositories_in_category<U>(
61 &self,
62 category: U,
63 ) -> Result<BoxedIterator<Result<RepositoryLocation>>>
64 where
65 U: AsRef<str>,
66 {
67 let category = self.config.get_by_category(category).ok_or(
68 DiscoveryStrategyError::CategoryNotFound {
69 context: "When trying to ge tthe category from the configuration".into(),
70 reason: "Category doesn't exist".into(),
71 },
72 )?;
73
74 let base_dir = self.config.base_dir.join(&category.name);
75
76 let directory =
77 fs::read_dir(&base_dir).map_err(|error| DiscoveryStrategyError::FilesystemError {
78 context: format!(
79 "When trying to read the directory '{dir}' (expected to be a category directory)",
80 dir = base_dir.to_str().unwrap_or("CANNOT DISPLAY DIRECTORY PATH")
81 ),
82 reason: error.to_string(),
83 })?;
84
85 Ok(Box::from(directory.into_iter().filter_map(move |entry| {
86 let entry = match entry {
87 Ok(entry) => entry,
88 Err(error) => {
89 return Some(Err(DiscoveryStrategyError::FilesystemError {
90 context: "When reading unknown entry".into(),
91 reason: error.to_string(),
92 }))
93 }
94 };
95
96 let metadata = match entry.metadata() {
97 Ok(metadata) => metadata,
98 Err(error) => {
99 return Some(Err(DiscoveryStrategyError::FilesystemError {
100 context: format!(
101 "When retrieving metadata for entry '{name}'",
102 name = entry
103 .path()
104 .to_str()
105 .unwrap_or("CANNOT DISPLAY DIRECTORY PATH")
106 ),
107 reason: error.to_string(),
108 }))
109 }
110 };
111
112 if !metadata.is_dir() {
113 return None;
114 };
115
116 let entry_path = entry.path();
117
118 let repository = match entry_path.file_name().ok_or_else(|| {
119 DiscoveryStrategyError::FilesystemError {
120 context: format!(
121 "When retrieving directory name from path {path}",
122 path = entry
123 .path()
124 .to_str()
125 .unwrap_or("CANNOT_DISPLAY_DIRECTORY_PATH")
126 ),
127 reason: "No reason given".into(),
128 }
129 }) {
130 Ok(repository) => repository,
131 Err(error) => return Some(Err(error)),
132 };
133
134 let repository: String = match repository.to_str() {
135 Some(repository) => repository,
136 None => {
137 return Some(Err(DiscoveryStrategyError::FilesystemError {
138 context: format!(
139 "When retrieving directory name from path {path}",
140 path = entry
141 .path()
142 .to_str()
143 .unwrap_or("CANNOT_DISPLAY_DIRECTORY_PATH")
144 ),
145 reason: "No reason given".into(),
146 }))
147 }
148 }
149 .into();
150
151 Some(Ok(RepositoryLocation {
152 category: category.name.clone().into(),
153 repository,
154 }))
155 })))
156 }
157
158 fn list_categories<U>(&self) -> Result<U>
159 where
160 U: FromIterator<String>,
161 {
162 Ok(self.config.category.keys().cloned().collect())
163 }
164
165 fn create_repository(&self, location: RepositoryLocation) -> Result<()> {
166 if matches!(
167 self.check_repository_exists(location.clone()),
168 Ok(DiscoveryExists::Exists)
169 ) {
170 return Err(DiscoveryStrategyError::RepositoryExists {
171 context: "Before creating a new repository".into(),
172 reason: "Repository already exists".into(),
173 });
174 }
175
176 let repository_directory = self.path_strategy.get_directory(location)?;
177
178 fs::create_dir_all(repository_directory)?;
179
180 Ok(())
181 }
182
183 fn move_repository(
184 &self,
185 old_location: RepositoryLocation,
186 new_location: RepositoryLocation,
187 ) -> Result<()> {
188 if !matches!(
189 self.check_repository_exists(old_location.clone()),
190 Ok(DiscoveryExists::Exists)
191 ) {
192 return Err(DiscoveryStrategyError::RepositoryDoesNotExist {
193 context: "Before moving a repository".into(),
194 reason: "Old repository does not exists".into(),
195 });
196 }
197
198 if matches!(
199 self.check_repository_exists(new_location.clone()),
200 Ok(DiscoveryExists::Exists)
201 ) {
202 return Err(DiscoveryStrategyError::RepositoryExists {
203 context: "Before moving a repository".into(),
204 reason: "New repository already exists".into(),
205 });
206 }
207
208 let old_repository_directory = self.path_strategy.get_directory(old_location)?;
209 let new_repository_directory = self.path_strategy.get_directory(new_location)?;
210
211 fs::rename(old_repository_directory, new_repository_directory)?;
212
213 Ok(())
214 }
215}
216
217impl From<PathStrategyError> for DiscoveryStrategyError {
218 fn from(value: PathStrategyError) -> Self {
219 match value {
220 PathStrategyError::RepositoryNotFound { context, reason } => {
221 DiscoveryStrategyError::CategoryNotFound { context, reason }
222 }
223 PathStrategyError::FileDoesNotExist { context, reason } => {
224 DiscoveryStrategyError::FilesystemError { context, reason }
225 }
226 PathStrategyError::Unknown { context, reason } => {
227 DiscoveryStrategyError::UnknownError { context, reason }
228 }
229 }
230 }
231}