Skip to main content

oxide_cli/addons/
install.rs

1use std::path::Path;
2
3use anyhow::Result;
4use serde::Deserialize;
5
6use crate::{
7  AppContext, BACKEND_URL,
8  auth::token::get_auth_user,
9  utils::archive::download_and_extract,
10};
11
12use super::{
13  cache::{get_cached_addon, update_addons_cache},
14  manifest::AddonManifest,
15};
16
17#[derive(Deserialize)]
18struct AddonUrlResponse {
19  archive_url: String,
20  commit_sha: String,
21}
22
23async fn get_addon_url(ctx: &AppContext, addon_id: &str) -> Result<AddonUrlResponse> {
24  let user = get_auth_user(&ctx.paths.auth)?;
25
26  let res: AddonUrlResponse = ctx
27    .client
28    .get(format!("{BACKEND_URL}/addon/{addon_id}/url"))
29    .bearer_auth(user.token)
30    .header("Content-Type", "application/json")
31    .send()
32    .await?
33    .error_for_status()?
34    .json()
35    .await?;
36
37  Ok(res)
38}
39
40pub async fn install_addon(ctx: &AppContext, addon_id: &str) -> Result<AddonManifest> {
41  let info = get_addon_url(ctx, addon_id).await?;
42
43  let dest = ctx.paths.addons.join(addon_id);
44
45  // Skip download if cached commit matches and dir exists
46  if let Some(cached) = get_cached_addon(&ctx.paths.addons, addon_id)?
47    && cached.commit_sha == info.commit_sha
48    && dest.exists()
49  {
50    let manifest_path = dest.join("oxide.addon.json");
51    let content = std::fs::read_to_string(&manifest_path)?;
52    let manifest: AddonManifest = serde_json::from_str(&content)?;
53    println!("Addon '{}' is already up to date", addon_id);
54    return Ok(manifest);
55  }
56
57  {
58    let mut guard = ctx.cleanup_state.lock().unwrap_or_else(|e| e.into_inner());
59    *guard = Some(dest.clone());
60  }
61
62  download_and_extract(&ctx.client, &info.archive_url, &dest, None).await?;
63
64  {
65    let mut guard = ctx.cleanup_state.lock().unwrap_or_else(|e| e.into_inner());
66    *guard = None;
67  }
68
69  let manifest_path = dest.join("oxide.addon.json");
70  let content = std::fs::read_to_string(&manifest_path)?;
71  let manifest: AddonManifest = serde_json::from_str(&content)?;
72
73  update_addons_cache(&ctx.paths.addons, addon_id, &manifest, &info.commit_sha)?;
74  println!("Addon '{}' successfully downloaded", addon_id);
75
76  Ok(manifest)
77}
78
79pub fn read_cached_manifest(addons_dir: &Path, addon_id: &str) -> Result<AddonManifest> {
80  let manifest_path = addons_dir.join(addon_id).join("oxide.addon.json");
81  let content = std::fs::read_to_string(&manifest_path)?;
82  Ok(serde_json::from_str(&content)?)
83}