1use anyhow::Result;
2use flate2::read::GzDecoder;
3use spin_common::data_dir::data_dir;
4use std::{
5 fs::{self, File},
6 path::{Path, PathBuf},
7};
8use tar::Archive;
9
10use crate::{Catalogue, manifest::PluginManifest};
11
12pub const PLUGIN_MANIFESTS_DIRECTORY_NAME: &str = "manifests";
14const INSTALLATION_RECORD_FILE_NAME: &str = ".install.json";
15
16const LOCAL_SNAPSHOT_PATH_IN_STORE: &str = ".spin-plugins";
19
20pub struct PluginStore {
22 root: PathBuf,
23}
24
25impl PluginStore {
26 pub fn new(root: impl Into<PathBuf>) -> Self {
27 Self { root: root.into() }
28 }
29
30 pub fn try_default() -> Result<Self> {
31 Ok(Self::new(data_dir()?.join("plugins")))
32 }
33
34 pub fn get_plugins_directory(&self) -> &Path {
36 &self.root
37 }
38
39 pub fn plugin_subdirectory_path(&self, plugin_name: &str) -> PathBuf {
41 self.root.join(plugin_name)
42 }
43
44 pub fn installed_binary_path(&self, plugin_name: &str) -> PathBuf {
45 let mut binary = self.root.join(plugin_name).join(plugin_name);
46 if cfg!(target_os = "windows") {
47 binary.set_extension("exe");
48 }
49 binary
50 }
51
52 pub fn installed_manifests_directory(&self) -> PathBuf {
55 self.root.join(PLUGIN_MANIFESTS_DIRECTORY_NAME)
56 }
57
58 pub fn installed_manifest_path(&self, plugin_name: &str) -> PathBuf {
59 self.installed_manifests_directory()
60 .join(crate::util::manifest_file_name(plugin_name))
61 }
62
63 pub fn installation_record_file(&self, plugin_name: &str) -> PathBuf {
64 self.root
65 .join(plugin_name)
66 .join(INSTALLATION_RECORD_FILE_NAME)
67 }
68
69 pub(crate) fn catalogue(&self) -> Catalogue {
70 let git_root = self.root.join(LOCAL_SNAPSHOT_PATH_IN_STORE);
71 Catalogue::new(git_root)
72 }
73
74 pub(crate) fn add_manifest(&self, plugin_manifest: &PluginManifest) -> Result<()> {
75 let manifests_dir = self.installed_manifests_directory();
76 std::fs::create_dir_all(manifests_dir)?;
77 serde_json::to_writer(
78 &File::create(self.installed_manifest_path(&plugin_manifest.name()))?,
79 plugin_manifest,
80 )?;
81 tracing::trace!("Added manifest for {}", &plugin_manifest.name());
82 Ok(())
83 }
84
85 pub(crate) fn untar_plugin(&self, plugin_file_name: &PathBuf, plugin_name: &str) -> Result<()> {
86 let tar_gz = File::open(plugin_file_name)?;
88 let tar = GzDecoder::new(tar_gz);
90 let mut archive = Archive::new(tar);
92 archive.set_preserve_permissions(true);
93 let plugin_sub_dir = self.plugin_subdirectory_path(plugin_name);
95 fs::remove_dir_all(&plugin_sub_dir).ok();
96 fs::create_dir_all(&plugin_sub_dir)?;
97 archive.unpack(&plugin_sub_dir)?;
98 Ok(())
99 }
100}