semver_php/constraint/
bound.rs1use crate::version::version_compare;
2use std::{
3 cmp::Ordering,
4 fmt::{self, Display},
5};
6
7#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct Bound {
11 version: String,
12 inclusive: bool,
13}
14
15impl Bound {
16 pub fn new(version: impl Into<String>, inclusive: bool) -> Self {
18 Self {
19 version: version.into(),
20 inclusive,
21 }
22 }
23
24 #[must_use]
27 pub fn zero() -> Self {
28 Self::new("0.0.0.0-dev", true)
29 }
30
31 #[must_use]
34 pub fn positive_infinity() -> Self {
35 Self::new("9223372036854775807.0.0.0", false)
36 }
37
38 #[must_use]
40 pub fn version(&self) -> &str {
41 &self.version
42 }
43
44 #[must_use]
46 pub const fn is_inclusive(&self) -> bool {
47 self.inclusive
48 }
49
50 #[must_use]
52 pub fn is_zero(&self) -> bool {
53 self.version == "0.0.0.0-dev" && self.inclusive
54 }
55
56 #[must_use]
58 pub fn is_positive_infinity(&self) -> bool {
59 self.version == "9223372036854775807.0.0.0" && !self.inclusive
60 }
61
62 #[must_use]
65 pub fn compare_to(&self, other: &Self) -> Ordering {
66 let cmp = version_compare(&self.version, &other.version);
67
68 if cmp != Ordering::Equal {
69 return cmp;
70 }
71
72 match (self.inclusive, other.inclusive) {
77 (true, false) => Ordering::Less,
78 (false, true) => Ordering::Greater,
79 _ => Ordering::Equal,
80 }
81 }
82
83 #[must_use]
86 pub fn compare_as_lower(&self, other: &Self) -> Ordering {
87 let cmp = version_compare(&self.version, &other.version);
88
89 if cmp != Ordering::Equal {
90 return cmp;
91 }
92
93 match (self.inclusive, other.inclusive) {
95 (true, false) => Ordering::Less,
96 (false, true) => Ordering::Greater,
97 _ => Ordering::Equal,
98 }
99 }
100
101 #[must_use]
104 pub fn compare_as_upper(&self, other: &Self) -> Ordering {
105 let cmp = version_compare(&self.version, &other.version);
106
107 if cmp != Ordering::Equal {
108 return cmp;
109 }
110
111 match (self.inclusive, other.inclusive) {
113 (false, true) => Ordering::Less,
114 (true, false) => Ordering::Greater,
115 _ => Ordering::Equal,
116 }
117 }
118}
119
120impl Display for Bound {
121 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122 let bracket = if self.inclusive {
123 "inclusive"
124 } else {
125 "exclusive"
126 };
127
128 write!(f, "{} ({})", self.version, bracket)
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use super::*;
135
136 #[test]
137 fn test_bound_creation() {
138 let bound = Bound::new("1.0.0", true);
139 assert_eq!(bound.version(), "1.0.0");
140 assert!(bound.is_inclusive());
141 }
142
143 #[test]
144 fn test_zero_bound() {
145 let zero = Bound::zero();
146 assert!(zero.is_zero());
147 assert!(!zero.is_positive_infinity());
148 }
149
150 #[test]
151 fn test_positive_infinity() {
152 let inf = Bound::positive_infinity();
153 assert!(inf.is_positive_infinity());
154 assert!(!inf.is_zero());
155 }
156
157 #[test]
158 fn test_bound_comparison() {
159 let a = Bound::new("1.0.0", true);
160 let b = Bound::new("2.0.0", true);
161 assert_eq!(a.compare_to(&b), Ordering::Less);
162
163 let c = Bound::new("1.0.0", false);
164 assert_eq!(a.compare_to(&c), Ordering::Less); }
166}