ploys/project/
packages.rs1use std::borrow::Cow;
2use std::iter::FusedIterator;
3use std::path::Path;
4
5use relative_path::RelativePath;
6use strum::IntoEnumIterator;
7
8use crate::package::manifest::Members;
9use crate::package::{Manifest, Package, PackageKind};
10use crate::repository::Repository;
11
12use super::Project;
13
14pub struct Packages<'a, T> {
16 kinds: <PackageKind as IntoEnumIterator>::Iterator,
17 state: State<'a, T>,
18}
19
20impl<'a, T> Packages<'a, T> {
21 pub(super) fn new(project: &'a Project<T>) -> Self {
23 Self {
24 kinds: PackageKind::iter(),
25 state: State::Initial { project },
26 }
27 }
28}
29
30impl<'a, T> Iterator for Packages<'a, T>
31where
32 T: Repository,
33{
34 type Item = Package<&'a T>;
35
36 fn next(&mut self) -> Option<Self::Item> {
37 loop {
38 match &mut self.state {
39 State::Initial { project } => {
40 let kind = self.kinds.next()?;
41 let manifest = project
42 .repository
43 .get_file(kind.file_name())
44 .ok()
45 .flatten()
46 .and_then(|bytes| Manifest::from_bytes(kind, &bytes).ok());
47
48 if let Some(manifest) = manifest
49 && let Ok(members) = manifest.members()
50 && let Ok(files) = project.repository.get_index()
51 {
52 self.state = State::Manifest {
53 packages: ManifestPackages {
54 project,
55 manifest,
56 members,
57 files: Box::new(files),
58 },
59 };
60 }
61 }
62 State::Manifest { packages } => match packages.next() {
63 Some(package) => break Some(package),
64 None => {
65 let kind = self.kinds.next()?;
66 let manifest = packages
67 .project
68 .repository
69 .get_file(kind.file_name())
70 .ok()
71 .flatten()
72 .and_then(|bytes| Manifest::from_bytes(kind, &bytes).ok());
73
74 if let Some(manifest) = manifest
75 && let Ok(members) = manifest.members()
76 && let Ok(files) = packages.project.repository.get_index()
77 {
78 packages.manifest = manifest;
79 packages.members = members;
80 packages.files = Box::new(files);
81 }
82 }
83 },
84 }
85 }
86 }
87}
88
89impl<T> FusedIterator for Packages<'_, T> where T: Repository {}
90
91#[allow(clippy::large_enum_variant)]
92enum State<'a, T> {
93 Initial { project: &'a Project<T> },
94 Manifest { packages: ManifestPackages<'a, T> },
95}
96
97struct ManifestPackages<'a, T> {
99 project: &'a Project<T>,
100 manifest: Manifest,
101 members: Members,
102 files: Box<dyn Iterator<Item = Cow<'a, RelativePath>> + 'a>,
103}
104
105impl<'a, T> Iterator for ManifestPackages<'a, T>
106where
107 T: Repository,
108{
109 type Item = Package<&'a T>;
110
111 fn next(&mut self) -> Option<Self::Item> {
112 loop {
113 let path = self.files.next()?;
114 let manifest_path = self.manifest.package_kind().file_name();
115
116 if path.file_name() != Some(manifest_path) {
117 continue;
118 }
119
120 let Some(parent) = path.parent() else {
121 continue;
122 };
123
124 if path.as_ref() != manifest_path && !self.members.includes(Path::new(parent.as_str()))
125 {
126 continue;
127 }
128
129 let manifest = self
130 .project
131 .repository
132 .get_file(&path)
133 .ok()
134 .flatten()
135 .and_then(|bytes| Manifest::from_bytes(self.manifest.package_kind(), &bytes).ok());
136
137 let Some(manifest) = manifest else {
138 continue;
139 };
140
141 let Some(package) = Package::from_manifest(self.project, parent, manifest) else {
142 continue;
143 };
144
145 break Some(package);
146 }
147 }
148}
149
150impl<T> FusedIterator for ManifestPackages<'_, T> where T: Repository {}