01_product_lookup/
01_product_lookup.rs1use std::env;
2use std::fs;
3use std::path::Path;
4use std::process::Command;
5
6use storekit::Product;
7
8fn main() -> Result<(), Box<dyn std::error::Error>> {
9 if env::var_os("STOREKIT_PRODUCT_LOOKUP_BUNDLED").is_none() {
10 return relaunch_inside_app_bundle();
11 }
12
13 match Product::products_for(["nonexistent.product.id"]) {
14 Ok(products) if products.is_empty() => println!("✅ product lookup OK (empty result)"),
15 Ok(products) => println!("ℹ️ product lookup returned {} products", products.len()),
16 Err(error) => println!("ℹ️ product lookup error: {error}"),
17 }
18 Ok(())
19}
20
21fn relaunch_inside_app_bundle() -> Result<(), Box<dyn std::error::Error>> {
22 let current_exe = env::current_exe()?;
23 let crate_root = env::current_dir()?;
24 let executable = executable_name(¤t_exe);
25 let app_root = crate_root.join("target/storekit-product-lookup.app");
26 let contents_dir = app_root.join("Contents");
27 let macos_dir = contents_dir.join("MacOS");
28 let bundle_exe = macos_dir.join(&executable);
29
30 fs::create_dir_all(&macos_dir)?;
31 fs::copy(¤t_exe, &bundle_exe)?;
32 fs::set_permissions(&bundle_exe, fs::metadata(¤t_exe)?.permissions())?;
33 fs::write(contents_dir.join("Info.plist"), info_plist(&executable))?;
34
35 let status = Command::new(&bundle_exe)
36 .env("STOREKIT_PRODUCT_LOOKUP_BUNDLED", "1")
37 .status()?;
38
39 if status.success() {
40 Ok(())
41 } else {
42 Err(format!("bundled product lookup runner exited with status {status}").into())
43 }
44}
45
46fn executable_name(path: &Path) -> String {
47 path.file_name()
48 .and_then(|value| value.to_str())
49 .map_or_else(|| "01_product_lookup".to_owned(), ToOwned::to_owned)
50}
51
52fn info_plist(executable_name: &str) -> String {
53 [
54 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>",
55 "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">",
56 "<plist version=\"1.0\">",
57 "<dict>",
58 " <key>CFBundleExecutable</key>",
59 &format!(" <string>{executable_name}</string>"),
60 " <key>CFBundleIdentifier</key>",
61 " <string>fish.doom.storekit.product.lookup</string>",
62 " <key>CFBundleName</key>",
63 " <string>storekit-product-lookup</string>",
64 " <key>CFBundlePackageType</key>",
65 " <string>APPL</string>",
66 " <key>LSMinimumSystemVersion</key>",
67 " <string>12.0</string>",
68 "</dict>",
69 "</plist>",
70 ]
71 .join("\n")
72}