1use crate::deps::{DependencyConfig, SystemDependency};
2use crate::env::*;
3use crate::error::Error;
4use crate::pm::*;
5use crate::pm_vendor::*;
6
7#[derive(Debug)]
10pub struct System {
11 pub arch: SystemArch,
13
14 pub manager: Option<SystemPackageManager>,
16
17 pub os: SystemOS,
19}
20
21impl Default for System {
22 fn default() -> Self {
23 Self {
24 arch: SystemArch::from_env(),
25 manager: SystemPackageManager::detect().ok(),
26 os: SystemOS::from_env(),
27 }
28 }
29}
30
31impl System {
32 pub fn new() -> Result<Self, Error> {
34 Ok(Self::with_manager(SystemPackageManager::detect()?))
35 }
36
37 pub fn with_manager(manager: SystemPackageManager) -> Self {
39 Self {
40 arch: SystemArch::from_env(),
41 manager: Some(manager),
42 os: SystemOS::from_env(),
43 }
44 }
45
46 pub fn get_install_package_command(
53 &self,
54 dep_config: &DependencyConfig,
55 interactive: bool,
56 ) -> Result<Option<Vec<String>>, Error> {
57 let Some(base_args) = self.extract_command(CommandType::InstallPackage, interactive)?
58 else {
59 return Ok(None);
60 };
61
62 let mut args = vec![];
63 let pm = self.manager.as_ref().unwrap();
64 let pm_config = pm.get_config();
65
66 for arg in base_args {
67 if arg == "$" {
68 args.extend(self.extract_package_args(dep_config, &pm_config, pm)?);
69 } else {
70 args.push(arg);
71 }
72 }
73
74 Ok(Some(args))
75 }
76
77 pub fn get_install_packages_command(
84 &self,
85 dep_configs: &[DependencyConfig],
86 interactive: bool,
87 ) -> Result<Option<Vec<String>>, Error> {
88 let Some(base_args) = self.extract_command(CommandType::InstallPackage, interactive)?
89 else {
90 return Ok(None);
91 };
92
93 let mut args = vec![];
94 let pm = self.manager.as_ref().unwrap();
95 let pm_config = pm.get_config();
96
97 for arg in base_args {
98 if arg == "$" {
99 for dep_config in dep_configs {
100 args.extend(self.extract_package_args(dep_config, &pm_config, pm)?);
101 }
102 } else {
103 args.push(arg);
104 }
105 }
106
107 Ok(Some(args))
108 }
109
110 pub fn get_list_packages_command(
113 &self,
114 interactive: bool,
115 ) -> Result<Option<Vec<String>>, Error> {
116 self.extract_command(CommandType::ListPackages, interactive)
117 }
118
119 pub fn get_update_index_command(
122 &self,
123 interactive: bool,
124 ) -> Result<Option<Vec<String>>, Error> {
125 self.extract_command(CommandType::UpdateIndex, interactive)
126 }
127
128 pub fn resolve_dependencies(&self, deps: &[SystemDependency]) -> Vec<DependencyConfig> {
131 let mut configs = vec![];
132
133 for dep in deps {
134 let config = dep.to_config();
135
136 if config.os.as_ref().is_some_and(|o| o != &self.os) {
137 continue;
138 }
139
140 if config.arch.as_ref().is_some_and(|a| a != &self.arch) {
141 continue;
142 }
143
144 if let Some(pm) = &self.manager {
145 if config.manager.as_ref().is_some_and(|m| m != pm) {
146 continue;
147 }
148 } else if config.manager.is_some() {
149 continue;
150 }
151
152 configs.push(config);
153 }
154
155 configs
156 }
157
158 fn append_interactive(
159 &self,
160 command: CommandType,
161 config: &PackageManagerConfig,
162 args: &mut Vec<String>,
163 interactive: bool,
164 ) {
165 if config.prompt_for.contains(&command) {
166 match &config.prompt_arg {
167 PromptArgument::None => {}
168 PromptArgument::Interactive(i) => {
169 if interactive {
170 args.push(i.to_owned());
171 }
172 }
173 PromptArgument::Skip(y) => {
174 if !interactive {
175 args.push(y.to_owned());
176 }
177 }
178 };
179 }
180 }
181
182 fn extract_command(
183 &self,
184 command: CommandType,
185 interactive: bool,
186 ) -> Result<Option<Vec<String>>, Error> {
187 let Some(pm) = self.manager else {
188 return Err(Error::RequiredPackageManager);
189 };
190
191 let pm_config = pm.get_config();
192
193 if let Some(args) = pm_config.commands.get(&command) {
194 let mut args = args.to_owned();
195
196 self.append_interactive(command, &pm_config, &mut args, interactive);
197
198 return Ok(Some(args));
199 }
200
201 Ok(None)
202 }
203
204 fn extract_package_args(
205 &self,
206 dep_config: &DependencyConfig,
207 pm_config: &PackageManagerConfig,
208 pm: &SystemPackageManager,
209 ) -> Result<Vec<String>, Error> {
210 let mut args = vec![];
211
212 for dep in dep_config.get_package_names(pm)? {
213 if let Some(ver) = &dep_config.version {
214 match &pm_config.version_arg {
215 VersionArgument::None => {
216 args.push(dep);
217 }
218 VersionArgument::Inline(op) => {
219 args.push(format!("{dep}{op}{ver}"));
220 }
221 VersionArgument::Separate(opt) => {
222 args.push(dep);
223 args.push(opt.to_owned());
224 args.push(ver.to_owned());
225 }
226 };
227 } else {
228 args.push(dep);
229 }
230 }
231
232 Ok(args)
233 }
234}