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