1use std::fmt::Display;
2
3use bon::{Builder, builder};
4use num_enum::{FromPrimitive, IntoPrimitive};
5use oma_utils::human_bytes::HumanBytes;
6use serde::{Deserialize, Serialize};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct OmaOperation {
10 pub install: Vec<InstallEntry>,
11 pub remove: Vec<RemoveEntry>,
12 pub disk_size_delta: i64,
13 pub autoremovable: (u64, u64),
14 pub total_download_size: u64,
15 pub suggest: Vec<(String, String)>,
16 pub recommend: Vec<(String, String)>,
17}
18
19impl Display for OmaOperation {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 let mut install = vec![];
22 let mut upgrade = vec![];
23 let mut reinstall = vec![];
24 let mut downgrade = vec![];
25 let mut remove = vec![];
26 let mut purge = vec![];
27
28 for ins in &self.install {
29 let name = ins.name();
30 let arch = ins.arch();
31 let version = ins.new_version();
32 match ins.op() {
33 InstallOperation::Default | InstallOperation::Download => unreachable!(),
34 InstallOperation::Install => {
35 if !ins.automatic() {
36 install.push(format!("{name}:{arch} ({version})"));
37 } else {
38 install.push(format!("{name}:{arch} ({version}, automatic)"));
39 }
40 }
41 InstallOperation::ReInstall => {
42 reinstall.push(format!("{name}:{arch} ({version})"));
43 }
44 InstallOperation::Upgrade => {
45 upgrade.push(format!(
47 "{name}:{arch} ({}, {version})",
48 ins.old_version().unwrap()
49 ));
50 }
51 InstallOperation::Downgrade => {
52 downgrade.push(format!("{name}:{arch} ({version})"));
53 }
54 }
55 }
56
57 for rm in &self.remove {
58 let tags = rm.details();
59 let name = rm.name();
60 let version = rm.version();
61 let arch = rm.arch();
62
63 let mut s = format!("{name}:{arch}");
64 if let Some(ver) = version {
65 s.push_str(&format!(" ({ver})"));
66 }
67
68 if tags.contains(&RemoveTag::Purge) {
69 purge.push(s);
70 } else {
71 remove.push(s);
72 }
73 }
74
75 if !install.is_empty() {
76 writeln!(f, "Install: {}", install.join(", "))?;
77 }
78
79 if !upgrade.is_empty() {
80 writeln!(f, "Upgrade: {}", upgrade.join(", "))?;
81 }
82
83 if !reinstall.is_empty() {
84 writeln!(f, "ReInstall: {}", reinstall.join(", "))?;
85 }
86
87 if !downgrade.is_empty() {
88 writeln!(f, "Downgrade: {}", downgrade.join(", "))?;
89 }
90
91 if !remove.is_empty() {
92 writeln!(f, "Remove: {}", remove.join(", "))?;
93 }
94
95 if !purge.is_empty() {
96 writeln!(f, "Purge: {}", purge.join(", "))?;
97 }
98
99 let (symbol, n) = if self.disk_size_delta >= 0 {
100 ("+", self.disk_size_delta as u64)
101 } else {
102 ("-", (0 - self.disk_size_delta) as u64)
103 };
104
105 writeln!(f, "Size-delta: {symbol}{}", HumanBytes(n.to_owned()))?;
106
107 Ok(())
108 }
109}
110
111#[derive(Debug, PartialEq, Eq, Hash, Clone, Default, Serialize, Deserialize, Builder)]
112pub struct InstallEntry {
113 name: String,
114 name_without_arch: String,
115 old_version: Option<String>,
116 new_version: String,
117 old_size: Option<u64>,
118 new_size: u64,
119 pkg_urls: Vec<PackageUrl>,
120 sha256: Option<String>,
121 md5: Option<String>,
122 sha512: Option<String>,
123 arch: String,
124 download_size: u64,
125 op: InstallOperation,
126 #[builder(default)]
127 automatic: bool,
128 index: usize,
129}
130
131#[derive(Debug, PartialEq, Eq, Hash, Clone, Default, Serialize, Deserialize, Builder)]
132pub struct PackageUrl {
133 pub download_url: String,
134 pub index_url: String,
135}
136
137#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
138pub struct RemoveEntry {
139 name: String,
140 version: Option<String>,
141 size: u64,
142 details: Vec<RemoveTag>,
143 arch: String,
144 index: usize,
145}
146
147#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
148pub enum RemoveTag {
149 Purge,
150 AutoRemove,
151 Resolver,
152}
153
154#[derive(
155 Debug,
156 PartialEq,
157 Eq,
158 Hash,
159 Clone,
160 Default,
161 Serialize,
162 Deserialize,
163 IntoPrimitive,
164 FromPrimitive,
165 Copy,
166)]
167#[repr(u8)]
168pub enum InstallOperation {
169 #[default]
170 Default = 0,
171 Install,
172 ReInstall,
173 Upgrade,
174 Downgrade,
175 Download,
176}
177
178impl InstallEntry {
179 pub fn name(&self) -> &str {
180 &self.name
181 }
182
183 pub fn name_without_arch(&self) -> &str {
184 &self.name_without_arch
185 }
186
187 pub fn old_size(&self) -> Option<u64> {
188 self.old_size
189 }
190
191 pub fn new_size(&self) -> u64 {
192 self.new_size
193 }
194
195 pub fn old_version(&self) -> Option<&str> {
196 self.old_version.as_deref()
197 }
198
199 pub fn new_version(&self) -> &str {
200 &self.new_version
201 }
202
203 pub fn pkg_urls(&self) -> &[PackageUrl] {
204 &self.pkg_urls
205 }
206
207 pub fn sha256(&self) -> Option<&str> {
208 self.sha256.as_deref()
209 }
210
211 pub fn md5(&self) -> Option<&str> {
212 self.md5.as_deref()
213 }
214
215 pub fn sha512(&self) -> Option<&str> {
216 self.sha512.as_deref()
217 }
218
219 pub fn arch(&self) -> &str {
220 &self.arch
221 }
222
223 pub fn download_size(&self) -> u64 {
224 self.download_size
225 }
226
227 pub fn op(&self) -> &InstallOperation {
228 &self.op
229 }
230
231 pub fn automatic(&self) -> bool {
232 self.automatic
233 }
234
235 pub fn index(&self) -> usize {
236 self.index
237 }
238}
239
240impl RemoveEntry {
241 pub fn new(
242 name: String,
243 version: Option<String>,
244 size: u64,
245 details: Vec<RemoveTag>,
246 arch: String,
247 index: usize,
248 ) -> Self {
249 Self {
250 name,
251 version,
252 size,
253 details,
254 arch,
255 index,
256 }
257 }
258
259 pub fn name(&self) -> &str {
260 &self.name
261 }
262
263 pub fn version(&self) -> Option<&str> {
264 self.version.as_deref()
265 }
266
267 pub fn size(&self) -> u64 {
268 self.size
269 }
270
271 pub fn details(&self) -> &[RemoveTag] {
272 &self.details
273 }
274
275 pub fn arch(&self) -> &str {
276 &self.arch
277 }
278
279 pub fn index(&self) -> usize {
280 self.index
281 }
282}