Skip to main content

01_product_lookup/
01_product_lookup.rs

1use 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(&current_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(&current_exe, &bundle_exe)?;
32    fs::set_permissions(&bundle_exe, fs::metadata(&current_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}