1use crate::requirement::parser::parse_version_range;
2use crate::version::RerVersion;
3use core::fmt;
4use lazy_static::lazy_static;
5use regex::Regex;
6use std::fmt::Debug;
7use std::hash::{Hash, Hasher};
8use version_ranges::Ranges;
9
10lazy_static! {
11 static ref SEP_REGEZ_STR: Regex =
12 Regex::new(r"[-@#]").expect("Can't compile SEP_REGEZ_STR regex");
13 static ref SEP_REGEX: Regex = Regex::new(r"[-@#=<>]").expect("Can't compile SEP_REGEX regex");
14}
15
16#[derive(Clone, PartialEq, Eq, Debug)]
24pub struct Requirement {
25 pub name: String,
26 pub range: Option<Ranges<RerVersion>>,
27 weak_ref: bool,
28 conflict: bool,
29 sep: char,
30 original_name: String,
31}
32
33impl Requirement {
34 fn parse_from_string(input_str: &str) -> Self {
44 let original_name: String = input_str.to_string();
45 let mut range = None;
46 let mut weak_ref = false;
47 let mut conflict = input_str.starts_with('!');
48 let mut sep = '-';
49
50 let mut input_str = input_str.to_string();
51 if conflict {
52 input_str.remove(0);
53 } else if input_str.starts_with('~') {
54 input_str.remove(0);
55 conflict = true;
56 weak_ref = true;
57 }
58
59 let name: String = if let Some(m) = SEP_REGEX.find(&input_str) {
60 let mut req_str = input_str[m.start()..].to_string();
61 if ['-', '@', '#'].contains(&req_str.chars().next().unwrap()) {
62 sep = req_str.remove(0);
63 }
64 if req_str.contains('|') {
65 let reqs: Vec<Ranges<RerVersion>> =
66 req_str.split('|').map(parse_version_range).collect();
67 for req in reqs {
68 if range.is_none() {
69 range = Some(req);
70 } else {
71 range = Some(range.unwrap().union(&req));
72 }
73 }
74 } else {
75 range = Some(parse_version_range(&req_str));
76 }
77 if conflict {
78 range = Some(range.unwrap().complement());
79 }
80 input_str[..m.start()].to_string()
81 } else if conflict {
82 input_str
83 } else {
85 range = Some(Ranges::full());
86 input_str
87 };
88
89 Requirement {
90 name,
91 range,
92 weak_ref,
93 conflict,
94 sep,
95 original_name,
96 }
97 }
98 pub fn get_pubgrub(&self) -> (String, Ranges<RerVersion>) {
105 (self.name.clone(), self.range.clone().unwrap())
106 }
107 pub fn get_name(&self) -> &str {
121 &self.name
122 }
123 pub fn get_version_range(&self) -> Option<Ranges<RerVersion>> {
129 self.range.clone()
130 }
131 pub fn merge(&self, other: &Self) -> Option<Self> {
137 if self.name != other.name {
138 return None; }
140
141 let merged_range = match (&self.range, &other.range) {
142 (None, _) => other.range.clone(),
143 (_, None) => self.range.clone(),
144 (Some(a), Some(b)) => {
145 if self.conflict {
146 if other.conflict {
147 Some(a.union(b)) } else {
149 Some(Self::difference(b, a)) }
151 } else if other.conflict {
152 Some(Self::difference(a, b)) } else {
154 Some(a.intersection(b)) }
156 }
157 };
158
159 merged_range.map(|range| Requirement {
160 name: self.name.clone(),
161 range: Some(range),
162 weak_ref: self.weak_ref && other.weak_ref, conflict: self.conflict && other.conflict, sep: self.sep, original_name: self.original_name.clone(), })
167 }
168 fn difference(a: &Ranges<RerVersion>, b: &Ranges<RerVersion>) -> Ranges<RerVersion> {
169 a.intersection(&b.complement())
170 }
171 pub fn is_weak_ref(&self) -> bool {
177 self.weak_ref
178 }
179 fn from_pkg_and_range(name: String, range: Ranges<RerVersion>) -> Self {
180 let original_name = format!("{}-{}", name, range);
181 Requirement {
182 name,
183 range: Some(range),
184 weak_ref: false,
185 conflict: false,
186 sep: '-',
187 original_name,
188 }
189 }
190}
191
192#[test]
193fn test_requierement() {
194 let a = Requirement::from("voodoo-1");
195 assert_eq!(a.name, "voodoo");
196 let v: RerVersion = "1".try_into().unwrap();
197 assert_eq!(a.range, Some(Ranges::between(v.clone(), v.bump())));
198 let a = Requirement::from("voodoo-1.13.0|2.1.0");
199 assert_eq!(a.name, "voodoo");
200 let v: RerVersion = "1.13.0".try_into().unwrap();
201 let v2: RerVersion = "2.1.0".try_into().unwrap();
202 assert_eq!(
203 a.range,
204 Some(Ranges::between(v.clone(), v.bump()).union(&Ranges::between(v2.clone(), v2.bump())))
205 );
206}
207
208impl fmt::Display for Requirement {
209 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
210 write!(f, "{}", self.original_name)
211 }
212}
213
214impl Hash for Requirement {
215 fn hash<H: Hasher>(&self, state: &mut H) {
216 self.original_name.hash(state);
217 }
218}
219
220impl From<&str> for Requirement {
221 fn from(s: &str) -> Self {
222 Requirement::parse_from_string(s)
223 }
224}
225#[test]
226fn test_to_string() {
227 let a = Requirement::from("maya-1");
228 assert_eq!(a.to_string(), "maya-1");
229 let a = Requirement::from("maya");
230 assert_eq!(a.to_string(), "maya");
231 let a = Requirement::from("maya-1.2.3+<2.0.0");
232 assert_eq!(a.to_string(), "maya-1.2.3+<2.0.0");
233 let a = Requirement::from("maya-1.2.3+");
234 assert_eq!(a.to_string(), "maya-1.2.3+");
235}
236
237#[test]
238fn test_merge_requirement() {
239 let a = Requirement::from("foo-1.2");
240 let b = Requirement::from("~foo-1");
241 let c = a.merge(&b).unwrap();
242 assert_eq!(c.to_string(), "foo-1.2");
243 let a = Requirement::from("foo-1.2");
244 let b = Requirement::from("foo-1");
245 let c = a.merge(&b).unwrap();
246 assert_eq!(c.to_string(), "foo-1.2");
247 let a = Requirement::from("foo-1.2");
248 let b = Requirement::from("foo==1.2.2");
249 let c = a.merge(&b).unwrap();
250 let v_start: RerVersion = "1.2.2".try_into().unwrap();
251 assert_eq!(c.get_version_range(), Some(Ranges::singleton(v_start)));
252 let a = Requirement::from("foo-1.2");
253 let b = Requirement::from("~foo-1");
254 let c = a.merge(&b).unwrap();
255 let v_start: RerVersion = "1.2".try_into().unwrap();
256 let v_end: RerVersion = "1.2_".try_into().unwrap();
257 assert_eq!(c.get_version_range(), Some(Ranges::between(v_start, v_end)));
258 let a = Requirement::from("foo-1.2");
259 let b = Requirement::from("!foo-1");
260 let c = a.merge(&b).unwrap();
261 let v_start: RerVersion = "1.2".try_into().unwrap();
262 let v_end: RerVersion = "1.2_".try_into().unwrap();
263 println!("{:?}", c.get_version_range().unwrap().to_string());
264 assert_eq!(c.get_version_range(), Some(Ranges::between(v_start, v_end)));
265}
266
267#[derive(Clone, Debug, Default, PartialEq, Eq)]
273pub struct Requirements(Vec<Requirement>);
274impl Requirements {
275 pub fn empty() -> Self {
276 Requirements(Vec::new())
277 }
278 pub fn add(&mut self, requirement: Requirement) {
279 self.0.push(requirement);
280 }
281 pub fn from(requirements: Vec<&str>) -> Self {
287 let requirements = requirements
288 .iter()
289 .map(|x| Requirement::parse_from_string(x))
290 .collect();
291 Requirements(requirements)
292 }
293 pub fn merge(&self) -> Self {
299 if self.0.len() <= 1 {
300 return self.clone();
301 }
302 let mut requirements = self.0.clone();
303 let mut i = 0;
304 while i < requirements.len() {
305 let mut j = i + 1;
306 while j < requirements.len() {
307 if let Some(req) = requirements[i].merge(&requirements[j]) {
308 requirements[i] = req;
309 requirements.remove(j);
310 } else {
311 j += 1;
312 }
313 }
314 i += 1;
315 }
316 Requirements(requirements)
317 }
318 pub fn extend(&mut self, other: &Self) {
319 self.0.extend(other.0.clone());
320 }
321 pub fn switch(&mut self, other: &Self) {
322 self.0.clone_from(&other.0)
323 }
324 pub fn to_pubgrub(&self) -> Vec<(String, Ranges<RerVersion>)> {
330 self.0.iter().map(|x| x.get_pubgrub()).collect()
331 }
332 pub fn is_empty(&self) -> bool {
338 self.0.is_empty()
339 }
340 pub fn split_weak_ref(&self) -> (Self, Self) {
341 let mut weak_ref = Vec::new();
342 let mut strong_ref = Vec::new();
343 for req in &self.0 {
344 if req.is_weak_ref() {
345 weak_ref.push(req.clone());
346 } else {
347 strong_ref.push(req.clone());
348 }
349 }
350 (Requirements(weak_ref), Requirements(strong_ref))
351 }
352 pub fn split_conflict(&self) -> (Self, Self) {
362 let mut conflict = Vec::new();
363 let mut no_conflict = Vec::new();
364 for req in &self.0 {
365 if req.conflict {
366 conflict.push(req.clone());
367 } else {
368 no_conflict.push(req.clone());
369 }
370 }
371 (Requirements(no_conflict), Requirements(conflict))
372 }
373 pub fn reduced(
374 &mut self,
375 package_name: &String,
376 range: &Ranges<RerVersion>,
377 ) -> (Ranges<RerVersion>, Self) {
378 let new_req = Requirement::from_pkg_and_range(package_name.clone(), range.clone());
379 let requierements: Requirements = Requirements(vec![new_req]);
380 self.extend(&requierements);
381 let n = self.merge();
382 let (no_conflict, _conflict) = n.split_conflict();
383 let range = no_conflict
384 .0
385 .iter()
386 .find(|x| x.name == *package_name)
387 .unwrap()
388 .range
389 .clone()
390 .unwrap();
391 (range, n)
392 }
393}
394
395impl fmt::Display for Requirements {
396 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
397 let mut first = true;
398 for req in &self.0 {
399 if first {
400 first = false;
401 } else {
402 write!(f, ", ")?;
403 }
404 write!(f, "{}", req)?;
405 }
406 Ok(())
407 }
408}
409
410impl Iterator for Requirements {
411 type Item = Requirement;
412 fn next(&mut self) -> Option<Self::Item> {
413 self.0.pop()
414 }
415}
416
417#[test]
418fn test_merge() {
419 let requirements_str = vec!["foo-1.2", "bah-3", "~foo-1"];
420 let requirements = Requirements::from(requirements_str);
421 let requirements = requirements.merge();
422 assert_eq!(requirements.0.len(), 2);
423 assert_eq!(requirements.0[0].name, "foo");
424}
425
426#[test]
427fn test_switch() {
428 let requirements_str = vec!["foo-1.2", "bah-3", "~foo-1"];
429 let mut requirements = Requirements::from(requirements_str);
430 let requirements_str2 = vec!["foo-1.5", "bah-4", "~foo-6", "~toto-6"];
431 let requirements2 = Requirements::from(requirements_str2);
432 requirements.switch(&requirements2);
433 assert_eq!(requirements, requirements2);
434}
435
436#[test]
437fn test_reduce() {
438 let requirements_str = vec!["~foo-1.0.5"];
439 let mut requirements = Requirements::from(requirements_str);
440 let v: RerVersion = "1".try_into().unwrap();
441 let (range, _toto) =
442 requirements.reduced(&"foo".to_string(), &Ranges::between(v.clone(), v.bump()));
443 let v_req: RerVersion = "1.0.5".try_into().unwrap();
444 let req_range: Ranges<RerVersion> = Ranges::between(v_req.clone(), v_req.bump());
445 assert_eq!(range, req_range);
446}