1use crate::{
5 diff::{changed_sort_key, PackageDiff, SummaryDiff, SummaryDiffStatus},
6 SummaryId,
7};
8use std::fmt;
9
10#[derive(Clone, Debug)]
14pub struct SummaryReport<'a, 'b> {
15 diff: &'b SummaryDiff<'a>,
16 sorted_target: Vec<(&'a SummaryId, &'b SummaryDiffStatus<'a>)>,
17 sorted_host: Vec<(&'a SummaryId, &'b SummaryDiffStatus<'a>)>,
18}
19
20impl<'a, 'b> SummaryReport<'a, 'b> {
21 pub fn new(diff: &'b SummaryDiff<'a>) -> Self {
23 let sorted_target = Self::make_sorted(&diff.target_packages);
24 let sorted_host = Self::make_sorted(&diff.host_packages);
25
26 Self {
27 diff,
28 sorted_target,
29 sorted_host,
30 }
31 }
32
33 fn make_sorted(
34 packages: &'b PackageDiff<'a>,
35 ) -> Vec<(&'a SummaryId, &'b SummaryDiffStatus<'a>)> {
36 let mut v: Vec<_> = packages
37 .changed
38 .iter()
39 .map(|(summary_id, status)| (*summary_id, status))
40 .collect();
41 v.sort_by_key(|(summary_id, status)| changed_sort_key(summary_id, status));
42
43 v
44 }
45}
46
47impl fmt::Display for SummaryReport<'_, '_> {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 if !self.diff.target_packages.is_unchanged() {
50 writeln!(
51 f,
52 "target packages:\n{}",
53 PackageReport::new(&self.diff.target_packages, &self.sorted_target)
54 )?;
55 }
56 if !self.diff.host_packages.is_unchanged() {
57 writeln!(
58 f,
59 "host packages:\n{}",
60 PackageReport::new(&self.diff.host_packages, &self.sorted_host)
61 )?;
62 }
63
64 Ok(())
65 }
66}
67
68struct PackageReport<'x> {
71 package_diff: &'x PackageDiff<'x>,
72 sorted: &'x [(&'x SummaryId, &'x SummaryDiffStatus<'x>)],
73}
74
75impl<'x> PackageReport<'x> {
76 fn new(
77 package_diff: &'x PackageDiff<'x>,
78 sorted: &'x [(&'x SummaryId, &'x SummaryDiffStatus<'x>)],
79 ) -> Self {
80 Self {
81 package_diff,
82 sorted,
83 }
84 }
85}
86
87impl fmt::Display for PackageReport<'_> {
88 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
89 for (summary_id, status) in self.sorted {
90 write!(
91 f,
92 " {} {} {} ({}, {})",
93 status.tag(),
94 summary_id.name,
95 summary_id.version,
96 status.latest_status(),
97 summary_id.source
98 )?;
99
100 if let Some(unchanged_list) = self.package_diff.unchanged.get(summary_id.name.as_str())
102 {
103 write!(f, " (other versions: ")?;
104 display_list(f, unchanged_list.iter().map(|(version, _, _)| *version))?;
105 write!(f, ")")?;
106 }
107
108 writeln!(f)?;
109
110 match status {
111 SummaryDiffStatus::Added { info } => {
112 write!(f, " * features: ")?;
113 display_list(f, &info.features)?;
114 writeln!(f)?;
115 }
116 SummaryDiffStatus::Removed { old_info } => {
117 write!(f, " * (old features: ")?;
118 display_list(f, &old_info.features)?;
119 writeln!(f, ")")?;
120 }
121 SummaryDiffStatus::Modified {
122 old_version,
123 old_source,
124 old_status,
125 new_status: _,
127 added_features,
128 removed_features,
129 unchanged_features,
130 added_optional_deps,
131 removed_optional_deps,
132 unchanged_optional_deps,
133 } => {
134 if let Some(old_version) = old_version {
135 let change_str = if summary_id.version > **old_version {
136 "upgraded"
137 } else {
138 "DOWNGRADED"
139 };
140 writeln!(f, " * version {} from {}", change_str, old_version)?;
141 }
142 if let Some(old_source) = old_source {
143 writeln!(f, " * source changed from {}", old_source)?;
144 }
145 if let Some(old_status) = old_status {
146 writeln!(f, " * status changed from {}", old_status)?;
147 }
148
149 if !added_features.is_empty() {
152 write!(f, " * added features: ")?;
153 display_list(f, added_features.iter().copied())?;
154 writeln!(f)?;
155 }
156 if !removed_features.is_empty() {
157 write!(f, " * removed features: ")?;
158 display_list(f, removed_features.iter().copied())?;
159 writeln!(f)?;
160 }
161 write!(f, " * (unchanged features: ")?;
162 display_list(f, unchanged_features.iter().copied())?;
163 writeln!(f, ")")?;
164
165 if !added_optional_deps.is_empty() {
168 write!(f, " * added optional dependencies: ")?;
169 display_list(f, added_optional_deps.iter().copied())?;
170 writeln!(f)?;
171 }
172 if !removed_optional_deps.is_empty() {
173 write!(f, " * removed optional dependencies: ")?;
174 display_list(f, removed_optional_deps.iter().copied())?;
175 writeln!(f)?;
176 }
177 write!(f, " * (unchanged optional dependencies: ")?;
178 display_list(f, unchanged_optional_deps.iter().copied())?;
179 writeln!(f, ")")?;
180 }
181 }
182 }
183
184 Ok(())
185 }
186}
187
188fn display_list<I>(f: &mut fmt::Formatter, items: I) -> fmt::Result
189where
190 I: IntoIterator,
191 I::Item: fmt::Display,
192 I::IntoIter: ExactSizeIterator,
193{
194 let items = items.into_iter();
195 let len = items.len();
196 if len == 0 {
197 write!(f, "[none]")?;
198 }
199
200 for (idx, item) in items.enumerate() {
201 write!(f, "{}", item)?;
202 if idx + 1 < len {
204 write!(f, ", ")?;
205 }
206 }
207
208 Ok(())
209}