semver/
display.rs

1use crate::{BuildMetadata, Comparator, Op, Prerelease, Version, VersionReq};
2use core::fmt::{self, Alignment, Debug, Display, Write};
3
4impl Display for Version {
5    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
6        let do_display = |formatter: &mut fmt::Formatter| -> fmt::Result {
7            write!(formatter, "{}.{}.{}", self.major, self.minor, self.patch)?;
8            if !self.pre.is_empty() {
9                write!(formatter, "-{}", self.pre)?;
10            }
11            if !self.build.is_empty() {
12                write!(formatter, "+{}", self.build)?;
13            }
14            Ok(())
15        };
16
17        let do_len = || -> usize {
18            digits(self.major)
19                + 1
20                + digits(self.minor)
21                + 1
22                + digits(self.patch)
23                + !self.pre.is_empty() as usize
24                + self.pre.len()
25                + !self.build.is_empty() as usize
26                + self.build.len()
27        };
28
29        pad(formatter, do_display, do_len)
30    }
31}
32
33impl Display for VersionReq {
34    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
35        if self.comparators.is_empty() {
36            return formatter.write_str("*");
37        }
38        for (i, comparator) in self.comparators.iter().enumerate() {
39            if i > 0 {
40                formatter.write_str(", ")?;
41            }
42            write!(formatter, "{}", comparator)?;
43        }
44        Ok(())
45    }
46}
47
48impl Display for Comparator {
49    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
50        let op = match self.op {
51            Op::Exact => "=",
52            Op::Greater => ">",
53            Op::GreaterEq => ">=",
54            Op::Less => "<",
55            Op::LessEq => "<=",
56            Op::Tilde => "~",
57            Op::Caret => "^",
58            Op::Wildcard => "",
59            #[cfg(no_non_exhaustive)]
60            Op::__NonExhaustive => unreachable!(),
61        };
62        formatter.write_str(op)?;
63        write!(formatter, "{}", self.major)?;
64        if let Some(minor) = &self.minor {
65            write!(formatter, ".{}", minor)?;
66            if let Some(patch) = &self.patch {
67                write!(formatter, ".{}", patch)?;
68                if !self.pre.is_empty() {
69                    write!(formatter, "-{}", self.pre)?;
70                }
71            } else if self.op == Op::Wildcard {
72                formatter.write_str(".*")?;
73            }
74        } else if self.op == Op::Wildcard {
75            formatter.write_str(".*")?;
76        }
77        Ok(())
78    }
79}
80
81impl Display for Prerelease {
82    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
83        formatter.write_str(self.as_str())
84    }
85}
86
87impl Display for BuildMetadata {
88    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
89        formatter.write_str(self.as_str())
90    }
91}
92
93impl Debug for Version {
94    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
95        let mut debug = formatter.debug_struct("Version");
96        debug
97            .field("major", &self.major)
98            .field("minor", &self.minor)
99            .field("patch", &self.patch);
100        if !self.pre.is_empty() {
101            debug.field("pre", &self.pre);
102        }
103        if !self.build.is_empty() {
104            debug.field("build", &self.build);
105        }
106        debug.finish()
107    }
108}
109
110impl Debug for Prerelease {
111    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
112        write!(formatter, "Prerelease(\"{}\")", self)
113    }
114}
115
116impl Debug for BuildMetadata {
117    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
118        write!(formatter, "BuildMetadata(\"{}\")", self)
119    }
120}
121
122fn pad(
123    formatter: &mut fmt::Formatter,
124    do_display: impl FnOnce(&mut fmt::Formatter) -> fmt::Result,
125    do_len: impl FnOnce() -> usize,
126) -> fmt::Result {
127    let min_width = match formatter.width() {
128        Some(min_width) => min_width,
129        None => return do_display(formatter),
130    };
131
132    let len = do_len();
133    if len >= min_width {
134        return do_display(formatter);
135    }
136
137    let default_align = Alignment::Left;
138    let align = formatter.align().unwrap_or(default_align);
139    let padding = min_width - len;
140    let (pre_pad, post_pad) = match align {
141        Alignment::Left => (0, padding),
142        Alignment::Right => (padding, 0),
143        Alignment::Center => (padding / 2, (padding + 1) / 2),
144    };
145
146    let fill = formatter.fill();
147    for _ in 0..pre_pad {
148        formatter.write_char(fill)?;
149    }
150
151    do_display(formatter)?;
152
153    for _ in 0..post_pad {
154        formatter.write_char(fill)?;
155    }
156    Ok(())
157}
158
159fn digits(val: u64) -> usize {
160    if val < 10 {
161        1
162    } else {
163        1 + digits(val / 10)
164    }
165}