1use guppy::Version;
5use std::fmt;
6
7#[derive(Clone, Copy, Debug, Eq, PartialEq)]
9pub(crate) struct VersionDisplay<'a> {
10 version: &'a Version,
11 exact_versions: bool,
12 with_build_metadata: bool,
15}
16
17impl<'a> VersionDisplay<'a> {
18 pub(crate) fn new(
19 version: &'a Version,
20 exact_versions: bool,
21 with_build_metadata: bool,
22 ) -> Self {
23 Self {
24 version,
25 exact_versions,
26 with_build_metadata,
27 }
28 }
29}
30
31impl fmt::Display for VersionDisplay<'_> {
32 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
33 if !self.exact_versions && self.version.pre.is_empty() {
34 if self.version.major >= 1 {
36 return write!(f, "{}", self.version.major);
37 }
38 if self.version.minor >= 1 {
39 return write!(f, "{}.{}", self.version.major, self.version.minor);
40 }
41 }
42 write!(
43 f,
44 "{}.{}.{}",
45 self.version.major, self.version.minor, self.version.patch
46 )?;
47 if !self.version.pre.is_empty() {
48 write!(f, "-{}", self.version.pre)?;
49 }
50 if self.with_build_metadata && !self.version.build.is_empty() {
51 write!(f, "+{}", self.version.build)?;
52 }
53 Ok(())
54 }
55}
56
57#[cfg(test)]
58mod tests {
59 use super::*;
60 use fixtures::json::*;
61 use guppy::{graph::DependencyDirection, VersionReq};
62
63 #[test]
64 fn min_version() {
65 let versions = vec![
66 ("1.4.0", "1", "1.4.0", "1", "1.4.0"),
67 ("2.8.0", "2", "2.8.0", "2", "2.8.0"),
68 ("0.4.2", "0.4", "0.4.2", "0.4", "0.4.2"),
69 ("0.0.7", "0.0.7", "0.0.7", "0.0.7", "0.0.7"),
70 ("1.4.0-b1", "1.4.0-b1", "1.4.0-b1", "1.4.0-b1", "1.4.0-b1"),
71 (
72 "2.8.0-a.1+v123",
73 "2.8.0-a.1",
74 "2.8.0-a.1",
75 "2.8.0-a.1+v123",
76 "2.8.0-a.1+v123",
77 ),
78 ("4.2.3+g456", "4", "4.2.3", "4", "4.2.3+g456"),
79 ];
80
81 for (version_str, min, exact, min_with_build_metadata, exact_with_build_metadata) in
82 versions
83 {
84 let version = Version::parse(version_str).expect("valid version");
85 let version_req = VersionReq::parse(min).expect("valid version req");
86 assert!(
87 version_req.matches(&version),
88 "version req {} should match version {}",
89 min,
90 version
91 );
92 assert_eq!(
93 &format!("{}", VersionDisplay::new(&version, false, false)),
94 min
95 );
96 assert_eq!(
97 &format!("{}", VersionDisplay::new(&version, true, false)),
98 exact
99 );
100 assert_eq!(
101 &format!("{}", VersionDisplay::new(&version, false, true)),
102 min_with_build_metadata
103 );
104 assert_eq!(
105 &format!("{}", VersionDisplay::new(&version, true, true)),
106 exact_with_build_metadata
107 );
108 }
109 }
110
111 #[test]
112 fn min_versions_match() {
113 for (&name, fixture) in JsonFixture::all_fixtures() {
114 let graph = fixture.graph();
115 for package in graph.resolve_all().packages(DependencyDirection::Forward) {
116 let version = package.version();
117 let min_version = format!("{}", VersionDisplay::new(version, false, false));
118 let version_req = VersionReq::parse(&min_version).expect("valid version req");
119
120 assert!(
121 version_req.matches(version),
122 "for fixture '{}', for package '{}', min version req {} should match version {}",
123 name,
124 package.id(),
125 min_version,
126 version,
127 );
128
129 let min_version_with_build_metadata =
130 format!("{}", VersionDisplay::new(version, false, true));
131 let version_req =
132 VersionReq::parse(&min_version_with_build_metadata).expect("valid version req");
133
134 assert!(
135 version_req.matches(version),
136 "for fixture '{}', for package '{}', min version (with build metadata) req {} should match version {}",
137 name,
138 package.id(),
139 min_version,
140 version,
141 );
142 }
143 }
144 }
145}