Skip to main content

vs_plugin_api/
model.rs

1//! Core plugin manifest and installation data model.
2
3use std::collections::BTreeMap;
4use std::path::{Path, PathBuf};
5
6use serde::{Deserialize, Serialize};
7
8/// Supported plugin backend implementations.
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
10#[serde(rename_all = "kebab-case")]
11pub enum PluginBackendKind {
12    /// Lua-compatible plugin backend.
13    Lua,
14    /// Native plugin backend modeled after a WASI component contract.
15    Wasi,
16}
17
18/// Registry metadata for a plugin.
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20pub struct PluginManifest {
21    /// Stable plugin identifier.
22    pub name: String,
23    /// Runtime backend used to execute hooks.
24    pub backend: PluginBackendKind,
25    /// Local source directory of the plugin.
26    pub source: PathBuf,
27    /// Optional human readable description.
28    #[serde(default)]
29    pub description: Option<String>,
30    /// Alternative names accepted by the CLI.
31    #[serde(default)]
32    pub aliases: Vec<String>,
33    /// Plugin runtime version.
34    #[serde(default)]
35    pub version: Option<String>,
36    /// Plugin homepage or repository.
37    #[serde(default)]
38    pub homepage: Option<String>,
39    /// Plugin license identifier.
40    #[serde(default)]
41    pub license: Option<String>,
42    /// Plugin update URL.
43    #[serde(default)]
44    pub update_url: Option<String>,
45    /// Plugin manifest URL.
46    #[serde(default)]
47    pub manifest_url: Option<String>,
48    /// Minimum runtime version required by the plugin.
49    #[serde(default)]
50    pub min_runtime_version: Option<String>,
51    /// Additional plugin notes.
52    #[serde(default)]
53    pub notes: Vec<String>,
54    /// Legacy file names handled by the plugin.
55    #[serde(default)]
56    pub legacy_filenames: Vec<String>,
57}
58
59/// A version published by a plugin.
60#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
61pub struct AvailableVersion {
62    /// Version string as understood by the plugin.
63    pub version: String,
64    /// Optional human-readable note.
65    #[serde(default)]
66    pub note: Option<String>,
67    /// Additional packages associated with the version.
68    #[serde(default)]
69    pub additions: Vec<AvailableAddition>,
70}
71
72/// An additional package listed next to an available version.
73#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
74pub struct AvailableAddition {
75    /// Package name.
76    pub name: String,
77    /// Package version.
78    pub version: String,
79    /// Optional note.
80    #[serde(default)]
81    pub note: Option<String>,
82}
83
84/// An environment key emitted by a plugin.
85#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
86pub struct EnvKey {
87    /// Environment variable name.
88    pub key: String,
89    /// Environment variable value.
90    pub value: String,
91}
92
93/// A checksum used to verify a downloaded artifact.
94#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
95pub struct Checksum {
96    /// Checksum algorithm.
97    pub algorithm: String,
98    /// Checksum value.
99    pub value: String,
100}
101
102/// An artifact source referenced by an install plan.
103#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
104#[serde(tag = "kind", rename_all = "kebab-case")]
105pub enum InstallSource {
106    /// A local directory that should be copied recursively.
107    Directory { path: PathBuf },
108    /// A local file that may be moved or extracted.
109    File { path: PathBuf },
110    /// A remote URL that should be downloaded.
111    Url {
112        url: String,
113        #[serde(default)]
114        headers: BTreeMap<String, String>,
115    },
116}
117
118/// An artifact returned by a plugin install hook.
119#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
120pub struct InstallArtifact {
121    /// Artifact name.
122    pub name: String,
123    /// Artifact version.
124    pub version: String,
125    /// Artifact source.
126    pub source: InstallSource,
127    /// Optional note.
128    #[serde(default)]
129    pub note: Option<String>,
130    /// Optional checksum.
131    #[serde(default)]
132    pub checksum: Option<Checksum>,
133}
134
135/// Installation plan returned by a plugin.
136#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
137pub struct InstallPlan {
138    /// Plugin name used for diagnostics.
139    pub plugin: String,
140    /// Version that will be installed.
141    pub version: String,
142    /// Primary runtime artifact.
143    pub main: InstallArtifact,
144    /// Additional artifacts.
145    #[serde(default)]
146    pub additions: Vec<InstallArtifact>,
147    /// Legacy file names understood by the plugin.
148    #[serde(default)]
149    pub legacy_filenames: Vec<String>,
150}
151
152/// An installed artifact on disk.
153#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
154pub struct InstalledArtifact {
155    /// Artifact name.
156    pub name: String,
157    /// Artifact version.
158    pub version: String,
159    /// Installed path.
160    pub path: PathBuf,
161    /// Optional note.
162    #[serde(default)]
163    pub note: Option<String>,
164}
165
166/// Fully installed runtime layout for a plugin version.
167#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
168pub struct InstalledRuntime {
169    /// Plugin identifier.
170    pub plugin: String,
171    /// Installed version.
172    pub version: String,
173    /// Version root directory.
174    pub root_dir: PathBuf,
175    /// Main installed artifact.
176    pub main: InstalledArtifact,
177    /// Additional installed artifacts.
178    #[serde(default)]
179    pub additions: Vec<InstalledArtifact>,
180}
181
182impl InstalledRuntime {
183    /// Returns the main runtime path.
184    pub fn main_path(&self) -> &Path {
185        &self.main.path
186    }
187
188    /// Returns a copy of this runtime with all paths relocated under
189    /// `new_root` instead of the original `root_dir`.  This is used to
190    /// make env-keys point at scope-specific symlinks rather than the raw
191    /// cache directory.
192    pub fn relocate(&self, new_root: &Path) -> Self {
193        let relocate_path = |p: &Path| -> PathBuf {
194            p.strip_prefix(&self.root_dir)
195                .map(|rel| new_root.join(rel))
196                .unwrap_or_else(|_| p.to_path_buf())
197        };
198        Self {
199            plugin: self.plugin.clone(),
200            version: self.version.clone(),
201            root_dir: new_root.to_path_buf(),
202            main: InstalledArtifact {
203                path: relocate_path(&self.main.path),
204                ..self.main.clone()
205            },
206            additions: self
207                .additions
208                .iter()
209                .map(|a| InstalledArtifact {
210                    path: relocate_path(&a.path),
211                    ..a.clone()
212                })
213                .collect(),
214        }
215    }
216}