1use crate::BoxSlice;
2use semver::Op;
3use solar_data_structures::smallvec::{SmallVec, smallvec};
4use solar_interface::Span;
5use std::{cmp::Ordering, fmt};
6
7pub use semver::Op as SemverOp;
8
9#[derive(Clone, Copy)]
20pub enum SemverVersionNumber {
21 Number(u32),
23 Wildcard,
25}
26
27impl From<u64> for SemverVersionNumber {
28 #[inline]
29 fn from(n: u64) -> Self {
30 match n.try_into() {
31 Ok(n) => Self::Number(n),
32 Err(_) => Self::Wildcard,
33 }
34 }
35}
36
37impl From<SemverVersionNumber> for u64 {
38 #[inline]
39 fn from(value: SemverVersionNumber) -> Self {
40 match value {
41 SemverVersionNumber::Number(n) => n as Self,
42 SemverVersionNumber::Wildcard => Self::MAX,
43 }
44 }
45}
46
47impl fmt::Display for SemverVersionNumber {
48 #[inline]
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 match self {
51 Self::Number(n) => n.fmt(f),
52 Self::Wildcard => "*".fmt(f),
53 }
54 }
55}
56
57impl fmt::Debug for SemverVersionNumber {
58 #[inline]
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 fmt::Display::fmt(self, f)
61 }
62}
63
64impl PartialEq for SemverVersionNumber {
65 #[inline]
66 fn eq(&self, other: &Self) -> bool {
67 match (self, other) {
68 (Self::Wildcard, _) | (_, Self::Wildcard) => true,
69 (Self::Number(a), Self::Number(b)) => a == b,
70 }
71 }
72}
73
74impl Eq for SemverVersionNumber {}
75
76impl PartialOrd for SemverVersionNumber {
77 #[inline]
78 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
79 Some(self.cmp(other))
80 }
81}
82
83impl Ord for SemverVersionNumber {
84 #[inline]
85 fn cmp(&self, other: &Self) -> Ordering {
86 match (self, other) {
87 (Self::Wildcard, _) | (_, Self::Wildcard) => Ordering::Equal,
88 (Self::Number(a), Self::Number(b)) => a.cmp(b),
89 }
90 }
91}
92
93#[derive(Clone)]
95pub struct SemverVersion {
96 pub span: Span,
97 pub major: SemverVersionNumber,
99 pub minor: Option<SemverVersionNumber>,
101 pub patch: Option<SemverVersionNumber>,
103 }
105
106impl PartialEq for SemverVersion {
107 #[inline]
108 fn eq(&self, other: &Self) -> bool {
109 self.cmp(other) == Ordering::Equal
110 }
111}
112
113impl Eq for SemverVersion {}
114
115impl PartialOrd for SemverVersion {
116 #[inline]
117 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
118 Some(self.cmp(other))
119 }
120}
121
122impl Ord for SemverVersion {
123 #[inline]
124 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
125 #[inline]
126 fn cmp_opt(a: &Option<SemverVersionNumber>, b: &Option<SemverVersionNumber>) -> Ordering {
127 match (a, b) {
128 (Some(a), Some(b)) => a.cmp(b),
129 _ => Ordering::Equal,
130 }
131 }
132
133 self.major
134 .cmp(&other.major)
135 .then_with(|| cmp_opt(&self.minor, &other.minor))
136 .then_with(|| cmp_opt(&self.patch, &other.patch))
137 }
138}
139
140impl fmt::Display for SemverVersion {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
142 let Self { span: _, major, minor, patch } = *self;
143 write!(f, "{major}")?;
144 if let Some(minor) = minor {
145 write!(f, ".{minor}")?;
146 }
147 if let Some(patch) = patch {
148 if minor.is_none() {
149 f.write_str(".*")?;
150 }
151 write!(f, ".{patch}")?;
152 }
153 Ok(())
154 }
155}
156
157impl fmt::Debug for SemverVersion {
158 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
159 f.debug_struct("SemverVersion")
160 .field("span", &self.span)
161 .field("version", &format_args!("{self}"))
162 .finish()
163 }
164}
165
166impl From<semver::Version> for SemverVersion {
167 #[inline]
168 fn from(version: semver::Version) -> Self {
169 Self {
170 span: Span::DUMMY,
171 major: version.major.into(),
172 minor: Some(version.minor.into()),
173 patch: Some(version.patch.into()),
174 }
175 }
176}
177
178impl From<SemverVersion> for semver::Version {
179 #[inline]
180 fn from(version: SemverVersion) -> Self {
181 Self::new(
182 version.major.into(),
183 version.minor.map(Into::into).unwrap_or(0),
184 version.patch.map(Into::into).unwrap_or(0),
185 )
186 }
187}
188
189impl SemverVersion {
190 #[inline]
192 pub fn into_semver(self) -> semver::Version {
193 self.into()
194 }
195}
196
197#[derive(Debug)]
199pub struct SemverReq<'ast> {
200 pub dis: BoxSlice<'ast, SemverReqCon<'ast>>,
206}
207
208impl fmt::Display for SemverReq<'_> {
209 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
210 for (i, con) in self.dis.iter().enumerate() {
211 if i > 0 {
212 f.write_str(" || ")?;
213 }
214 write!(f, "{con}")?;
215 }
216 Ok(())
217 }
218}
219
220impl SemverReq<'_> {
221 pub fn matches(&self, version: &SemverVersion) -> bool {
223 self.dis.iter().any(|c| c.matches(version))
224 }
225
226 pub fn to_semver(&self) -> SemverVersionReqCompat {
228 SemverVersionReqCompat { reqs: self.dis.iter().map(SemverReqCon::to_semver).collect() }
229 }
230}
231
232pub struct SemverVersionReqCompat {
236 pub reqs: Vec<semver::VersionReq>,
238}
239
240impl SemverVersionReqCompat {
241 pub fn matches(&self, version: &semver::Version) -> bool {
243 self.reqs.iter().any(|r| r.matches(version))
244 }
245}
246
247#[derive(Debug)]
249pub struct SemverReqCon<'ast> {
250 pub span: Span,
251 pub components: BoxSlice<'ast, SemverReqComponent>,
253}
254
255impl fmt::Display for SemverReqCon<'_> {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 for (j, component) in self.components.iter().enumerate() {
258 if j > 0 {
259 f.write_str(" ")?;
260 }
261 write!(f, "{component}")?;
262 }
263 Ok(())
264 }
265}
266
267impl SemverReqCon<'_> {
268 pub fn to_semver(&self) -> semver::VersionReq {
270 semver::VersionReq {
271 comparators: self.components.iter().flat_map(SemverReqComponent::to_semver).collect(),
272 }
273 }
274
275 pub fn matches(&self, version: &SemverVersion) -> bool {
277 self.components.iter().all(|c| c.matches(version))
278 }
279}
280
281#[derive(Clone, Debug)]
283pub struct SemverReqComponent {
284 pub span: Span,
285 pub kind: SemverReqComponentKind,
286}
287
288impl fmt::Display for SemverReqComponent {
289 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290 self.kind.fmt(f)
291 }
292}
293
294impl SemverReqComponent {
295 pub fn to_semver(&self) -> SmallVec<[semver::Comparator; 2]> {
297 self.kind.to_semver()
298 }
299
300 pub fn matches(&self, version: &SemverVersion) -> bool {
302 self.kind.matches(version)
303 }
304}
305
306#[derive(Clone, Debug)]
308pub enum SemverReqComponentKind {
309 Op(Option<Op>, SemverVersion),
311 Range(SemverVersion, SemverVersion),
313}
314
315impl fmt::Display for SemverReqComponentKind {
316 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
317 match self {
318 Self::Op(op, version) => {
319 if let Some(op) = op {
320 let op = match op {
321 Op::Exact => "=",
322 Op::Greater => ">",
323 Op::GreaterEq => ">=",
324 Op::Less => "<",
325 Op::LessEq => "<=",
326 Op::Tilde => "~",
327 Op::Caret => "^",
328 Op::Wildcard => "*",
329 _ => "",
330 };
331 f.write_str(op)?;
332 }
333 write!(f, "{version}")
334 }
335 Self::Range(left, right) => write!(f, "{left} - {right}"),
336 }
337 }
338}
339
340impl SemverReqComponentKind {
341 pub fn to_semver(&self) -> SmallVec<[semver::Comparator; 2]> {
343 let cvt_op = |op: Option<Op>, version: &SemverVersion| semver::Comparator {
344 op: op.unwrap_or(Op::Exact),
345 major: version.major.into(),
346 minor: version.minor.map(Into::into),
347 patch: version.patch.map(Into::into),
348 pre: Default::default(),
349 };
350 match self {
351 Self::Op(op, version) => smallvec![cvt_op(*op, version)],
352 Self::Range(start, end) => smallvec![
353 cvt_op(Some(semver::Op::GreaterEq), start),
354 cvt_op(Some(semver::Op::LessEq), end)
355 ],
356 }
357 }
358
359 pub fn matches(&self, version: &SemverVersion) -> bool {
361 match self {
362 Self::Op(op, other) => matches_op(op.unwrap_or(Op::Exact), version, other),
363 Self::Range(start, end) => {
364 matches_op(Op::GreaterEq, version, start) && matches_op(Op::LessEq, version, end)
365 }
366 }
367 }
368}
369
370fn matches_op(op: Op, a: &SemverVersion, b: &SemverVersion) -> bool {
371 match op {
372 Op::Exact => a == b,
373 Op::Greater => a > b,
374 Op::GreaterEq => a >= b,
375 Op::Less => a < b,
376 Op::LessEq => a <= b,
377 Op::Tilde => matches_tilde(a, b),
378 Op::Caret => matches_caret(a, b),
379 Op::Wildcard => true,
380 _ => false,
381 }
382}
383
384fn matches_tilde(a: &SemverVersion, b: &SemverVersion) -> bool {
385 if !matches_op(Op::GreaterEq, a, b) {
387 return false;
388 }
389
390 let mut a = a.clone();
391 a.patch = None;
392 matches_op(Op::LessEq, &a, b)
393}
394
395fn matches_caret(a: &SemverVersion, b: &SemverVersion) -> bool {
396 if !matches_op(Op::GreaterEq, a, b) {
398 return false;
399 }
400
401 let mut a = a.clone();
402 if a.major > SemverVersionNumber::Number(0) {
403 a.minor = None;
404 }
405 a.patch = None;
406 matches_op(Op::LessEq, &a, b)
407}
408
409