1use crate::PackageId;
2use serde::{Deserialize, Serialize};
3use std::fmt::{Debug, Formatter};
4use std::{
5 collections::{HashMap, HashSet},
6 ops::{Add, AddAssign},
7 path::PathBuf,
8};
9
10fn debug_fmt_set(
11 f: &mut Formatter<'_>,
12 set: &HashSet<impl Debug>,
13) -> std::fmt::Result {
14 let mut strings: Vec<String> =
15 set.iter().map(|v| format!("{v:?}")).collect();
16 strings.sort();
17 write!(f, "{{ {} }}, ", strings.join(", "))
18}
19
20fn debug_fmt_map(
21 f: &mut Formatter<'_>,
22 set: &HashMap<impl Debug, impl Debug>,
23) -> std::fmt::Result {
24 let mut strings: Vec<String> =
25 set.iter().map(|(k, v)| format!("{k:?}: {v:?}")).collect();
26 strings.sort();
27 write!(f, "{{ {} }}, ", strings.join(", "))
28}
29
30#[derive(Clone, Deserialize, Eq, PartialEq, Serialize)]
32pub struct PackageInfo {
33 pub id: PackageId,
34 #[serde(serialize_with = "set_serde::serialize")]
35 pub dependencies: HashSet<PackageId>,
36 #[serde(serialize_with = "set_serde::serialize")]
37 pub dev_dependencies: HashSet<PackageId>,
38 #[serde(serialize_with = "set_serde::serialize")]
39 pub build_dependencies: HashSet<PackageId>,
40}
41impl Debug for PackageInfo {
42 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
43 write!(f, "PackageInfo {{ id: {:?}", self.id)?;
44 write!(f, ", dependencies: ")?;
45 debug_fmt_set(f, &self.dependencies)?;
46 write!(f, ", dev_dependencies: ")?;
47 debug_fmt_set(f, &self.dev_dependencies)?;
48 write!(f, ", build_dependencies: ")?;
49 debug_fmt_set(f, &self.build_dependencies)?;
50 write!(f, " }}")
51 }
52}
53
54impl PackageInfo {
55 pub fn new(id: PackageId) -> Self {
56 PackageInfo {
57 id,
58 dependencies: Default::default(),
59 dev_dependencies: Default::default(),
60 build_dependencies: Default::default(),
61 }
62 }
63
64 pub fn add_dependency(&mut self, dep: PackageId, kind: DependencyKind) {
65 match kind {
66 DependencyKind::Normal => self.dependencies.insert(dep),
67 DependencyKind::Development => self.dev_dependencies.insert(dep),
68 DependencyKind::Build => self.build_dependencies.insert(dep),
69 };
70 }
71}
72
73#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
75pub struct QuickReportEntry {
76 pub package: PackageInfo,
77 pub forbids_unsafe: bool,
79}
80
81#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
83pub struct QuickSafetyReport {
84 #[serde(with = "entry_serde")]
86 pub packages: HashMap<PackageId, QuickReportEntry>,
87 #[serde(serialize_with = "set_serde::serialize")]
89 pub packages_without_metrics: HashSet<PackageId>,
90}
91
92#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
94pub struct ReportEntry {
95 pub package: PackageInfo,
96 pub unsafety: UnsafeInfo,
98}
99
100#[derive(Clone, Default, Deserialize, Eq, PartialEq, Serialize)]
102pub struct SafetyReport {
103 #[serde(with = "entry_serde")]
104 pub packages: HashMap<PackageId, ReportEntry>,
105 #[serde(serialize_with = "set_serde::serialize")]
106 pub packages_without_metrics: HashSet<PackageId>,
107 #[serde(serialize_with = "set_serde::serialize")]
108 pub used_but_not_scanned_files: HashSet<PathBuf>,
109}
110impl Debug for SafetyReport {
111 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
112 write!(f, "SafetyReport {{ packages: ")?;
113 debug_fmt_map(f, &self.packages)?;
114 write!(f, ", packages_without_metrics: ")?;
115 debug_fmt_set(f, &self.packages_without_metrics)?;
116 write!(f, ", used_but_not_scanned_files: ")?;
117 debug_fmt_set(f, &self.used_but_not_scanned_files)?;
118 write!(f, " }}")
119 }
120}
121
122#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
124pub struct UnsafeInfo {
125 pub used: CounterBlock,
127 pub unused: CounterBlock,
129 pub forbids_unsafe: bool,
131}
132
133#[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
135pub enum DependencyKind {
136 Normal,
138 Development,
140 Build,
142}
143
144#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
146pub struct Count {
147 pub safe: u64,
149 pub unsafe_: u64,
151}
152
153impl Count {
154 pub fn count(&mut self, is_unsafe: bool) {
156 if is_unsafe {
157 self.unsafe_ += 1;
158 } else {
159 self.safe += 1;
160 }
161 }
162}
163
164impl Add for Count {
165 type Output = Count;
166
167 fn add(self, other: Count) -> Count {
168 Count {
169 safe: self.safe + other.safe,
170 unsafe_: self.unsafe_ + other.unsafe_,
171 }
172 }
173}
174
175impl AddAssign for Count {
176 fn add_assign(&mut self, rhs: Count) {
177 *self = self.clone() + rhs;
178 }
179}
180
181#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
183pub struct CounterBlock {
184 pub functions: Count,
185 pub exprs: Count,
186 pub item_impls: Count,
187 pub item_traits: Count,
188 pub methods: Count,
189}
190
191impl CounterBlock {
192 pub fn has_unsafe(&self) -> bool {
193 self.functions.unsafe_ > 0
194 || self.exprs.unsafe_ > 0
195 || self.item_impls.unsafe_ > 0
196 || self.item_traits.unsafe_ > 0
197 || self.methods.unsafe_ > 0
198 }
199}
200
201impl Add for CounterBlock {
202 type Output = CounterBlock;
203
204 fn add(self, other: CounterBlock) -> CounterBlock {
205 CounterBlock {
206 functions: self.functions + other.functions,
207 exprs: self.exprs + other.exprs,
208 item_impls: self.item_impls + other.item_impls,
209 item_traits: self.item_traits + other.item_traits,
210 methods: self.methods + other.methods,
211 }
212 }
213}
214
215impl AddAssign for CounterBlock {
216 fn add_assign(&mut self, rhs: Self) {
217 *self = self.clone() + rhs;
218 }
219}
220
221trait Entry {
222 fn package_id(&self) -> &PackageId;
223}
224
225impl Entry for ReportEntry {
226 fn package_id(&self) -> &PackageId {
227 &self.package.id
228 }
229}
230
231impl Entry for QuickReportEntry {
232 fn package_id(&self) -> &PackageId {
233 &self.package.id
234 }
235}
236
237mod entry_serde {
238 use crate::PackageId;
239 use serde::{
240 ser::SerializeSeq, Deserialize, Deserializer, Serialize, Serializer,
241 };
242 use std::{collections::HashMap, fmt, marker::PhantomData};
243
244 pub(super) fn serialize<T, S>(
245 map: &HashMap<PackageId, T>,
246 serializer: S,
247 ) -> Result<S::Ok, S::Error>
248 where
249 T: Serialize + super::Entry,
250 S: Serializer,
251 {
252 let mut values = map.values().collect::<Vec<_>>();
253 values.sort_by(|a, b| a.package_id().cmp(b.package_id()));
254 let mut seq = serializer.serialize_seq(Some(values.len()))?;
255 for value in values {
256 seq.serialize_element(value)?;
257 }
258 seq.end()
259 }
260
261 pub(super) fn deserialize<'de, T, D>(
262 deserializer: D,
263 ) -> Result<HashMap<PackageId, T>, D::Error>
264 where
265 T: Deserialize<'de> + super::Entry,
266 D: Deserializer<'de>,
267 {
268 struct Visitor<U>(PhantomData<fn() -> U>);
269
270 impl<'d, U> serde::de::Visitor<'d> for Visitor<U>
271 where
272 U: Deserialize<'d> + super::Entry,
273 {
274 type Value = HashMap<PackageId, U>;
275
276 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
277 f.write_str("a sequence")
278 }
279
280 fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
281 where
282 A: serde::de::SeqAccess<'d>,
283 {
284 let mut map = HashMap::new();
285 while let Some(item) = seq.next_element::<U>()? {
286 map.insert(item.package_id().clone(), item);
287 }
288 Ok(map)
289 }
290 }
291
292 deserializer.deserialize_seq(Visitor(PhantomData))
293 }
294}
295
296mod set_serde {
297 use serde::{ser::SerializeSeq, Serialize, Serializer};
298 use std::collections::HashSet;
299
300 pub(super) fn serialize<T, S>(
301 set: &HashSet<T>,
302 serializer: S,
303 ) -> Result<S::Ok, S::Error>
304 where
305 T: Serialize + Ord,
306 S: Serializer,
307 {
308 let mut values = set.iter().collect::<Vec<_>>();
309 values.sort();
310 let mut seq = serializer.serialize_seq(Some(values.len()))?;
311 for value in values {
312 seq.serialize_element(value)?;
313 }
314 seq.end()
315 }
316}