1use crate::global::Package as PackageDetails;
2#[cfg(feature = "network")]
3use crate::network::remote;
4use crate::technical::{Availability, Element, Package, Platform, PlatformAvailability, Type};
5use crate::{RepositoryConfig, RepositoryError, RepositoryResult};
6use melodium_common::descriptor::{Version, VersionReq};
7use std::collections::HashMap;
8use std::fs;
9use std::path::PathBuf;
10
11#[derive(Debug)]
12pub struct Repository {
13 config: RepositoryConfig,
14 packages: Vec<Package>,
15}
16
17impl Repository {
18 pub fn new(config: RepositoryConfig) -> Self {
19 Self {
20 config,
21 packages: Vec::new(),
22 }
23 }
24
25 pub fn config(&self) -> &RepositoryConfig {
26 &self.config
27 }
28
29 pub fn packages(&self) -> &Vec<Package> {
30 &self.packages
31 }
32
33 pub fn add_package(&mut self, package: Package) -> RepositoryResult<()> {
34 if self.packages.contains(&package) {
35 Err(RepositoryError::already_existing_package(
36 1,
37 package.name,
38 package.version,
39 ))
40 } else {
41 self.packages.push(package);
42 self.save_packages()
43 }
44 }
45
46 pub fn load_packages(&mut self) -> RepositoryResult<()> {
47 let path = self.packages_path();
48 if path.exists() {
49 let content =
50 fs::read_to_string(path).map_err(|err| RepositoryError::fs_error(23, err))?;
51 let packages = serde_json::from_str(&content)
52 .map_err(|err| RepositoryError::json_error(24, err))?;
53 self.merge_packages(packages)
54 } else {
55 Ok(())
56 }
57 }
58
59 pub fn load_packages_with_network(&mut self) -> RepositoryResult<()> {
60 #[cfg(not(feature = "network"))]
61 {
62 Err(RepositoryError::no_network(25))
63 }
64 #[cfg(feature = "network")]
65 {
66 let config = self.config.network.clone();
67 if let Some(config) = config {
68 self.load_packages()?;
69 self.merge_packages(remote::get_tech_packages(&config)?)
70 } else {
71 Err(RepositoryError::no_network(26))
72 }
73 }
74 }
75
76 pub fn set_platform_availability(
82 &mut self,
83 package: &Package,
84 platform: &Platform,
85 availability: &Availability,
86 element: Element,
87 ) -> RepositoryResult<()> {
88 if let Some(package) = self.packages.iter_mut().find(|p| *p == package) {
89 match &mut package.r#type {
90 Type::Compiled {
91 crate_name: _,
92 platforms,
93 } => {
94 if let Some(platform_availability) =
95 platforms.iter_mut().find(|pa| &pa.platform == platform)
96 {
97 platform_availability
98 .availability
99 .insert(availability.clone(), element);
100 } else {
101 let pa = PlatformAvailability {
102 platform: platform.clone(),
103 availability: {
104 let mut availabilities = HashMap::new();
105 availabilities.insert(availability.clone(), element);
106 availabilities
107 },
108 };
109 platforms.push(pa);
110 }
111
112 self.save_packages()
113 }
114 _ => Err(RepositoryError::not_platform_dependent(
115 18,
116 package.name.clone(),
117 package.version.clone(),
118 )),
119 }
120 } else {
121 Err(RepositoryError::unknown_package(
122 17,
123 package.name.clone(),
124 package.version.clone(),
125 ))
126 }
127 }
128
129 pub fn get_platform_availability(
133 &self,
134 package: &Package,
135 platform: &Platform,
136 availability: &Availability,
137 ) -> RepositoryResult<Element> {
138 if let Some(package) = self.packages.iter().find(|p| *p == package) {
139 match &package.r#type {
140 Type::Compiled {
141 crate_name: _,
142 platforms,
143 } => {
144 if let Some(platform_availability) =
145 platforms.iter().find(|pa| &pa.platform == platform)
146 {
147 if let Some(element) = platform_availability.availability.get(&availability)
148 {
149 Ok(element.clone())
150 } else {
151 Err(RepositoryError::platform_unavailable(
152 22,
153 package.name.clone(),
154 package.version.clone(),
155 platform.clone(),
156 availability.clone(),
157 ))
158 }
159 } else {
160 Err(RepositoryError::platform_unavailable(
161 21,
162 package.name.clone(),
163 package.version.clone(),
164 platform.clone(),
165 availability.clone(),
166 ))
167 }
168 }
169 _ => Err(RepositoryError::not_platform_dependent(
170 19,
171 package.name.clone(),
172 package.version.clone(),
173 )),
174 }
175 } else {
176 Err(RepositoryError::unknown_package(
177 20,
178 package.name.clone(),
179 package.version.clone(),
180 ))
181 }
182 }
183
184 #[allow(unused_variables)]
188 pub fn get_platform_availability_with_network(
189 &mut self,
190 package: &Package,
191 platform: &Platform,
192 availability: &Availability,
193 ) -> RepositoryResult<Element> {
194 #[cfg(not(feature = "network"))]
195 {
196 Err(RepositoryError::no_network(36))
197 }
198 #[cfg(feature = "network")]
199 {
200 if self.config.network.is_some() {
201 if let Ok(element) = self.get_platform_availability(package, platform, availability)
202 {
203 Ok(element)
204 } else {
205 self.load_packages_with_network()?;
206 self.get_platform_availability(package, platform, availability)
207 }
208 } else {
209 Err(RepositoryError::no_network(37))
210 }
211 }
212 }
213
214 pub fn get_package_element(
221 &self,
222 package: &Package,
223 platform_availability: Option<(&Platform, &Availability)>,
224 ) -> RepositoryResult<Element> {
225 if let Some(package) = self.packages.iter().find(|p| *p == package) {
226 match &package.r#type {
227 Type::Compiled {
228 crate_name: _,
229 platforms,
230 } => {
231 if let Some((platform, availability)) = platform_availability {
232 if let Some(platform_availability) =
233 platforms.iter().find(|pa| &pa.platform == platform)
234 {
235 if let Some(element) =
236 platform_availability.availability.get(&availability)
237 {
238 Ok(element.clone())
239 } else {
240 Err(RepositoryError::platform_unavailable(
241 28,
242 package.name.clone(),
243 package.version.clone(),
244 platform.clone(),
245 availability.clone(),
246 ))
247 }
248 } else {
249 Err(RepositoryError::platform_unavailable(
250 29,
251 package.name.clone(),
252 package.version.clone(),
253 platform.clone(),
254 availability.clone(),
255 ))
256 }
257 } else {
258 Err(RepositoryError::platform_dependent(
259 30,
260 package.name.clone(),
261 package.version.clone(),
262 ))
263 }
264 }
265 Type::Jeu { file } => Ok(file.clone()),
266 }
267 } else {
268 Err(RepositoryError::unknown_package(
269 27,
270 package.name.clone(),
271 package.version.clone(),
272 ))
273 }
274 }
275
276 pub fn get_package_element_path(
283 &self,
284 package: &Package,
285 platform_availability: Option<(&Platform, &Availability)>,
286 ) -> RepositoryResult<PathBuf> {
287 let element = self.get_package_element(package, platform_availability)?;
288 match &package.r#type {
289 Type::Compiled {
290 crate_name: _,
291 platforms: _,
292 } => {
293 if let Some((platform, availability)) = platform_availability {
294 let mut path = self.config.repository_location.clone();
295 path.push(package.get_path());
296 path.push(platform.to_string());
297 path.push(availability.to_string());
298 path.push(element.name);
299 Ok(path)
300 } else {
301 Err(RepositoryError::platform_dependent(
302 31,
303 package.name.clone(),
304 package.version.clone(),
305 ))
306 }
307 }
308 Type::Jeu { file: _ } => {
309 let mut path = self.config.repository_location.clone();
310 path.push(package.get_path());
311 path.push(element.name);
312 Ok(path)
313 }
314 }
315 }
316
317 pub fn reach_package_element(
323 &self,
324 package: &Package,
325 platform_availability: Option<(&Platform, &Availability)>,
326 ) -> RepositoryResult<PathBuf> {
327 let path = self.get_package_element_path(package, platform_availability)?;
328 if path.is_file() {
329 Ok(path)
330 } else {
331 Err(RepositoryError::package_element_absent(
332 32,
333 package.name.clone(),
334 package.version.clone(),
335 platform_availability.map(|(p, a)| (p.clone(), a.clone())),
336 self.get_package_element(package, platform_availability)?,
337 path,
338 ))
339 }
340 }
341
342 #[allow(unused_variables)]
348 pub fn reach_package_element_with_network(
349 &mut self,
350 package: &Package,
351 platform_availability: Option<(&Platform, &Availability)>,
352 ) -> RepositoryResult<PathBuf> {
353 #[cfg(not(feature = "network"))]
354 {
355 Err(RepositoryError::no_network(38))
356 }
357 #[cfg(feature = "network")]
358 {
359 let config = self.config.network.clone();
360 if let Some(config) = config {
361 let path = self.get_package_element_path(package, platform_availability)?;
362 if path.is_file() {
363 Ok(path)
364 } else {
365 let element = self.get_package_element(package, platform_availability)?;
366
367 let url_path = match &package.r#type {
368 Type::Compiled {
369 crate_name: _,
370 platforms: _,
371 } => {
372 if let Some((platform, availability)) = platform_availability {
373 let mut path = package.get_path();
374 path.push(platform.to_string());
375 path.push(availability.to_string());
376 path.push(element.name.clone());
377 path
378 } else {
379 return Err(RepositoryError::platform_dependent(
380 40,
381 package.name.clone(),
382 package.version.clone(),
383 ));
384 }
385 }
386 Type::Jeu { file: _ } => {
387 let mut path = package.get_path();
388 path.push(element.name.clone());
389 path
390 }
391 };
392
393 remote::get_element_package(&config, element.clone(), url_path, path.clone())?;
394
395 if path.is_file() {
396 Ok(path)
397 } else {
398 Err(RepositoryError::package_element_absent(
399 41,
400 package.name.clone(),
401 package.version.clone(),
402 platform_availability.map(|(p, a)| (p.clone(), a.clone())),
403 element,
404 path,
405 ))
406 }
407 }
408 } else {
409 Err(RepositoryError::no_network(39))
410 }
411 }
412 }
413
414 pub fn remove_package(&mut self, name: &str, version: &Version) -> RepositoryResult<()> {
415 self.packages
416 .retain(|pkg| !(pkg.name == name && &pkg.version == version));
417 Ok(())
418 }
419
420 fn merge_packages(&mut self, packages: Vec<Package>) -> RepositoryResult<()> {
421 for package in packages {
422 if !self.packages.contains(&package) {
423 self.packages.push(package);
424 }
425 }
426 self.save_packages()
427 }
428
429 pub fn get_package(
430 &self,
431 name: &str,
432 version_req: &VersionReq,
433 ) -> RepositoryResult<Option<Package>> {
434 Ok(self
435 .packages
436 .iter()
437 .filter(|p| p.name == name && version_req.matches(&p.version))
438 .max_by_key(|p| p.version.clone())
439 .map(|p| p.clone()))
440 }
441
442 #[allow(unused_variables)]
443 pub fn get_package_with_network(
444 &mut self,
445 name: &str,
446 version_req: &VersionReq,
447 ) -> RepositoryResult<Option<Package>> {
448 #[cfg(not(feature = "network"))]
449 {
450 Err(RepositoryError::no_network(13))
451 }
452 #[cfg(feature = "network")]
453 {
454 let config = self.config.network.clone();
455 if let Some(config) = config {
456 if let Ok(Some(package)) = self.get_package(name, version_req) {
457 Ok(Some(package))
458 } else {
459 let packages = remote::get_tech_packages(&config)?;
460 self.merge_packages(packages)?;
461 self.get_package(name, version_req)
462 }
463 } else {
464 Err(RepositoryError::no_network(14))
465 }
466 }
467 }
468
469 pub fn set_package_details(&self, details: &PackageDetails) -> RepositoryResult<()> {
470 if let Some(package) = self
471 .packages
472 .iter()
473 .find(|p| p.name == details.name && p.version == details.version)
474 {
475 let mut path = self.config.repository_location.clone();
476 path.push(package.get_path());
477 fs::create_dir_all(path.clone()).map_err(|err| RepositoryError::fs_error(7, err))?;
478 path.push("package.json");
479 fs::write(
480 path,
481 serde_json::to_string(&details)
482 .map_err(|err| RepositoryError::json_error(8, err))?,
483 )
484 .map_err(|err| RepositoryError::fs_error(9, err))
485 } else {
486 Err(RepositoryError::unknown_package(
487 6,
488 details.name.clone(),
489 details.version.clone(),
490 ))
491 }
492 }
493
494 pub fn get_package_details(&self, package: &Package) -> RepositoryResult<PackageDetails> {
495 if let Some(package) = self
496 .packages
497 .iter()
498 .find(|p| p.name == package.name && p.version == package.version)
499 {
500 let mut path = self.config.repository_location.clone();
501 path.push(package.get_path());
502 path.push("package.json");
503 fs::read_to_string(path)
504 .map_err(|err| RepositoryError::fs_error(10, err))
505 .and_then(|str| {
506 serde_json::from_str(&str).map_err(|err| RepositoryError::json_error(11, err))
507 })
508 } else {
509 Err(RepositoryError::unknown_package(
510 12,
511 package.name.to_string(),
512 package.version.clone(),
513 ))
514 }
515 }
516
517 #[allow(unused_variables)]
518 pub fn get_package_details_with_network(
519 &self,
520 package: &Package,
521 ) -> RepositoryResult<PackageDetails> {
522 #[cfg(not(feature = "network"))]
523 {
524 Err(RepositoryError::no_network(16))
525 }
526 #[cfg(feature = "network")]
527 {
528 let config = self.config.network.clone();
529 if let Some(config) = config {
530 if let Ok(pkg) = self.get_package_details(package) {
531 return Ok(pkg);
532 }
533
534 let pkg = remote::get_detail_package(&config, package);
535 if let Ok(pkg) = pkg {
536 self.set_package_details(&pkg)?;
537 self.get_package_details(package)
538 } else {
539 pkg
540 }
541 } else {
542 Err(RepositoryError::no_network(15))
543 }
544 }
545 }
546
547 fn save_packages(&self) -> RepositoryResult<()> {
548 fs::create_dir_all(self.config.repository_location.clone())
549 .map_err(|err| RepositoryError::fs_error(3, err))?;
550 fs::write(
551 self.packages_path(),
552 serde_json::to_string(&self.packages)
553 .map_err(|err| RepositoryError::json_error(5, err))?,
554 )
555 .map_err(|err| RepositoryError::fs_error(4, err))
556 }
557
558 fn packages_path(&self) -> PathBuf {
559 let mut path = self.config.repository_location.clone();
560 path.push("packages.json");
561 path
562 }
563}