xcodeproj/pbxproj/object/
collection.rs1use crate::pbxproj::*;
5use anyhow::Result;
6use std::collections::HashMap;
7
8#[derive(Default, Debug, derive_new::new, derive_deref_rs::Deref)]
10pub struct PBXObjectCollection(pub(crate) HashMap<String, PBXHashMap>);
11
12pub trait AsPBXObject<'a> {
14 fn as_pbx_object(
16 id: String,
17 value: &'a PBXHashMap,
18 objects: &'a PBXObjectCollection,
19 ) -> Result<Self>
20 where
21 Self: Sized + 'a;
22}
23
24impl PBXObjectCollection {
25 pub fn get<'a, T, S>(&'a self, key: S) -> Option<T>
27 where
28 T: AsPBXObject<'a> + 'a,
29 S: AsRef<str>,
30 {
31 self.0.get(key.as_ref()).and_then(|value| {
32 AsPBXObject::as_pbx_object(key.as_ref().to_string(), value, self).ok()
33 })
34 }
35
36 pub fn try_get<'a, T, S>(&'a self, key: S) -> Result<T>
38 where
39 T: AsPBXObject<'a> + 'a,
40 S: AsRef<str> + std::fmt::Debug,
41 {
42 self.get(key.as_ref())
43 .ok_or_else(|| anyhow::anyhow!("{key:?} doesn't exists!"))
44 }
45
46 pub fn get_vec<'a, T, I, S>(&'a self, keys: I) -> Vec<T>
48 where
49 T: AsPBXObject<'a> + 'a,
50 I: IntoIterator<Item = S> + Send,
51 S: AsRef<str>,
52 {
53 keys.into_iter()
54 .flat_map(|key| self.get(key.as_ref()))
55 .collect::<Vec<_>>()
56 }
57
58 pub fn get_vec_by<'a, T: AsPBXObject<'a> + 'a>(
60 &'a self,
61 predict: impl Fn(&(&String, &PBXHashMap)) -> bool,
62 ) -> Vec<T> {
63 self.iter()
64 .filter(predict)
65 .flat_map(|(k, _)| self.get(k))
66 .collect::<Vec<_>>()
67 }
68
69 pub fn targets<'a>(&'a self) -> Vec<PBXTarget<'a>> {
71 self.get_vec_by(|(_, v)| {
72 v.get_kind("isa")
73 .map(|k| k.is_pbx_target())
74 .unwrap_or_default()
75 })
76 }
77
78 pub fn projects<'a>(&'a self) -> Vec<PBXProject<'a>> {
80 self.get_vec_by(|(_, v)| {
81 v.get_kind("isa")
82 .map(|k| k.is_pbx_project())
83 .unwrap_or_default()
84 })
85 }
86
87 pub fn build_phases<'a>(&'a self) -> Vec<PBXBuildPhase<'a>> {
89 self.get_vec_by(|(_, v)| {
90 v.get_kind("isa")
91 .map(|k| k.is_pbx_build_phase())
92 .unwrap_or_default()
93 })
94 }
95
96 pub fn build_configurations<'a>(&'a self) -> Vec<XCBuildConfiguration<'a>> {
98 self.get_vec_by(|(_, v)| {
99 v.get_kind("isa")
100 .map(|k| k.is_xc_build_configuration())
101 .unwrap_or_default()
102 })
103 }
104
105 pub fn build_files<'a>(&'a self) -> Vec<PBXBuildFile<'a>> {
107 self.get_vec_by(|(_, v)| {
108 v.get_kind("isa")
109 .map(|k| k.is_pbx_build_file())
110 .unwrap_or_default()
111 })
112 }
113
114 pub fn build_rules<'a>(&'a self) -> Vec<PBXBuildRule<'a>> {
116 self.get_vec_by(|(_, v)| {
117 v.get_kind("isa")
118 .map(|k| k.is_pbx_build_rule())
119 .unwrap_or_default()
120 })
121 }
122
123 pub fn files<'a>(&'a self) -> Vec<PBXFSReference<'a>> {
125 self.get_vec_by(|(_, v)| {
126 v.get_kind("isa")
127 .map(|k| {
128 k.is_pbx_fsreference()
129 && k.as_pbxfs_reference()
130 .map(|r| r.is_file())
131 .unwrap_or_default()
132 })
133 .unwrap_or_default()
134 })
135 }
136
137 pub fn groups<'a>(&'a self) -> Vec<PBXFSReference<'a>> {
139 self.get_vec_by(|(_, v)| {
140 v.get_kind("isa")
141 .map(|k| {
142 k.is_pbx_fsreference()
143 && k.as_pbxfs_reference()
144 .map(|r| r.is_group())
145 .unwrap_or_default()
146 })
147 .unwrap_or_default()
148 })
149 }
150
151 pub fn swift_package_product_dependencies<'a>(
153 &'a self,
154 ) -> Vec<XCSwiftPackageProductDependency> {
155 self.get_vec_by(|(_, v)| {
156 v.get_kind("isa")
157 .map(|k| k.is_xc_swift_package_product_dependency())
158 .unwrap_or_default()
159 })
160 }
161
162 pub fn swift_package_references<'a>(&'a self) -> Vec<XCRemoteSwiftPackageReference> {
164 self.get_vec_by(|(_, v)| {
165 v.get_kind("isa")
166 .map(|k| k.is_xc_remote_swift_package_reference())
167 .unwrap_or_default()
168 })
169 }
170
171 pub fn get_target<'a>(&'a self, key: &str) -> Option<PBXTarget> {
173 self.get(key)
174 }
175
176 pub fn get_build_phase<'a>(&'a self, key: &str) -> Option<PBXBuildPhase> {
178 self.get(key)
179 }
180
181 pub fn get_build_file<'a>(&'a self, key: &str) -> Option<PBXBuildFile> {
183 self.get(key)
184 }
185
186 pub fn get_build_rule<'a>(&'a self, key: &str) -> Option<PBXBuildRule> {
188 self.get(key)
189 }
190
191 pub fn get_project<'a>(&'a self, key: &str) -> Option<PBXProject> {
193 self.get(key)
194 }
195
196 pub fn get_file<'a>(&'a self, key: &str) -> Option<PBXFSReference> {
198 let fs_ref = self.get::<PBXFSReference, _>(key)?;
199 if fs_ref.is_file() {
200 Some(fs_ref)
201 } else {
202 None
203 }
204 }
205
206 pub fn get_group<'a>(&'a self, key: &str) -> Option<PBXFSReference> {
208 let fs_ref = self.get::<PBXFSReference, _>(key)?;
209 if fs_ref.is_group() {
210 Some(fs_ref)
211 } else {
212 None
213 }
214 }
215
216 pub fn get_fs_object<'a>(&'a self, key: &str) -> Option<PBXFSReference> {
218 self.get(key)
219 }
220
221 pub fn get_group_by_name_or_path<'a, S: AsRef<str>>(
223 &'a self,
224 name_or_path: S,
225 ) -> Option<PBXFSReference> {
226 let name = name_or_path.as_ref();
227 self.groups().into_iter().find(|o| {
228 if let Some(n) = o.name {
229 return n == name;
230 } else if let Some(p) = o.path {
231 return p == name;
232 } else {
233 false
234 }
235 })
236 }
237
238 pub fn get_build_configurations_by_base_id<S: AsRef<str>>(
240 &self,
241 id: S,
242 ) -> Vec<XCBuildConfiguration> {
243 let key = id.as_ref();
244 self.get_vec_by(move |(_, v)| {
245 v.get_kind("isa")
246 .map(|o| o.is_xc_build_configuration())
247 .unwrap_or_default()
248 && v.get_string("baseConfigurationReference")
249 .map(|s| s.as_str())
250 == Some(key)
251 })
252 }
253
254 pub fn get_swift_package_product_dependency<'a>(
256 &'a self,
257 key: &str,
258 ) -> Option<XCSwiftPackageProductDependency> {
259 self.get(key)
260 }
261
262 pub fn get_swift_package_reference<'a>(
264 &'a self,
265 key: &str,
266 ) -> Option<XCRemoteSwiftPackageReference> {
267 self.get(key)
268 }
269
270 pub fn get_target_by_name<'a>(&'a self, name: &'a str) -> Option<PBXTarget> {
272 self.targets().into_iter().find(|target| {
273 if let Some(target_name) = target.name {
274 target_name == name
275 } else {
276 false
277 }
278 })
279 }
280}