polyhorn_cli/ios/tasks/generate_xcassets.rs
1use super::{IOSContext, IOSError};
2use crate::core::{rasterize, Manager, Task};
3use crate::ios::xcassets::{Folder, Image, ImageSet, Info, Properties, XcAssets};
4
5/// This task generates an Xcode-compatible assets catalog for the assets of
6/// the Polyhorn project that is being compiled and its dependencies.
7pub struct GenerateXcassets;
8
9impl Task for GenerateXcassets {
10 type Context = IOSContext;
11 type Error = IOSError;
12
13 fn verb(&self) -> &str {
14 "Generating"
15 }
16
17 fn message(&self) -> &str {
18 "xcassets"
19 }
20
21 fn detail(&self) -> &str {
22 "for iOS"
23 }
24
25 fn run(
26 &self,
27 context: Self::Context,
28 _manager: &mut Manager,
29 ) -> Result<Self::Context, Self::Error> {
30 let target_dir = context.config.target_dir.join("polyhorn-ios");
31
32 let mut cmd = cargo_metadata::MetadataCommand::new();
33 cmd.manifest_path(context.config.manifest_dir.join("Cargo.toml"));
34 let metadata = cmd.exec().unwrap();
35
36 let mut result_path = std::env::current_dir().unwrap();
37 result_path.push(target_dir.join("Sources/Assets.xcassets/"));
38 let _ = std::fs::create_dir_all(&result_path);
39
40 {
41 let mut result_path = result_path.clone();
42 result_path.push("Contents.json");
43
44 serde_json::to_writer_pretty(
45 std::fs::File::create(result_path).unwrap(),
46 &XcAssets {
47 info: Info {
48 author: "polyhorn",
49 version: 1,
50 },
51 },
52 )
53 .unwrap();
54 }
55
56 {
57 let mut result_path = result_path.clone();
58 result_path.push("AppIcon.appiconset");
59 let _ = std::fs::create_dir_all(&result_path);
60
61 result_path.push("Contents.json");
62
63 serde_json::to_writer_pretty(
64 std::fs::File::create(result_path).unwrap(),
65 &ImageSet {
66 images: vec![
67 Image {
68 filename: None,
69 idiom: "iphone",
70 scale: "2x",
71 size: Some("20x20"),
72 },
73 Image {
74 filename: None,
75 idiom: "iphone",
76 scale: "3x",
77 size: Some("20x20"),
78 },
79 Image {
80 filename: None,
81 idiom: "iphone",
82 scale: "2x",
83 size: Some("29x29"),
84 },
85 Image {
86 filename: None,
87 idiom: "iphone",
88 scale: "3x",
89 size: Some("29x29"),
90 },
91 Image {
92 filename: None,
93 idiom: "iphone",
94 scale: "2x",
95 size: Some("40x40"),
96 },
97 Image {
98 filename: None,
99 idiom: "iphone",
100 scale: "3x",
101 size: Some("40x40"),
102 },
103 Image {
104 filename: None,
105 idiom: "iphone",
106 scale: "2x",
107 size: Some("60x60"),
108 },
109 Image {
110 filename: None,
111 idiom: "iphone",
112 scale: "3x",
113 size: Some("60x60"),
114 },
115 Image {
116 filename: None,
117 idiom: "ipad",
118 scale: "1x",
119 size: Some("20x20"),
120 },
121 Image {
122 filename: None,
123 idiom: "ipad",
124 scale: "2x",
125 size: Some("20x20"),
126 },
127 Image {
128 filename: None,
129 idiom: "ipad",
130 scale: "1x",
131 size: Some("29x29"),
132 },
133 Image {
134 filename: None,
135 idiom: "ipad",
136 scale: "2x",
137 size: Some("29x29"),
138 },
139 Image {
140 filename: None,
141 idiom: "ipad",
142 scale: "1x",
143 size: Some("40x40"),
144 },
145 Image {
146 filename: None,
147 idiom: "ipad",
148 scale: "2x",
149 size: Some("40x40"),
150 },
151 Image {
152 filename: None,
153 idiom: "ipad",
154 scale: "1x",
155 size: Some("76x76"),
156 },
157 Image {
158 filename: None,
159 idiom: "ipad",
160 scale: "2x",
161 size: Some("76x76"),
162 },
163 Image {
164 filename: None,
165 idiom: "ipad",
166 scale: "2x",
167 size: Some("83.5x83.5"),
168 },
169 Image {
170 filename: None,
171 idiom: "ios-marketing",
172 scale: "1x",
173 size: Some("1024x1024"),
174 },
175 ],
176 info: Info {
177 author: "polyhorn",
178 version: 1,
179 },
180 },
181 )
182 .unwrap();
183 }
184
185 for package in metadata.packages {
186 let mut path = package.manifest_path.clone();
187 path.pop();
188 path.push("Polyhorn.toml");
189
190 if !path.exists() {
191 continue;
192 }
193
194 path.pop();
195 path.push("assets");
196
197 for entry in std::fs::read_dir(path).unwrap() {
198 if let Ok(entry) = entry {
199 if entry
200 .path()
201 .extension()
202 .map(|ext| ext == "svg")
203 .unwrap_or_default()
204 {
205 let path = entry.path();
206 let name = path.file_stem().unwrap().to_str().unwrap();
207
208 let mut result_path = std::env::current_dir().unwrap();
209 result_path.push(target_dir.join("Sources/Assets.xcassets/"));
210 result_path.push(&package.name);
211 let _ = std::fs::create_dir_all(&result_path);
212
213 {
214 let mut result_path = result_path.clone();
215 result_path.push("Contents.json");
216
217 serde_json::to_writer_pretty(
218 std::fs::File::create(result_path).unwrap(),
219 &Folder {
220 info: Info {
221 author: "polyhorn",
222 version: 1,
223 },
224 properties: Properties {
225 provides_namespace: true,
226 },
227 },
228 )
229 .unwrap();
230 }
231
232 result_path.push(name.to_owned() + ".imageset");
233 let _ = std::fs::create_dir_all(&result_path);
234
235 let zooms = [("1x", 1.0), ("2x", 2.0), ("3x", 3.0)];
236
237 for &(suffix, zoom) in &zooms {
238 let mut result_path = result_path.clone();
239 result_path.push("image".to_owned() + "@" + suffix + ".png");
240
241 rasterize(&entry.path(), zoom, &result_path).unwrap();
242 }
243
244 result_path.push("Contents.json");
245
246 serde_json::to_writer_pretty(
247 std::fs::File::create(result_path).unwrap(),
248 &ImageSet {
249 images: vec![
250 Image {
251 filename: Some("image@1x.png"),
252 idiom: "universal",
253 scale: "1x",
254 size: None,
255 },
256 Image {
257 filename: Some("image@2x.png"),
258 idiom: "universal",
259 scale: "2x",
260 size: None,
261 },
262 Image {
263 filename: Some("image@3x.png"),
264 idiom: "universal",
265 scale: "3x",
266 size: None,
267 },
268 ],
269 info: Info {
270 author: "polyhorn",
271 version: 1,
272 },
273 },
274 )
275 .unwrap();
276 }
277 }
278 }
279 }
280
281 Ok(context)
282 }
283}