vs_core/service/
update.rs1use vs_plugin_api::PluginBackendKind;
2use vs_registry::RegistryEntry;
3
4use crate::plugin_source::is_remote_source;
5use crate::registry_source::{
6 fetch_plugin_manifest, fetch_plugin_manifest_from_url, fetch_url_text,
7 is_remote_registry_source, parse_registry_entries, registry_index_url,
8};
9use crate::{App, CoreError};
10
11impl App {
12 pub fn update_registry(&self) -> Result<usize, CoreError> {
14 let config = self.app_config()?;
15 let source = config.registry.address;
16 if source.is_empty() {
17 return Err(CoreError::Unsupported(String::from(
18 "registry.address is not configured",
19 )));
20 }
21 let registry_source = if is_remote_registry_source(&source) {
22 registry_index_url(&source)
23 } else {
24 source
25 };
26 let mut entries = if is_remote_registry_source(®istry_source) {
27 let content = fetch_url_text(®istry_source)?;
28 parse_registry_entries(&content).map_err(|error| CoreError::RegistrySource {
29 path: registry_source.clone().into(),
30 message: error.to_string(),
31 })?
32 } else {
33 let path = self.normalize_source_path(®istry_source);
34 let content =
35 std::fs::read_to_string(&path).map_err(|error| CoreError::RegistrySource {
36 path: path.clone(),
37 message: error.to_string(),
38 })?;
39 parse_registry_entries(&content).map_err(|error| CoreError::RegistrySource {
40 path: path.clone(),
41 message: error.to_string(),
42 })?
43 };
44
45 if !is_remote_registry_source(®istry_source) {
46 let path = self.normalize_source_path(®istry_source);
47 let base_dir = path.parent().unwrap_or(&self.cwd);
48 for entry in &mut entries {
49 let source_path = std::path::PathBuf::from(&entry.source);
50 if source_path.is_relative() {
51 entry.source = base_dir.join(source_path).display().to_string();
52 }
53 }
54 }
55
56 entries.retain(|entry| self.ensure_backend_supported(entry.backend).is_ok());
57 self.registry.replace_available_plugins(&entries)?;
58 Ok(entries.len())
59 }
60
61 pub fn update_plugin(&self, name: &str) -> Result<RegistryEntry, CoreError> {
63 let entry = self
64 .added_plugins()?
65 .into_iter()
66 .find(|entry| entry.matches(name))
67 .ok_or_else(|| CoreError::UnknownPlugin(name.to_string()))?;
68 let materialized = self.materialize_plugin_entry(&entry)?;
69 let plugin = self.load_plugin(&materialized)?;
70 let manifest = plugin.manifest().clone();
71
72 let mut refreshed = RegistryEntry {
73 name: entry.name.clone(),
74 source: entry.source.clone(),
75 backend: entry.backend,
76 description: manifest.description.clone().or(entry.description.clone()),
77 aliases: entry.aliases.clone(),
78 };
79
80 if let Some(source) =
81 self.resolve_plugin_update_source(&entry, &manifest.name, &manifest)?
82 {
83 refreshed.source = source;
84 }
85
86 let refreshed = if is_remote_source(&refreshed.source) {
87 self.materialize_plugin_entry_with_refresh(&refreshed, true)?
88 } else {
89 self.materialize_plugin_entry(&refreshed)?
90 };
91 self.registry.add_plugin(refreshed.clone())?;
92 Ok(refreshed)
93 }
94
95 pub fn update_all_plugins(&self) -> Result<Vec<RegistryEntry>, CoreError> {
97 let entries = self.added_plugins()?;
98 let mut updated = Vec::new();
99 for entry in entries {
100 updated.push(self.update_plugin(&entry.name)?);
101 }
102 Ok(updated)
103 }
104
105 fn resolve_plugin_update_source(
106 &self,
107 entry: &RegistryEntry,
108 manifest_name: &str,
109 manifest: &vs_plugin_api::PluginManifest,
110 ) -> Result<Option<String>, CoreError> {
111 if let Some(manifest_url) = manifest
112 .manifest_url
113 .as_deref()
114 .or(manifest.update_url.as_deref())
115 {
116 let plugin_manifest = fetch_plugin_manifest_from_url(manifest_url)?;
117 if !plugin_manifest.download_url.is_empty() {
118 return Ok(Some(plugin_manifest.download_url));
119 }
120 }
121
122 if entry.backend == PluginBackendKind::Lua {
123 let config = self.app_config()?;
124 if !config.registry.address.is_empty() {
125 if let Ok(plugin_manifest) =
126 fetch_plugin_manifest(&config.registry.address, manifest_name)
127 {
128 if !plugin_manifest.download_url.is_empty() {
129 return Ok(Some(plugin_manifest.download_url));
130 }
131 }
132 }
133 }
134
135 if is_remote_source(&entry.source) {
136 return Ok(Some(entry.source.clone()));
137 }
138
139 Ok(None)
140 }
141}