1use std::sync::Arc;
4
5use vs_core::{App, CoreError, InstalledVersion, UninstallResult};
6
7use crate::model::{
8 AddSource, ScopeChoice, ToolRow, VersionRow, available_rows, installed_rows, merge_tool_rows,
9};
10
11#[derive(Clone)]
16pub struct AppService {
17 core: Arc<App>,
18}
19
20impl AppService {
21 pub fn new(core: App) -> Self {
22 Self {
23 core: Arc::new(core),
24 }
25 }
26
27 pub fn tool_rows(&self) -> Result<Vec<ToolRow>, CoreError> {
29 let names = self
30 .core
31 .added_plugins()?
32 .into_iter()
33 .map(|entry| entry.name)
34 .collect::<Vec<_>>();
35 let statuses = self.core.current_tool_statuses()?;
36 Ok(merge_tool_rows(names, statuses))
37 }
38
39 pub fn installed_rows(&self, name: &str) -> Result<Vec<VersionRow>, CoreError> {
41 let installed = self
42 .core
43 .installed_versions_for_plugin(name)?
44 .into_iter()
45 .map(|v| v.version)
46 .collect::<Vec<_>>();
47 let current = self.core.current_tool(name)?.map(|c| c.version);
48 Ok(installed_rows(installed, current.as_deref()))
49 }
50
51 pub fn search_available(&self, name: &str, query: &str) -> Result<Vec<VersionRow>, CoreError> {
53 let args: Vec<String> = if query.is_empty() {
54 Vec::new()
55 } else {
56 vec![query.to_string()]
57 };
58 let found = self
59 .core
60 .search_versions(name, &args)?
61 .into_iter()
62 .map(|v| v.version)
63 .collect::<Vec<_>>();
64 let installed = self
65 .core
66 .installed_versions_for_plugin(name)?
67 .into_iter()
68 .map(|v| v.version)
69 .collect::<Vec<_>>();
70 Ok(available_rows(found, &installed))
71 }
72
73 pub fn install_with_progress(
76 &self,
77 name: &str,
78 version: &str,
79 on_progress: &vs_core::ProgressFn<'_>,
80 ) -> Result<InstalledVersion, CoreError> {
81 self.core
82 .install_plugin_version(name, Some(version), Some(on_progress))
83 }
84
85 pub fn use_version(
87 &self,
88 name: &str,
89 version: &str,
90 scope: ScopeChoice,
91 ) -> Result<InstalledVersion, CoreError> {
92 self.core
93 .use_tool(name, version, scope.to_use_scope(), false)
94 }
95
96 pub fn uninstall(&self, name: &str, version: &str) -> Result<UninstallResult, CoreError> {
98 self.core.uninstall_plugin_version(name, version)
99 }
100
101 pub fn registry_plugin_names(&self) -> Result<Vec<String>, CoreError> {
103 Ok(self
104 .core
105 .available_plugins()?
106 .into_iter()
107 .map(|entry| entry.name)
108 .collect())
109 }
110
111 pub fn add(&self, source: AddSource) -> Result<(), CoreError> {
113 match source {
114 AddSource::Registry { name } => {
115 self.core.add_plugin(Some(&name), None, None, None)?;
116 }
117 AddSource::Source {
118 source,
119 alias,
120 backend,
121 } => {
122 self.core.add_plugin(
123 None,
124 Some(source),
125 Some(backend.to_kind()),
126 alias.as_deref(),
127 )?;
128 }
129 }
130 Ok(())
131 }
132
133 pub fn update_plugin(&self, name: &str) -> Result<(), CoreError> {
136 self.core.update_plugin(name)?;
137 Ok(())
138 }
139
140 pub fn remove_plugin(&self, name: &str) -> Result<bool, CoreError> {
142 self.core.remove_plugin(name)
143 }
144
145 pub fn refresh_registry(&self) -> Result<usize, CoreError> {
147 self.core.update_registry()
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
160 fn app_service_is_send_and_sync() {
161 fn assert_send_sync<T: Send + Sync>() {}
162 assert_send_sync::<AppService>();
163 }
164}