typescript_tools/
unpinned_dependencies.rs1use std::{fmt::Display, path::PathBuf};
2
3use crate::types::PackageName;
4
5#[derive(Clone, Debug, Eq, PartialEq, Hash)]
6#[non_exhaustive]
7pub(crate) struct UnpinnedDependency {
8 pub name: PackageName,
9 pub actual: String,
10 pub expected: String,
11}
12
13impl Display for UnpinnedDependency {
14 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
15 write!(
16 f,
17 "dependency: {}\texpected: {}\tgot: {}",
18 self.name, self.expected, self.actual
19 )
20 }
21}
22
23#[derive(Clone, Debug)]
24pub(crate) struct UnpinnedPackageDependencies(PathBuf, Vec<UnpinnedDependency>);
25
26impl Display for UnpinnedPackageDependencies {
27 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28 writeln!(
29 f,
30 "File contains unpinned dependency versions: {:?}",
31 self.0
32 )?;
33 for unpinned_dependency in self.1.iter() {
34 writeln!(f, "\t{}", unpinned_dependency)?;
35 }
36 Ok(())
37 }
38}
39
40impl From<(PathBuf, Vec<UnpinnedDependency>)> for UnpinnedPackageDependencies {
41 fn from(value: (PathBuf, Vec<UnpinnedDependency>)) -> Self {
42 Self(value.0, value.1)
43 }
44}
45
46#[derive(Clone, Debug)]
47pub struct UnpinnedMonorepoDependencies(Vec<UnpinnedPackageDependencies>);
48
49impl Display for UnpinnedMonorepoDependencies {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 for unpinned_package_dependencies in self.0.iter() {
52 write!(f, "{}", unpinned_package_dependencies)?;
53 }
54 Ok(())
55 }
56}
57
58impl From<Vec<UnpinnedPackageDependencies>> for UnpinnedMonorepoDependencies {
59 fn from(value: Vec<UnpinnedPackageDependencies>) -> Self {
60 Self(value)
61 }
62}
63
64impl FromIterator<(PathBuf, Vec<UnpinnedDependency>)> for UnpinnedMonorepoDependencies {
65 fn from_iter<T: IntoIterator<Item = (PathBuf, Vec<UnpinnedDependency>)>>(iter: T) -> Self {
66 let collection = iter
67 .into_iter()
68 .filter(|(_package_name, unpinned_dependencies)| !unpinned_dependencies.is_empty())
69 .map(Into::into)
70 .collect();
71 Self(collection)
72 }
73}
74
75impl UnpinnedMonorepoDependencies {
76 pub fn is_empty(&self) -> bool {
77 self.0.is_empty()
78 }
79}
80
81#[cfg(test)]
82mod tests {
83 use std::path::PathBuf;
84
85 use super::{UnpinnedDependency, UnpinnedMonorepoDependencies, UnpinnedPackageDependencies};
86
87 #[test]
88 fn should_display_all_data() {
89 let unpinned: UnpinnedMonorepoDependencies = vec![UnpinnedPackageDependencies(
90 PathBuf::from("packages/a/package.json"),
91 vec![
92 UnpinnedDependency {
93 name: "one".into(),
94 actual: "0.0.0".into(),
95 expected: "2.0.0".into(),
96 },
97 UnpinnedDependency {
98 name: "two".into(),
99 actual: "0.0.0".into(),
100 expected: "2.0.0".into(),
101 },
102 ],
103 )]
104 .into();
105
106 let expected = r#"
107File contains unpinned dependency versions: "packages/a/package.json"
108 dependency: one expected: 2.0.0 got: 0.0.0
109 dependency: two expected: 2.0.0 got: 0.0.0
110"#
111 .trim_start();
112 let actual = format!("{}", unpinned);
113 assert_eq!(expected, actual);
114 }
115}