1use crate::relations::SyntaxKind::{self, *};
22use crate::relations::{BuildProfile, VersionConstraint};
23use debversion::Version;
24use rowan::{Direction, NodeOrToken};
25use std::collections::HashSet;
26
27#[derive(Debug, Clone, PartialEq, Eq, Hash)]
29pub struct ParseError(Vec<String>);
30
31impl std::fmt::Display for ParseError {
32 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
33 for err in &self.0 {
34 writeln!(f, "{}", err)?;
35 }
36 Ok(())
37 }
38}
39
40impl std::error::Error for ParseError {}
41
42#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
46enum Lang {}
47impl rowan::Language for Lang {
48 type Kind = SyntaxKind;
49 fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
50 unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
51 }
52 fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
53 kind.into()
54 }
55}
56
57use rowan::{GreenNode, GreenToken};
60
61use rowan::GreenNodeBuilder;
65
66struct Parse {
69 green_node: GreenNode,
70 #[allow(unused)]
71 errors: Vec<String>,
72}
73
74fn parse(text: &str, allow_substvar: bool) -> Parse {
75 struct Parser {
76 tokens: Vec<(SyntaxKind, String)>,
79 builder: GreenNodeBuilder<'static>,
81 errors: Vec<String>,
84 allow_substvar: bool,
86 }
87
88 impl Parser {
89 fn parse_substvar(&mut self) {
90 self.builder.start_node(SyntaxKind::SUBSTVAR.into());
91 self.bump();
92 if self.current() != Some(L_CURLY) {
93 self.error(format!("expected {{ but got {:?}", self.current()).to_string());
94 } else {
95 self.bump();
96 }
97 loop {
98 match self.current() {
99 Some(IDENT) | Some(COLON) => {
100 self.bump();
101 }
102 Some(R_CURLY) => {
103 break;
104 }
105 e => {
106 self.error(format!("expected identifier or : but got {:?}", e).to_string());
107 }
108 }
109 }
110 if self.current() != Some(R_CURLY) {
111 self.error(format!("expected }} but got {:?}", self.current()).to_string());
112 } else {
113 self.bump();
114 }
115 self.builder.finish_node();
116 }
117
118 fn parse_entry(&mut self) {
119 self.skip_ws();
120 self.builder.start_node(SyntaxKind::ENTRY.into());
121 loop {
122 self.parse_relation();
123 match self.peek_past_ws() {
124 Some(COMMA) => {
125 break;
126 }
127 Some(PIPE) => {
128 self.skip_ws();
129 self.bump();
130 self.skip_ws();
131 }
132 None => {
133 self.skip_ws();
134 break;
135 }
136 _ => {
137 self.skip_ws();
138 self.builder.start_node(SyntaxKind::ERROR.into());
139 match self.tokens.pop() {
140 Some((k, t)) => {
141 self.builder.token(k.into(), t.as_str());
142 self.errors
143 .push(format!("Expected comma or pipe, not {:?}", (k, t)));
144 }
145 None => {
146 self.errors
147 .push("Expected comma or pipe, got end of file".to_string());
148 }
149 }
150 self.builder.finish_node();
151 }
152 }
153 }
154 self.builder.finish_node();
155 }
156
157 fn error(&mut self, error: String) {
158 self.errors.push(error);
159 self.builder.start_node(SyntaxKind::ERROR.into());
160 if self.current().is_some() {
161 self.bump();
162 }
163 self.builder.finish_node();
164 }
165
166 fn parse_relation(&mut self) {
167 self.builder.start_node(SyntaxKind::RELATION.into());
168 if self.current() == Some(IDENT) {
169 self.bump();
170 } else {
171 self.error("Expected package name".to_string());
172 }
173 match self.peek_past_ws() {
174 Some(COLON) => {
175 self.skip_ws();
176 self.builder.start_node(ARCHQUAL.into());
177 self.bump();
178 self.skip_ws();
179 if self.current() == Some(IDENT) {
180 self.bump();
181 } else {
182 self.error("Expected architecture name".to_string());
183 }
184 self.builder.finish_node();
185 self.skip_ws();
186 }
187 Some(PIPE) | Some(COMMA) => {}
188 None | Some(L_PARENS) | Some(L_BRACKET) | Some(L_ANGLE) => {
189 self.skip_ws();
190 }
191 e => {
192 self.skip_ws();
193 self.error(format!(
194 "Expected ':' or '|' or '[' or '<' or ',' but got {:?}",
195 e
196 ));
197 }
198 }
199
200 if self.peek_past_ws() == Some(L_PARENS) {
201 self.skip_ws();
202 self.builder.start_node(VERSION.into());
203 self.bump();
204 self.skip_ws();
205
206 self.builder.start_node(CONSTRAINT.into());
207
208 while self.current() == Some(L_ANGLE)
209 || self.current() == Some(R_ANGLE)
210 || self.current() == Some(EQUAL)
211 {
212 self.bump();
213 }
214
215 self.builder.finish_node();
216
217 self.skip_ws();
218
219 if self.current() == Some(IDENT) {
220 self.bump();
221 } else {
222 self.error("Expected version".to_string());
223 }
224
225 if self.current() == Some(R_PARENS) {
226 self.bump();
227 } else {
228 self.error("Expected ')'".to_string());
229 }
230
231 self.builder.finish_node();
232 }
233
234 if self.peek_past_ws() == Some(L_BRACKET) {
235 self.skip_ws();
236 self.builder.start_node(ARCHITECTURES.into());
237 self.bump();
238 loop {
239 self.skip_ws();
240 match self.current() {
241 Some(NOT) => {
242 self.bump();
243 }
244 Some(IDENT) => {
245 self.bump();
246 }
247 Some(R_BRACKET) => {
248 self.bump();
249 break;
250 }
251 _ => {
252 self.error("Expected architecture name or '!' or ']'".to_string());
253 }
254 }
255 }
256 self.builder.finish_node();
257 }
258
259 while self.peek_past_ws() == Some(L_ANGLE) {
260 self.skip_ws();
261 self.builder.start_node(PROFILES.into());
262 self.bump();
263
264 loop {
265 self.skip_ws();
266 match self.current() {
267 Some(IDENT) => {
268 self.bump();
269 }
270 Some(NOT) => {
271 self.bump();
272 self.skip_ws();
273 if self.current() == Some(IDENT) {
274 self.bump();
275 } else {
276 self.error("Expected profile".to_string());
277 }
278 }
279 Some(R_ANGLE) => {
280 self.bump();
281 break;
282 }
283 None => {
284 self.error("Expected profile or '>'".to_string());
285 break;
286 }
287 _ => {
288 self.error("Expected profile or '!' or '>'".to_string());
289 }
290 }
291 }
292
293 self.builder.finish_node();
294 }
295
296 self.builder.finish_node();
297 }
298
299 fn parse(mut self) -> Parse {
300 self.builder.start_node(SyntaxKind::ROOT.into());
301
302 self.skip_ws();
303
304 while self.current().is_some() {
305 match self.current() {
306 Some(IDENT) => self.parse_entry(),
307 Some(DOLLAR) => {
308 if self.allow_substvar {
309 self.parse_substvar()
310 } else {
311 self.error("Substvars are not allowed".to_string());
312 }
313 }
314 Some(COMMA) => {
315 }
317 Some(c) => {
318 self.error(format!("expected $ or identifier but got {:?}", c));
319 }
320 None => {
321 self.error("expected identifier but got end of file".to_string());
322 }
323 }
324
325 self.skip_ws();
326 match self.current() {
327 Some(COMMA) => {
328 self.bump();
329 }
330 None => {
331 break;
332 }
333 c => {
334 self.error(format!("expected comma or end of file but got {:?}", c));
335 }
336 }
337 self.skip_ws();
338 }
339
340 self.builder.finish_node();
341 Parse {
343 green_node: self.builder.finish(),
344 errors: self.errors,
345 }
346 }
347 fn bump(&mut self) {
349 let (kind, text) = self.tokens.pop().unwrap();
350 self.builder.token(kind.into(), text.as_str());
351 }
352 fn current(&self) -> Option<SyntaxKind> {
354 self.tokens.last().map(|(kind, _)| *kind)
355 }
356 fn skip_ws(&mut self) {
357 while self.current() == Some(WHITESPACE) || self.current() == Some(NEWLINE) {
358 self.bump()
359 }
360 }
361
362 fn peek_past_ws(&self) -> Option<SyntaxKind> {
363 let mut i = self.tokens.len();
364 while i > 0 {
365 i -= 1;
366 match self.tokens[i].0 {
367 WHITESPACE | NEWLINE => {}
368 _ => return Some(self.tokens[i].0),
369 }
370 }
371 None
372 }
373 }
374
375 let mut tokens = crate::relations::lex(text);
376 tokens.reverse();
377 Parser {
378 tokens,
379 builder: GreenNodeBuilder::new(),
380 errors: Vec::new(),
381 allow_substvar,
382 }
383 .parse()
384}
385
386type SyntaxNode = rowan::SyntaxNode<Lang>;
393#[allow(unused)]
394type SyntaxToken = rowan::SyntaxToken<Lang>;
395#[allow(unused)]
396type SyntaxElement = rowan::NodeOrToken<SyntaxNode, SyntaxToken>;
397
398impl Parse {
399 fn root_mut(&self) -> Relations {
400 Relations::cast(SyntaxNode::new_root_mut(self.green_node.clone())).unwrap()
401 }
402}
403
404macro_rules! ast_node {
405 ($ast:ident, $kind:ident) => {
406 #[repr(transparent)]
408 pub struct $ast(SyntaxNode);
409 impl $ast {
410 #[allow(unused)]
411 fn cast(node: SyntaxNode) -> Option<Self> {
412 if node.kind() == $kind {
413 Some(Self(node))
414 } else {
415 None
416 }
417 }
418 }
419
420 impl std::fmt::Display for $ast {
421 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
422 f.write_str(&self.0.text().to_string())
423 }
424 }
425 };
426}
427
428ast_node!(Relations, ROOT);
429ast_node!(Entry, ENTRY);
430ast_node!(Relation, RELATION);
431ast_node!(Substvar, SUBSTVAR);
432
433impl PartialEq for Relations {
434 fn eq(&self, other: &Self) -> bool {
435 self.entries().collect::<Vec<_>>() == other.entries().collect::<Vec<_>>()
436 }
437}
438
439impl PartialEq for Entry {
440 fn eq(&self, other: &Self) -> bool {
441 self.relations().collect::<Vec<_>>() == other.relations().collect::<Vec<_>>()
442 }
443}
444
445impl PartialEq for Relation {
446 fn eq(&self, other: &Self) -> bool {
447 self.name() == other.name()
448 && self.version() == other.version()
449 && self.archqual() == other.archqual()
450 && self.architectures().map(|x| x.collect::<HashSet<_>>())
451 == other.architectures().map(|x| x.collect::<HashSet<_>>())
452 && self.profiles().eq(other.profiles())
453 }
454}
455
456#[cfg(feature = "serde")]
457impl serde::Serialize for Relations {
458 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
459 let rep = self.to_string();
460 serializer.serialize_str(&rep)
461 }
462}
463
464#[cfg(feature = "serde")]
465impl<'de> serde::Deserialize<'de> for Relations {
466 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
467 let s = String::deserialize(deserializer)?;
468 let relations = s.parse().map_err(serde::de::Error::custom)?;
469 Ok(relations)
470 }
471}
472
473impl std::fmt::Debug for Relations {
474 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
475 let mut s = f.debug_struct("Relations");
476
477 for entry in self.entries() {
478 s.field("entry", &entry);
479 }
480
481 s.finish()
482 }
483}
484
485impl std::fmt::Debug for Entry {
486 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
487 let mut s = f.debug_struct("Entry");
488
489 for relation in self.relations() {
490 s.field("relation", &relation);
491 }
492
493 s.finish()
494 }
495}
496
497#[cfg(feature = "serde")]
498impl serde::Serialize for Entry {
499 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
500 let rep = self.to_string();
501 serializer.serialize_str(&rep)
502 }
503}
504
505#[cfg(feature = "serde")]
506impl<'de> serde::Deserialize<'de> for Entry {
507 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
508 let s = String::deserialize(deserializer)?;
509 let entry = s.parse().map_err(serde::de::Error::custom)?;
510 Ok(entry)
511 }
512}
513
514impl std::fmt::Debug for Relation {
515 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516 let mut s = f.debug_struct("Relation");
517
518 s.field("name", &self.name());
519
520 if let Some((vc, version)) = self.version() {
521 s.field("version", &vc);
522 s.field("version", &version);
523 }
524
525 s.finish()
526 }
527}
528
529#[cfg(feature = "serde")]
530impl serde::Serialize for Relation {
531 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
532 let rep = self.to_string();
533 serializer.serialize_str(&rep)
534 }
535}
536
537#[cfg(feature = "serde")]
538impl<'de> serde::Deserialize<'de> for Relation {
539 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
540 let s = String::deserialize(deserializer)?;
541 let relation = s.parse().map_err(serde::de::Error::custom)?;
542 Ok(relation)
543 }
544}
545
546impl Default for Relations {
547 fn default() -> Self {
548 Self::new()
549 }
550}
551
552impl Relations {
553 pub fn new() -> Self {
555 Self::from(vec![])
556 }
557
558 #[must_use]
560 pub fn wrap_and_sort(self) -> Self {
561 let mut entries = self
562 .entries()
563 .map(|e| e.wrap_and_sort())
564 .collect::<Vec<_>>();
565 entries.sort();
566 Self::from(entries)
568 }
569
570 pub fn entries(&self) -> impl Iterator<Item = Entry> + '_ {
572 self.0.children().filter_map(Entry::cast)
573 }
574
575 pub fn iter(&self) -> impl Iterator<Item = Entry> + '_ {
577 self.entries()
578 }
579
580 pub fn get_entry(&self, idx: usize) -> Option<Entry> {
582 self.entries().nth(idx)
583 }
584
585 pub fn remove_entry(&mut self, idx: usize) -> Entry {
587 let mut entry = self.get_entry(idx).unwrap();
588 entry.remove();
589 entry
590 }
591
592 pub fn insert(&mut self, idx: usize, entry: Entry) {
594 let is_empty = !self.0.children_with_tokens().any(|n| n.kind() == COMMA);
595 let (position, new_children) = if let Some(current_entry) = self.entries().nth(idx) {
596 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if idx == 0 && is_empty {
597 vec![entry.0.green().into()]
598 } else {
599 vec![
600 entry.0.green().into(),
601 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
602 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
603 ]
604 };
605
606 (current_entry.0.index(), to_insert)
607 } else {
608 let child_count = self.0.children_with_tokens().count();
609 (
610 child_count,
611 if idx == 0 {
612 vec![entry.0.green().into()]
613 } else {
614 vec![
615 NodeOrToken::Token(GreenToken::new(COMMA.into(), ",")),
616 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
617 entry.0.green().into(),
618 ]
619 },
620 )
621 };
622 self.0 = SyntaxNode::new_root_mut(
624 self.0.replace_with(
625 self.0
626 .green()
627 .splice_children(position..position, new_children),
628 ),
629 );
630 }
631
632 pub fn replace(&mut self, idx: usize, entry: Entry) {
634 let current_entry = self.get_entry(idx).unwrap();
635 self.0.splice_children(
636 current_entry.0.index()..current_entry.0.index() + 1,
637 vec![entry.0.into()],
638 );
639 }
640
641 pub fn push(&mut self, entry: Entry) {
643 let pos = self.entries().count();
644 self.insert(pos, entry);
645 }
646
647 pub fn substvars(&self) -> impl Iterator<Item = String> + '_ {
649 self.0
650 .children()
651 .filter_map(Substvar::cast)
652 .map(|s| s.to_string())
653 }
654
655 pub fn parse_relaxed(s: &str, allow_substvar: bool) -> (Relations, Vec<String>) {
657 let parse = parse(s, allow_substvar);
658 (parse.root_mut(), parse.errors)
659 }
660
661 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
663 self.entries().all(|e| e.satisfied_by(package_version))
664 }
665
666 pub fn is_empty(&self) -> bool {
668 self.entries().count() == 0
669 }
670
671 pub fn len(&self) -> usize {
673 self.entries().count()
674 }
675}
676
677impl From<Vec<Entry>> for Relations {
678 fn from(entries: Vec<Entry>) -> Self {
679 let mut builder = GreenNodeBuilder::new();
680 builder.start_node(ROOT.into());
681 for (i, entry) in entries.into_iter().enumerate() {
682 if i > 0 {
683 builder.token(COMMA.into(), ",");
684 builder.token(WHITESPACE.into(), " ");
685 }
686 inject(&mut builder, entry.0);
687 }
688 builder.finish_node();
689 Relations(SyntaxNode::new_root_mut(builder.finish()))
690 }
691}
692
693impl From<Entry> for Relations {
694 fn from(entry: Entry) -> Self {
695 Self::from(vec![entry])
696 }
697}
698
699impl Default for Entry {
700 fn default() -> Self {
701 Self::new()
702 }
703}
704
705impl PartialOrd for Entry {
706 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
707 let mut rels_a = self.relations();
708 let mut rels_b = other.relations();
709 while let (Some(a), Some(b)) = (rels_a.next(), rels_b.next()) {
710 match a.cmp(&b) {
711 std::cmp::Ordering::Equal => continue,
712 x => return Some(x),
713 }
714 }
715
716 if rels_a.next().is_some() {
717 return Some(std::cmp::Ordering::Greater);
718 }
719
720 if rels_b.next().is_some() {
721 return Some(std::cmp::Ordering::Less);
722 }
723
724 Some(std::cmp::Ordering::Equal)
725 }
726}
727
728impl Eq for Entry {}
729
730impl Ord for Entry {
731 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
732 self.partial_cmp(other).unwrap()
733 }
734}
735
736impl Entry {
737 pub fn new() -> Self {
739 let mut builder = GreenNodeBuilder::new();
740 builder.start_node(SyntaxKind::ENTRY.into());
741 builder.finish_node();
742 Entry(SyntaxNode::new_root_mut(builder.finish()))
743 }
744
745 pub fn replace(&mut self, idx: usize, relation: Relation) {
747 let current_relation = self.get_relation(idx).unwrap();
748
749 let old_root = current_relation.0;
750 let new_root = relation.0;
751 let mut prev = new_root.first_child_or_token();
753 let mut new_head_len = 0;
754 while let Some(p) = prev {
756 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
757 new_head_len += 1;
758 prev = p.next_sibling_or_token();
759 } else {
760 break;
761 }
762 }
763 let mut new_tail_len = 0;
764 let mut next = new_root.last_child_or_token();
765 while let Some(n) = next {
766 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
767 new_tail_len += 1;
768 next = n.prev_sibling_or_token();
769 } else {
770 break;
771 }
772 }
773 let mut prev = old_root.first_child_or_token();
775 let mut old_head = vec![];
776 while let Some(p) = prev {
777 if p.kind() == WHITESPACE || p.kind() == NEWLINE {
778 old_head.push(p.clone());
779 prev = p.next_sibling_or_token();
780 } else {
781 break;
782 }
783 }
784 let mut old_tail = vec![];
785 let mut next = old_root.last_child_or_token();
786 while let Some(n) = next {
787 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
788 old_tail.push(n.clone());
789 next = n.prev_sibling_or_token();
790 } else {
791 break;
792 }
793 }
794 new_root.splice_children(0..new_head_len, old_head);
795 let tail_pos = new_root.children_with_tokens().count() - new_tail_len;
796 new_root.splice_children(
797 tail_pos - new_tail_len..tail_pos,
798 old_tail.into_iter().rev(),
799 );
800 let index = old_root.index();
801 self.0
802 .splice_children(index..index + 1, vec![new_root.into()]);
803 }
804
805 #[must_use]
807 pub fn wrap_and_sort(&self) -> Self {
808 let mut relations = self
809 .relations()
810 .map(|r| r.wrap_and_sort())
811 .collect::<Vec<_>>();
812 relations.sort();
814 Self::from(relations)
815 }
816
817 pub fn relations(&self) -> impl Iterator<Item = Relation> + '_ {
819 self.0.children().filter_map(Relation::cast)
820 }
821
822 pub fn iter(&self) -> impl Iterator<Item = Relation> + '_ {
824 self.relations()
825 }
826
827 pub fn get_relation(&self, idx: usize) -> Option<Relation> {
829 self.relations().nth(idx)
830 }
831
832 pub fn remove_relation(&self, idx: usize) -> Relation {
845 let mut relation = self.get_relation(idx).unwrap();
846 relation.remove();
847 relation
848 }
849
850 pub fn satisfied_by(&self, package_version: impl crate::VersionLookup + Copy) -> bool {
866 self.relations().any(|r| {
867 let actual = package_version.lookup_version(r.name().as_str());
868 if let Some((vc, version)) = r.version() {
869 if let Some(actual) = actual {
870 match vc {
871 VersionConstraint::GreaterThanEqual => *actual >= version,
872 VersionConstraint::LessThanEqual => *actual <= version,
873 VersionConstraint::Equal => *actual == version,
874 VersionConstraint::GreaterThan => *actual > version,
875 VersionConstraint::LessThan => *actual < version,
876 }
877 } else {
878 false
879 }
880 } else {
881 actual.is_some()
882 }
883 })
884 }
885
886 pub fn remove(&mut self) {
897 let mut removed_comma = false;
898 let is_first = !self
899 .0
900 .siblings(Direction::Prev)
901 .skip(1)
902 .any(|n| n.kind() == ENTRY);
903 while let Some(n) = self.0.next_sibling_or_token() {
904 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
905 n.detach();
906 } else if n.kind() == COMMA {
907 n.detach();
908 removed_comma = true;
909 break;
910 } else {
911 panic!("Unexpected node: {:?}", n);
912 }
913 }
914 if !is_first {
915 while let Some(n) = self.0.prev_sibling_or_token() {
916 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
917 n.detach();
918 } else if !removed_comma && n.kind() == COMMA {
919 n.detach();
920 break;
921 } else {
922 break;
923 }
924 }
925 } else {
926 while let Some(n) = self.0.next_sibling_or_token() {
927 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
928 n.detach();
929 } else {
930 break;
931 }
932 }
933 }
934 self.0.detach();
935 }
936
937 pub fn is_empty(&self) -> bool {
939 self.relations().count() == 0
940 }
941
942 pub fn len(&self) -> usize {
944 self.relations().count()
945 }
946
947 pub fn push(&mut self, relation: Relation) {
960 let is_empty = !self
961 .0
962 .children_with_tokens()
963 .any(|n| n.kind() == PIPE || n.kind() == RELATION);
964
965 let (position, new_children) = if let Some(current_relation) = self.relations().last() {
966 let to_insert: Vec<NodeOrToken<GreenNode, GreenToken>> = if is_empty {
967 vec![relation.0.green().into()]
968 } else {
969 vec![
970 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
971 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
972 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
973 relation.0.green().into(),
974 ]
975 };
976
977 (current_relation.0.index() + 1, to_insert)
978 } else {
979 let child_count = self.0.children_with_tokens().count();
980 (
981 child_count,
982 if is_empty {
983 vec![relation.0.green().into()]
984 } else {
985 vec![
986 NodeOrToken::Token(GreenToken::new(PIPE.into(), "|")),
987 NodeOrToken::Token(GreenToken::new(WHITESPACE.into(), " ")),
988 relation.0.green().into(),
989 ]
990 },
991 )
992 };
993
994 let new_root = SyntaxNode::new_root_mut(
995 self.0.replace_with(
996 self.0
997 .green()
998 .splice_children(position..position, new_children),
999 ),
1000 );
1001
1002 if let Some(parent) = self.0.parent() {
1003 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
1004 self.0 = parent
1005 .children_with_tokens()
1006 .nth(self.0.index())
1007 .unwrap()
1008 .clone()
1009 .into_node()
1010 .unwrap();
1011 } else {
1012 self.0 = new_root;
1013 }
1014 }
1015}
1016
1017fn inject(builder: &mut GreenNodeBuilder, node: SyntaxNode) {
1018 builder.start_node(node.kind().into());
1019 for child in node.children_with_tokens() {
1020 match child {
1021 rowan::NodeOrToken::Node(child) => {
1022 inject(builder, child);
1023 }
1024 rowan::NodeOrToken::Token(token) => {
1025 builder.token(token.kind().into(), token.text());
1026 }
1027 }
1028 }
1029 builder.finish_node();
1030}
1031
1032impl From<Vec<Relation>> for Entry {
1033 fn from(relations: Vec<Relation>) -> Self {
1034 let mut builder = GreenNodeBuilder::new();
1035 builder.start_node(SyntaxKind::ENTRY.into());
1036 for (i, relation) in relations.into_iter().enumerate() {
1037 if i > 0 {
1038 builder.token(WHITESPACE.into(), " ");
1039 builder.token(COMMA.into(), "|");
1040 builder.token(WHITESPACE.into(), " ");
1041 }
1042 inject(&mut builder, relation.0);
1043 }
1044 builder.finish_node();
1045 Entry(SyntaxNode::new_root_mut(builder.finish()))
1046 }
1047}
1048
1049impl From<Relation> for Entry {
1050 fn from(relation: Relation) -> Self {
1051 Self::from(vec![relation])
1052 }
1053}
1054
1055impl Relation {
1056 pub fn new(name: &str, version_constraint: Option<(VersionConstraint, Version)>) -> Self {
1070 let mut builder = GreenNodeBuilder::new();
1071 builder.start_node(SyntaxKind::RELATION.into());
1072 builder.token(IDENT.into(), name);
1073 if let Some((vc, version)) = version_constraint {
1074 builder.token(WHITESPACE.into(), " ");
1075 builder.start_node(SyntaxKind::VERSION.into());
1076 builder.token(L_PARENS.into(), "(");
1077 builder.start_node(SyntaxKind::CONSTRAINT.into());
1078 for c in vc.to_string().chars() {
1079 builder.token(
1080 match c {
1081 '>' => R_ANGLE.into(),
1082 '<' => L_ANGLE.into(),
1083 '=' => EQUAL.into(),
1084 _ => unreachable!(),
1085 },
1086 c.to_string().as_str(),
1087 );
1088 }
1089 builder.finish_node();
1090
1091 builder.token(WHITESPACE.into(), " ");
1092
1093 builder.token(IDENT.into(), version.to_string().as_str());
1094
1095 builder.token(R_PARENS.into(), ")");
1096
1097 builder.finish_node();
1098 }
1099
1100 builder.finish_node();
1101 Relation(SyntaxNode::new_root_mut(builder.finish()))
1102 }
1103
1104 #[must_use]
1113 pub fn wrap_and_sort(&self) -> Self {
1114 let mut builder = GreenNodeBuilder::new();
1115 builder.start_node(SyntaxKind::RELATION.into());
1116 builder.token(IDENT.into(), self.name().as_str());
1117 if let Some(archqual) = self.archqual() {
1118 builder.token(COLON.into(), ":");
1119 builder.token(IDENT.into(), archqual.as_str());
1120 }
1121 if let Some((vc, version)) = self.version() {
1122 builder.token(WHITESPACE.into(), " ");
1123 builder.start_node(SyntaxKind::VERSION.into());
1124 builder.token(L_PARENS.into(), "(");
1125 builder.start_node(SyntaxKind::CONSTRAINT.into());
1126 builder.token(
1127 match vc {
1128 VersionConstraint::GreaterThanEqual => R_ANGLE.into(),
1129 VersionConstraint::LessThanEqual => L_ANGLE.into(),
1130 VersionConstraint::Equal => EQUAL.into(),
1131 VersionConstraint::GreaterThan => R_ANGLE.into(),
1132 VersionConstraint::LessThan => L_ANGLE.into(),
1133 },
1134 vc.to_string().as_str(),
1135 );
1136 builder.finish_node();
1137 builder.token(WHITESPACE.into(), " ");
1138 builder.token(IDENT.into(), version.to_string().as_str());
1139 builder.token(R_PARENS.into(), ")");
1140 builder.finish_node();
1141 }
1142 if let Some(architectures) = self.architectures() {
1143 builder.token(WHITESPACE.into(), " ");
1144 builder.start_node(ARCHITECTURES.into());
1145 builder.token(L_BRACKET.into(), "[");
1146 for (i, arch) in architectures.enumerate() {
1147 if i > 0 {
1148 builder.token(WHITESPACE.into(), " ");
1149 }
1150 builder.token(IDENT.into(), arch.as_str());
1151 }
1152 builder.token(R_BRACKET.into(), "]");
1153 builder.finish_node();
1154 }
1155 for profiles in self.profiles() {
1156 builder.token(WHITESPACE.into(), " ");
1157 builder.start_node(PROFILES.into());
1158 builder.token(L_ANGLE.into(), "<");
1159 for (i, profile) in profiles.into_iter().enumerate() {
1160 if i > 0 {
1161 builder.token(WHITESPACE.into(), " ");
1162 }
1163 match profile {
1164 BuildProfile::Disabled(name) => {
1165 builder.token(NOT.into(), "!");
1166 builder.token(IDENT.into(), name.as_str());
1167 }
1168 BuildProfile::Enabled(name) => {
1169 builder.token(IDENT.into(), name.as_str());
1170 }
1171 }
1172 }
1173 builder.token(R_ANGLE.into(), ">");
1174 builder.finish_node();
1175 }
1176 builder.finish_node();
1177 Relation(SyntaxNode::new_root_mut(builder.finish()))
1178 }
1179
1180 pub fn simple(name: &str) -> Self {
1189 Self::new(name, None)
1190 }
1191
1192 pub fn drop_constraint(&mut self) -> bool {
1203 let version_token = self.0.children().find(|n| n.kind() == VERSION);
1204 if let Some(version_token) = version_token {
1205 while let Some(prev) = version_token.prev_sibling_or_token() {
1207 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
1208 prev.detach();
1209 } else {
1210 break;
1211 }
1212 }
1213 version_token.detach();
1214 return true;
1215 }
1216
1217 false
1218 }
1219
1220 pub fn name(&self) -> String {
1229 self.0
1230 .children_with_tokens()
1231 .find_map(|it| match it {
1232 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
1233 _ => None,
1234 })
1235 .unwrap()
1236 .text()
1237 .to_string()
1238 }
1239
1240 pub fn archqual(&self) -> Option<String> {
1249 let archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
1250 let node = if let Some(archqual) = archqual {
1251 archqual.children_with_tokens().find_map(|it| match it {
1252 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
1253 _ => None,
1254 })
1255 } else {
1256 None
1257 };
1258 node.map(|n| n.text().to_string())
1259 }
1260
1261 pub fn set_archqual(&mut self, archqual: &str) {
1271 let mut builder = GreenNodeBuilder::new();
1272 builder.start_node(ARCHQUAL.into());
1273 builder.token(COLON.into(), ":");
1274 builder.token(IDENT.into(), archqual);
1275 builder.finish_node();
1276
1277 let node_archqual = self.0.children().find(|n| n.kind() == ARCHQUAL);
1278 if let Some(node_archqual) = node_archqual {
1279 self.0.splice_children(
1280 node_archqual.index()..node_archqual.index() + 1,
1281 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
1282 );
1283 } else {
1284 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
1285 let idx = if let Some(name_node) = name_node {
1286 name_node.index() + 1
1287 } else {
1288 0
1289 };
1290 self.0.splice_children(
1291 idx..idx,
1292 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
1293 );
1294 }
1295 }
1296
1297 pub fn version(&self) -> Option<(VersionConstraint, Version)> {
1299 let vc = self.0.children().find(|n| n.kind() == VERSION);
1300 let vc = vc.as_ref()?;
1301 let constraint = vc.children().find(|n| n.kind() == CONSTRAINT);
1302
1303 let version = vc.children_with_tokens().find_map(|it| match it {
1304 SyntaxElement::Token(token) if token.kind() == IDENT => Some(token),
1305 _ => None,
1306 });
1307
1308 if let (Some(constraint), Some(version)) = (constraint, version) {
1309 let vc: VersionConstraint = constraint.to_string().parse().unwrap();
1310 Some((vc, (version.text().to_string()).parse().unwrap()))
1311 } else {
1312 None
1313 }
1314 }
1315
1316 pub fn set_version(&mut self, version_constraint: Option<(VersionConstraint, Version)>) {
1327 let current_version = self.0.children().find(|n| n.kind() == VERSION);
1328 if let Some((vc, version)) = version_constraint {
1329 let mut builder = GreenNodeBuilder::new();
1330 builder.start_node(VERSION.into());
1331 builder.token(L_PARENS.into(), "(");
1332 builder.start_node(CONSTRAINT.into());
1333 match vc {
1334 VersionConstraint::GreaterThanEqual => {
1335 builder.token(R_ANGLE.into(), ">");
1336 builder.token(EQUAL.into(), "=");
1337 }
1338 VersionConstraint::LessThanEqual => {
1339 builder.token(L_ANGLE.into(), "<");
1340 builder.token(EQUAL.into(), "=");
1341 }
1342 VersionConstraint::Equal => {
1343 builder.token(EQUAL.into(), "=");
1344 }
1345 VersionConstraint::GreaterThan => {
1346 builder.token(R_ANGLE.into(), ">");
1347 }
1348 VersionConstraint::LessThan => {
1349 builder.token(L_ANGLE.into(), "<");
1350 }
1351 }
1352 builder.finish_node(); builder.token(WHITESPACE.into(), " ");
1354 builder.token(IDENT.into(), version.to_string().as_str());
1355 builder.token(R_PARENS.into(), ")");
1356 builder.finish_node(); if let Some(current_version) = current_version {
1359 self.0.splice_children(
1360 current_version.index()..current_version.index() + 1,
1361 vec![SyntaxNode::new_root_mut(builder.finish()).into()],
1362 );
1363 } else {
1364 let name_node = self.0.children_with_tokens().find(|n| n.kind() == IDENT);
1365 let idx = if let Some(name_node) = name_node {
1366 name_node.index() + 1
1367 } else {
1368 0
1369 };
1370 let new_children = vec![
1371 GreenToken::new(WHITESPACE.into(), " ").into(),
1372 builder.finish().into(),
1373 ];
1374 let new_root = SyntaxNode::new_root_mut(
1375 self.0.green().splice_children(idx..idx, new_children),
1376 );
1377 if let Some(parent) = self.0.parent() {
1378 parent
1379 .splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
1380 self.0 = parent
1381 .children_with_tokens()
1382 .nth(self.0.index())
1383 .unwrap()
1384 .clone()
1385 .into_node()
1386 .unwrap();
1387 } else {
1388 self.0 = new_root;
1389 }
1390 }
1391 } else if let Some(current_version) = current_version {
1392 while let Some(prev) = current_version.prev_sibling_or_token() {
1394 if prev.kind() == WHITESPACE || prev.kind() == NEWLINE {
1395 prev.detach();
1396 } else {
1397 break;
1398 }
1399 }
1400 current_version.detach();
1401 }
1402 }
1403
1404 pub fn architectures(&self) -> Option<impl Iterator<Item = String> + '_> {
1413 let architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES)?;
1414
1415 Some(architectures.children_with_tokens().filter_map(|node| {
1416 let token = node.as_token()?;
1417 if token.kind() == IDENT {
1418 Some(token.text().to_string())
1419 } else {
1420 None
1421 }
1422 }))
1423 }
1424
1425 pub fn profiles(&self) -> impl Iterator<Item = Vec<BuildProfile>> + '_ {
1435 let profiles = self.0.children().filter(|n| n.kind() == PROFILES);
1436
1437 profiles.map(|profile| {
1438 let mut ret = vec![];
1440 let mut current = vec![];
1441 for token in profile.children_with_tokens() {
1442 match token.kind() {
1443 WHITESPACE | NEWLINE => {
1444 if !current.is_empty() {
1445 ret.push(current.join("").parse::<BuildProfile>().unwrap());
1446 current = vec![];
1447 }
1448 }
1449 L_ANGLE | R_ANGLE => {}
1450 _ => {
1451 current.push(token.to_string());
1452 }
1453 }
1454 }
1455 if !current.is_empty() {
1456 ret.push(current.concat().parse().unwrap());
1457 }
1458 ret
1459 })
1460 }
1461
1462 pub fn remove(&mut self) {
1473 let is_first = !self
1474 .0
1475 .siblings(Direction::Prev)
1476 .skip(1)
1477 .any(|n| n.kind() == RELATION);
1478 if !is_first {
1479 while let Some(n) = self.0.prev_sibling_or_token() {
1482 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1483 n.detach();
1484 } else if n.kind() == PIPE {
1485 n.detach();
1486 break;
1487 } else {
1488 break;
1489 }
1490 }
1491 while let Some(n) = self.0.prev_sibling_or_token() {
1492 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1493 n.detach();
1494 } else {
1495 break;
1496 }
1497 }
1498 } else {
1499 while let Some(n) = self.0.next_sibling_or_token() {
1502 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1503 n.detach();
1504 } else if n.kind() == PIPE {
1505 n.detach();
1506 break;
1507 } else {
1508 panic!("Unexpected node: {:?}", n);
1509 }
1510 }
1511
1512 while let Some(n) = self.0.next_sibling_or_token() {
1513 if n.kind() == WHITESPACE || n.kind() == NEWLINE {
1514 n.detach();
1515 } else {
1516 break;
1517 }
1518 }
1519 }
1520 if let Some(mut parent) = self.0.parent().and_then(Entry::cast) {
1522 if parent.is_empty() {
1523 parent.remove();
1524 } else {
1525 self.0.detach();
1526 }
1527 } else {
1528 self.0.detach();
1529 }
1530 }
1531
1532 pub fn set_architectures<'a>(&mut self, architectures: impl Iterator<Item = &'a str>) {
1542 let mut builder = GreenNodeBuilder::new();
1543 builder.start_node(ARCHITECTURES.into());
1544 builder.token(L_BRACKET.into(), "[");
1545 for (i, arch) in architectures.enumerate() {
1546 if i > 0 {
1547 builder.token(WHITESPACE.into(), " ");
1548 }
1549 builder.token(IDENT.into(), arch);
1550 }
1551 builder.token(R_BRACKET.into(), "]");
1552 builder.finish_node();
1553
1554 let node_architectures = self.0.children().find(|n| n.kind() == ARCHITECTURES);
1555 if let Some(node_architectures) = node_architectures {
1556 let new_root = SyntaxNode::new_root_mut(builder.finish());
1557 self.0.splice_children(
1558 node_architectures.index()..node_architectures.index() + 1,
1559 vec![new_root.into()],
1560 );
1561 } else {
1562 let profiles = self.0.children().find(|n| n.kind() == PROFILES);
1563 let idx = if let Some(profiles) = profiles {
1564 profiles.index()
1565 } else {
1566 self.0.children_with_tokens().count()
1567 };
1568 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
1569 idx..idx,
1570 vec![
1571 GreenToken::new(WHITESPACE.into(), " ").into(),
1572 builder.finish().into(),
1573 ],
1574 ));
1575 if let Some(parent) = self.0.parent() {
1576 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
1577 self.0 = parent
1578 .children_with_tokens()
1579 .nth(self.0.index())
1580 .unwrap()
1581 .clone()
1582 .into_node()
1583 .unwrap();
1584 } else {
1585 self.0 = new_root;
1586 }
1587 }
1588 }
1589
1590 pub fn add_profile(&mut self, profile: &[BuildProfile]) {
1601 let mut builder = GreenNodeBuilder::new();
1602 builder.start_node(PROFILES.into());
1603 builder.token(L_ANGLE.into(), "<");
1604 for (i, profile) in profile.iter().enumerate() {
1605 if i > 0 {
1606 builder.token(WHITESPACE.into(), " ");
1607 }
1608 match profile {
1609 BuildProfile::Disabled(name) => {
1610 builder.token(NOT.into(), "!");
1611 builder.token(IDENT.into(), name.as_str());
1612 }
1613 BuildProfile::Enabled(name) => {
1614 builder.token(IDENT.into(), name.as_str());
1615 }
1616 }
1617 }
1618 builder.token(R_ANGLE.into(), ">");
1619 builder.finish_node();
1620
1621 let node_profiles = self.0.children().find(|n| n.kind() == PROFILES);
1622 if let Some(node_profiles) = node_profiles {
1623 let new_root = SyntaxNode::new_root_mut(builder.finish());
1624 self.0.splice_children(
1625 node_profiles.index()..node_profiles.index() + 1,
1626 vec![new_root.into()],
1627 );
1628 } else {
1629 let idx = self.0.children_with_tokens().count();
1630 let new_root = SyntaxNode::new_root(self.0.green().splice_children(
1631 idx..idx,
1632 vec![
1633 GreenToken::new(WHITESPACE.into(), " ").into(),
1634 builder.finish().into(),
1635 ],
1636 ));
1637 if let Some(parent) = self.0.parent() {
1638 parent.splice_children(self.0.index()..self.0.index() + 1, vec![new_root.into()]);
1639 self.0 = parent
1640 .children_with_tokens()
1641 .nth(self.0.index())
1642 .unwrap()
1643 .clone()
1644 .into_node()
1645 .unwrap();
1646 } else {
1647 self.0 = new_root;
1648 }
1649 }
1650 }
1651
1652 pub fn build(name: &str) -> RelationBuilder {
1654 RelationBuilder::new(name)
1655 }
1656}
1657
1658pub struct RelationBuilder {
1672 name: String,
1673 version_constraint: Option<(VersionConstraint, Version)>,
1674 archqual: Option<String>,
1675 architectures: Vec<String>,
1676 profiles: Vec<Vec<BuildProfile>>,
1677}
1678
1679impl RelationBuilder {
1680 fn new(name: &str) -> Self {
1682 Self {
1683 name: name.to_string(),
1684 version_constraint: None,
1685 archqual: None,
1686 architectures: vec![],
1687 profiles: vec![],
1688 }
1689 }
1690
1691 pub fn version_constraint(mut self, vc: VersionConstraint, version: Version) -> Self {
1693 self.version_constraint = Some((vc, version));
1694 self
1695 }
1696
1697 pub fn archqual(mut self, archqual: &str) -> Self {
1699 self.archqual = Some(archqual.to_string());
1700 self
1701 }
1702
1703 pub fn architectures(mut self, architectures: Vec<String>) -> Self {
1705 self.architectures = architectures;
1706 self
1707 }
1708
1709 pub fn profiles(mut self, profiles: Vec<Vec<BuildProfile>>) -> Self {
1711 self.profiles = profiles;
1712 self
1713 }
1714
1715 pub fn add_profile(mut self, profile: Vec<BuildProfile>) -> Self {
1717 self.profiles.push(profile);
1718 self
1719 }
1720
1721 pub fn build(self) -> Relation {
1723 let mut relation = Relation::new(&self.name, self.version_constraint);
1724 if let Some(archqual) = &self.archqual {
1725 relation.set_archqual(archqual);
1726 }
1727 relation.set_architectures(self.architectures.iter().map(|s| s.as_str()));
1728 for profile in &self.profiles {
1729 relation.add_profile(profile);
1730 }
1731 relation
1732 }
1733}
1734
1735impl PartialOrd for Relation {
1736 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
1737 let name_cmp = self.name().cmp(&other.name());
1739 if name_cmp != std::cmp::Ordering::Equal {
1740 return Some(name_cmp);
1741 }
1742
1743 let self_version = self.version();
1744 let other_version = other.version();
1745
1746 match (self_version, other_version) {
1747 (Some((self_vc, self_version)), Some((other_vc, other_version))) => {
1748 let vc_cmp = self_vc.cmp(&other_vc);
1749 if vc_cmp != std::cmp::Ordering::Equal {
1750 return Some(vc_cmp);
1751 }
1752
1753 Some(self_version.cmp(&other_version))
1754 }
1755 (Some(_), None) => Some(std::cmp::Ordering::Greater),
1756 (None, Some(_)) => Some(std::cmp::Ordering::Less),
1757 (None, None) => Some(std::cmp::Ordering::Equal),
1758 }
1759 }
1760}
1761
1762impl Eq for Relation {}
1763
1764impl Ord for Relation {
1765 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
1766 self.partial_cmp(other).unwrap()
1767 }
1768}
1769
1770impl std::str::FromStr for Relations {
1771 type Err = String;
1772
1773 fn from_str(s: &str) -> Result<Self, Self::Err> {
1774 let parse = parse(s, false);
1775 if parse.errors.is_empty() {
1776 Ok(parse.root_mut())
1777 } else {
1778 Err(parse.errors.join("\n"))
1779 }
1780 }
1781}
1782
1783impl std::str::FromStr for Entry {
1784 type Err = String;
1785
1786 fn from_str(s: &str) -> Result<Self, Self::Err> {
1787 let root: Relations = s.parse()?;
1788
1789 let mut entries = root.entries();
1790 let entry = if let Some(entry) = entries.next() {
1791 entry
1792 } else {
1793 return Err("No entry found".to_string());
1794 };
1795
1796 if entries.next().is_some() {
1797 return Err("Multiple entries found".to_string());
1798 }
1799
1800 Ok(entry)
1801 }
1802}
1803
1804impl std::str::FromStr for Relation {
1805 type Err = String;
1806
1807 fn from_str(s: &str) -> Result<Self, Self::Err> {
1808 let entry: Entry = s.parse()?;
1809
1810 let mut relations = entry.relations();
1811 let relation = if let Some(relation) = relations.next() {
1812 relation
1813 } else {
1814 return Err("No relation found".to_string());
1815 };
1816
1817 if relations.next().is_some() {
1818 return Err("Multiple relations found".to_string());
1819 }
1820
1821 Ok(relation)
1822 }
1823}
1824
1825impl From<crate::lossy::Relation> for Relation {
1826 fn from(relation: crate::lossy::Relation) -> Self {
1827 let mut builder = Relation::build(&relation.name);
1828
1829 if let Some((vc, version)) = relation.version {
1830 builder = builder.version_constraint(vc, version);
1831 }
1832
1833 if let Some(archqual) = relation.archqual {
1834 builder = builder.archqual(&archqual);
1835 }
1836
1837 if let Some(architectures) = relation.architectures {
1838 builder = builder.architectures(architectures);
1839 }
1840
1841 builder = builder.profiles(relation.profiles);
1842
1843 builder.build()
1844 }
1845}
1846
1847impl From<Relation> for crate::lossy::Relation {
1848 fn from(relation: Relation) -> Self {
1849 crate::lossy::Relation {
1850 name: relation.name(),
1851 version: relation.version(),
1852 archqual: relation.archqual(),
1853 architectures: relation.architectures().map(|a| a.collect()),
1854 profiles: relation.profiles().collect(),
1855 }
1856 }
1857}
1858
1859impl From<Entry> for Vec<crate::lossy::Relation> {
1860 fn from(entry: Entry) -> Self {
1861 entry.relations().map(|r| r.into()).collect()
1862 }
1863}
1864
1865impl From<Vec<crate::lossy::Relation>> for Entry {
1866 fn from(relations: Vec<crate::lossy::Relation>) -> Self {
1867 let relations: Vec<Relation> = relations.into_iter().map(|r| r.into()).collect();
1868 Entry::from(relations)
1869 }
1870}
1871
1872#[cfg(test)]
1873mod tests {
1874 use super::*;
1875
1876 #[test]
1877 fn test_parse() {
1878 let input = "python3-dulwich";
1879 let parsed: Relations = input.parse().unwrap();
1880 assert_eq!(parsed.to_string(), input);
1881 assert_eq!(parsed.entries().count(), 1);
1882 let entry = parsed.entries().next().unwrap();
1883 assert_eq!(entry.to_string(), "python3-dulwich");
1884 assert_eq!(entry.relations().count(), 1);
1885 let relation = entry.relations().next().unwrap();
1886 assert_eq!(relation.to_string(), "python3-dulwich");
1887 assert_eq!(relation.version(), None);
1888
1889 let input = "python3-dulwich (>= 0.20.21)";
1890 let parsed: Relations = input.parse().unwrap();
1891 assert_eq!(parsed.to_string(), input);
1892 assert_eq!(parsed.entries().count(), 1);
1893 let entry = parsed.entries().next().unwrap();
1894 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
1895 assert_eq!(entry.relations().count(), 1);
1896 let relation = entry.relations().next().unwrap();
1897 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
1898 assert_eq!(
1899 relation.version(),
1900 Some((
1901 VersionConstraint::GreaterThanEqual,
1902 "0.20.21".parse().unwrap()
1903 ))
1904 );
1905 }
1906
1907 #[test]
1908 fn test_multiple() {
1909 let input = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)";
1910 let parsed: Relations = input.parse().unwrap();
1911 assert_eq!(parsed.to_string(), input);
1912 assert_eq!(parsed.entries().count(), 2);
1913 let entry = parsed.entries().next().unwrap();
1914 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
1915 assert_eq!(entry.relations().count(), 1);
1916 let relation = entry.relations().next().unwrap();
1917 assert_eq!(relation.to_string(), "python3-dulwich (>= 0.20.21)");
1918 assert_eq!(
1919 relation.version(),
1920 Some((
1921 VersionConstraint::GreaterThanEqual,
1922 "0.20.21".parse().unwrap()
1923 ))
1924 );
1925 let entry = parsed.entries().nth(1).unwrap();
1926 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.21)");
1927 assert_eq!(entry.relations().count(), 1);
1928 let relation = entry.relations().next().unwrap();
1929 assert_eq!(relation.to_string(), "python3-dulwich (<< 0.21)");
1930 assert_eq!(
1931 relation.version(),
1932 Some((VersionConstraint::LessThan, "0.21".parse().unwrap()))
1933 );
1934 }
1935
1936 #[test]
1937 fn test_architectures() {
1938 let input = "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]";
1939 let parsed: Relations = input.parse().unwrap();
1940 assert_eq!(parsed.to_string(), input);
1941 assert_eq!(parsed.entries().count(), 1);
1942 let entry = parsed.entries().next().unwrap();
1943 assert_eq!(
1944 entry.to_string(),
1945 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
1946 );
1947 assert_eq!(entry.relations().count(), 1);
1948 let relation = entry.relations().next().unwrap();
1949 assert_eq!(
1950 relation.to_string(),
1951 "python3-dulwich [amd64 arm64 armhf i386 mips mips64el mipsel ppc64el s390x]"
1952 );
1953 assert_eq!(relation.version(), None);
1954 assert_eq!(
1955 relation.architectures().unwrap().collect::<Vec<_>>(),
1956 vec![
1957 "amd64", "arm64", "armhf", "i386", "mips", "mips64el", "mipsel", "ppc64el", "s390x"
1958 ]
1959 .into_iter()
1960 .map(|s| s.to_string())
1961 .collect::<Vec<_>>()
1962 );
1963 }
1964
1965 #[test]
1966 fn test_profiles() {
1967 let input = "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>, bar";
1968 let parsed: Relations = input.parse().unwrap();
1969 assert_eq!(parsed.to_string(), input);
1970 assert_eq!(parsed.entries().count(), 2);
1971 let entry = parsed.entries().next().unwrap();
1972 assert_eq!(
1973 entry.to_string(),
1974 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
1975 );
1976 assert_eq!(entry.relations().count(), 1);
1977 let relation = entry.relations().next().unwrap();
1978 assert_eq!(
1979 relation.to_string(),
1980 "foo (>= 1.0) [i386 arm] <!nocheck> <!cross>"
1981 );
1982 assert_eq!(
1983 relation.version(),
1984 Some((VersionConstraint::GreaterThanEqual, "1.0".parse().unwrap()))
1985 );
1986 assert_eq!(
1987 relation.architectures().unwrap().collect::<Vec<_>>(),
1988 vec!["i386", "arm"]
1989 .into_iter()
1990 .map(|s| s.to_string())
1991 .collect::<Vec<_>>()
1992 );
1993 assert_eq!(
1994 relation.profiles().collect::<Vec<_>>(),
1995 vec![
1996 vec![BuildProfile::Disabled("nocheck".to_string())],
1997 vec![BuildProfile::Disabled("cross".to_string())]
1998 ]
1999 );
2000 }
2001
2002 #[test]
2003 fn test_substvar() {
2004 let input = "${shlibs:Depends}";
2005
2006 let (parsed, errors) = Relations::parse_relaxed(input, true);
2007 assert_eq!(errors, Vec::<String>::new());
2008 assert_eq!(parsed.to_string(), input);
2009 assert_eq!(parsed.entries().count(), 0);
2010
2011 assert_eq!(
2012 parsed.substvars().collect::<Vec<_>>(),
2013 vec!["${shlibs:Depends}"]
2014 );
2015 }
2016
2017 #[test]
2018 fn test_new() {
2019 let r = Relation::new(
2020 "samba",
2021 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
2022 );
2023
2024 assert_eq!(r.to_string(), "samba (>= 2.0)");
2025 }
2026
2027 #[test]
2028 fn test_drop_constraint() {
2029 let mut r = Relation::new(
2030 "samba",
2031 Some((VersionConstraint::GreaterThanEqual, "2.0".parse().unwrap())),
2032 );
2033
2034 r.drop_constraint();
2035
2036 assert_eq!(r.to_string(), "samba");
2037 }
2038
2039 #[test]
2040 fn test_simple() {
2041 let r = Relation::simple("samba");
2042
2043 assert_eq!(r.to_string(), "samba");
2044 }
2045
2046 #[test]
2047 fn test_remove_first_entry() {
2048 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2049 .parse()
2050 .unwrap();
2051 let removed = rels.remove_entry(0);
2052 assert_eq!(removed.to_string(), "python3-dulwich (>= 0.20.21)");
2053 assert_eq!(rels.to_string(), "python3-dulwich (<< 0.21)");
2054 }
2055
2056 #[test]
2057 fn test_remove_last_entry() {
2058 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2059 .parse()
2060 .unwrap();
2061 rels.remove_entry(1);
2062 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
2063 }
2064
2065 #[test]
2066 fn test_remove_middle() {
2067 let mut rels: Relations =
2068 r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21), python3-dulwich (<< 0.22)"#
2069 .parse()
2070 .unwrap();
2071 rels.remove_entry(1);
2072 assert_eq!(
2073 rels.to_string(),
2074 "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.22)"
2075 );
2076 }
2077
2078 #[test]
2079 fn test_remove_added() {
2080 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
2081 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2082 rels.push(entry);
2083 rels.remove_entry(1);
2084 assert_eq!(rels.to_string(), "python3-dulwich (>= 0.20.21)");
2085 }
2086
2087 #[test]
2088 fn test_push() {
2089 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21)"#.parse().unwrap();
2090 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2091 rels.push(entry);
2092 assert_eq!(
2093 rels.to_string(),
2094 "python3-dulwich (>= 0.20.21), python3-dulwich"
2095 );
2096 }
2097
2098 #[test]
2099 fn test_push_from_empty() {
2100 let mut rels: Relations = "".parse().unwrap();
2101 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2102 rels.push(entry);
2103 assert_eq!(rels.to_string(), "python3-dulwich");
2104 }
2105
2106 #[test]
2107 fn test_insert() {
2108 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2109 .parse()
2110 .unwrap();
2111 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2112 rels.insert(1, entry);
2113 assert_eq!(
2114 rels.to_string(),
2115 "python3-dulwich (>= 0.20.21), python3-dulwich, python3-dulwich (<< 0.21)"
2116 );
2117 }
2118
2119 #[test]
2120 fn test_insert_at_start() {
2121 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2122 .parse()
2123 .unwrap();
2124 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2125 rels.insert(0, entry);
2126 assert_eq!(
2127 rels.to_string(),
2128 "python3-dulwich, python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
2129 );
2130 }
2131
2132 #[test]
2133 fn test_insert_after_error() {
2134 let (mut rels, errors) = Relations::parse_relaxed("@foo@, debhelper (>= 1.0)", false);
2135 assert_eq!(
2136 errors,
2137 vec![
2138 "expected $ or identifier but got ERROR",
2139 "expected comma or end of file but got Some(IDENT)",
2140 "expected $ or identifier but got ERROR"
2141 ]
2142 );
2143 let entry = Entry::from(vec![Relation::simple("bar")]);
2144 rels.push(entry);
2145 assert_eq!(rels.to_string(), "@foo@, debhelper (>= 1.0), bar");
2146 }
2147
2148 #[test]
2149 fn test_insert_before_error() {
2150 let (mut rels, errors) = Relations::parse_relaxed("debhelper (>= 1.0), @foo@, bla", false);
2151 assert_eq!(
2152 errors,
2153 vec![
2154 "expected $ or identifier but got ERROR",
2155 "expected comma or end of file but got Some(IDENT)",
2156 "expected $ or identifier but got ERROR"
2157 ]
2158 );
2159 let entry = Entry::from(vec![Relation::simple("bar")]);
2160 rels.insert(0, entry);
2161 assert_eq!(rels.to_string(), "bar, debhelper (>= 1.0), @foo@, bla");
2162 }
2163
2164 #[test]
2165 fn test_replace() {
2166 let mut rels: Relations = r#"python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"#
2167 .parse()
2168 .unwrap();
2169 let entry = Entry::from(vec![Relation::simple("python3-dulwich")]);
2170 rels.replace(1, entry);
2171 assert_eq!(
2172 rels.to_string(),
2173 "python3-dulwich (>= 0.20.21), python3-dulwich"
2174 );
2175 }
2176
2177 #[test]
2178 fn test_relation_from_entries() {
2179 let entries = vec![
2180 Entry::from(vec![Relation::simple("python3-dulwich")]),
2181 Entry::from(vec![Relation::simple("python3-breezy")]),
2182 ];
2183 let rels: Relations = entries.into();
2184 assert_eq!(rels.entries().count(), 2);
2185 assert_eq!(rels.to_string(), "python3-dulwich, python3-breezy");
2186 }
2187
2188 #[test]
2189 fn test_entry_from_relations() {
2190 let relations = vec![
2191 Relation::simple("python3-dulwich"),
2192 Relation::simple("python3-breezy"),
2193 ];
2194 let entry: Entry = relations.into();
2195 assert_eq!(entry.relations().count(), 2);
2196 assert_eq!(entry.to_string(), "python3-dulwich | python3-breezy");
2197 }
2198
2199 #[test]
2200 fn test_parse_entry() {
2201 let parsed: Entry = "python3-dulwich (>= 0.20.21) | bar".parse().unwrap();
2202 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21) | bar");
2203 assert_eq!(parsed.relations().count(), 2);
2204
2205 assert_eq!(
2206 "foo, bar".parse::<Entry>().unwrap_err(),
2207 "Multiple entries found"
2208 );
2209 assert_eq!("".parse::<Entry>().unwrap_err(), "No entry found");
2210 }
2211
2212 #[test]
2213 fn test_parse_relation() {
2214 let parsed: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
2215 assert_eq!(parsed.to_string(), "python3-dulwich (>= 0.20.21)");
2216 assert_eq!(
2217 parsed.version(),
2218 Some((
2219 VersionConstraint::GreaterThanEqual,
2220 "0.20.21".parse().unwrap()
2221 ))
2222 );
2223 assert_eq!(
2224 "foo | bar".parse::<Relation>().unwrap_err(),
2225 "Multiple relations found"
2226 );
2227 assert_eq!("".parse::<Relation>().unwrap_err(), "No entry found");
2228 }
2229
2230 #[test]
2231 fn test_special() {
2232 let parsed: Relation = "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
2233 .parse()
2234 .unwrap();
2235 assert_eq!(
2236 parsed.to_string(),
2237 "librust-breezyshim+dirty-tracker-dev:amd64 (>= 0.1.138-~~)"
2238 );
2239 assert_eq!(
2240 parsed.version(),
2241 Some((
2242 VersionConstraint::GreaterThanEqual,
2243 "0.1.138-~~".parse().unwrap()
2244 ))
2245 );
2246 assert_eq!(parsed.archqual(), Some("amd64".to_string()));
2247 assert_eq!(parsed.name(), "librust-breezyshim+dirty-tracker-dev");
2248 }
2249
2250 #[test]
2251 fn test_relations_satisfied_by() {
2252 let rels: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
2253 .parse()
2254 .unwrap();
2255 let satisfied = |name: &str| -> Option<debversion::Version> {
2256 match name {
2257 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
2258 _ => None,
2259 }
2260 };
2261 assert!(rels.satisfied_by(satisfied));
2262
2263 let satisfied = |name: &str| match name {
2264 "python3-dulwich" => Some("0.21".parse().unwrap()),
2265 _ => None,
2266 };
2267 assert!(!rels.satisfied_by(satisfied));
2268
2269 let satisfied = |name: &str| match name {
2270 "python3-dulwich" => Some("0.20.20".parse().unwrap()),
2271 _ => None,
2272 };
2273 assert!(!rels.satisfied_by(satisfied));
2274 }
2275
2276 #[test]
2277 fn test_entry_satisfied_by() {
2278 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
2279 .parse()
2280 .unwrap();
2281 let satisfied = |name: &str| -> Option<debversion::Version> {
2282 match name {
2283 "python3-dulwich" => Some("0.20.21".parse().unwrap()),
2284 _ => None,
2285 }
2286 };
2287 assert!(entry.satisfied_by(satisfied));
2288 let satisfied = |name: &str| -> Option<debversion::Version> {
2289 match name {
2290 "python3-dulwich" => Some("0.18".parse().unwrap()),
2291 _ => None,
2292 }
2293 };
2294 assert!(!entry.satisfied_by(satisfied));
2295 }
2296
2297 #[test]
2298 fn test_wrap_and_sort_relation() {
2299 let relation: Relation = " python3-dulwich (>= 11) [ amd64 ] < lala>"
2300 .parse()
2301 .unwrap();
2302
2303 let wrapped = relation.wrap_and_sort();
2304
2305 assert_eq!(
2306 wrapped.to_string(),
2307 "python3-dulwich (>= 11) [amd64] <lala>"
2308 );
2309 }
2310
2311 #[test]
2312 fn test_wrap_and_sort_relations() {
2313 let entry: Relations =
2314 "python3-dulwich (>= 0.20.21) | bar, \n\n\n\npython3-dulwich (<< 0.21)"
2315 .parse()
2316 .unwrap();
2317
2318 let wrapped = entry.wrap_and_sort();
2319
2320 assert_eq!(
2321 wrapped.to_string(),
2322 "bar | python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
2323 );
2324 }
2325
2326 #[cfg(feature = "serde")]
2327 #[test]
2328 fn test_serialize_relations() {
2329 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
2330 .parse()
2331 .unwrap();
2332 let serialized = serde_json::to_string(&relations).unwrap();
2333 assert_eq!(
2334 serialized,
2335 r#""python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)""#
2336 );
2337 }
2338
2339 #[cfg(feature = "serde")]
2340 #[test]
2341 fn test_deserialize_relations() {
2342 let relations: Relations = "python3-dulwich (>= 0.20.21), python3-dulwich (<< 0.21)"
2343 .parse()
2344 .unwrap();
2345 let serialized = serde_json::to_string(&relations).unwrap();
2346 let deserialized: Relations = serde_json::from_str(&serialized).unwrap();
2347 assert_eq!(deserialized.to_string(), relations.to_string());
2348 }
2349
2350 #[cfg(feature = "serde")]
2351 #[test]
2352 fn test_serialize_relation() {
2353 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
2354 let serialized = serde_json::to_string(&relation).unwrap();
2355 assert_eq!(serialized, r#""python3-dulwich (>= 0.20.21)""#);
2356 }
2357
2358 #[cfg(feature = "serde")]
2359 #[test]
2360 fn test_deserialize_relation() {
2361 let relation: Relation = "python3-dulwich (>= 0.20.21)".parse().unwrap();
2362 let serialized = serde_json::to_string(&relation).unwrap();
2363 let deserialized: Relation = serde_json::from_str(&serialized).unwrap();
2364 assert_eq!(deserialized.to_string(), relation.to_string());
2365 }
2366
2367 #[cfg(feature = "serde")]
2368 #[test]
2369 fn test_serialize_entry() {
2370 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
2371 .parse()
2372 .unwrap();
2373 let serialized = serde_json::to_string(&entry).unwrap();
2374 assert_eq!(
2375 serialized,
2376 r#""python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)""#
2377 );
2378 }
2379
2380 #[cfg(feature = "serde")]
2381 #[test]
2382 fn test_deserialize_entry() {
2383 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
2384 .parse()
2385 .unwrap();
2386 let serialized = serde_json::to_string(&entry).unwrap();
2387 let deserialized: Entry = serde_json::from_str(&serialized).unwrap();
2388 assert_eq!(deserialized.to_string(), entry.to_string());
2389 }
2390
2391 #[test]
2392 fn test_remove_first_relation() {
2393 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
2394 .parse()
2395 .unwrap();
2396 let mut rel = entry.relations().next().unwrap();
2397 rel.remove();
2398 assert_eq!(entry.to_string(), "python3-dulwich (<< 0.18)");
2399 }
2400
2401 #[test]
2402 fn test_remove_last_relation() {
2403 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
2404 .parse()
2405 .unwrap();
2406 let mut rel = entry.relations().nth(1).unwrap();
2407 rel.remove();
2408 assert_eq!(entry.to_string(), "python3-dulwich (>= 0.20.21)");
2409 }
2410
2411 #[test]
2412 fn test_remove_only_relation() {
2413 let entry: Entry = "python3-dulwich (>= 0.20.21)".parse().unwrap();
2414 let mut rel = entry.relations().next().unwrap();
2415 rel.remove();
2416 assert_eq!(entry.to_string(), "");
2417 }
2418
2419 #[test]
2420 fn test_relations_is_empty() {
2421 let entry: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
2422 assert!(!entry.is_empty());
2423 assert_eq!(1, entry.len());
2424 let mut rel = entry.entries().next().unwrap();
2425 rel.remove();
2426 assert!(entry.is_empty());
2427 assert_eq!(0, entry.len());
2428 }
2429
2430 #[test]
2431 fn test_entry_is_empty() {
2432 let entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
2433 .parse()
2434 .unwrap();
2435 assert!(!entry.is_empty());
2436 assert_eq!(2, entry.len());
2437 let mut rel = entry.relations().next().unwrap();
2438 rel.remove();
2439 assert!(!entry.is_empty());
2440 assert_eq!(1, entry.len());
2441 let mut rel = entry.relations().next().unwrap();
2442 rel.remove();
2443 assert!(entry.is_empty());
2444 assert_eq!(0, entry.len());
2445 }
2446
2447 #[test]
2448 fn test_relation_set_version() {
2449 let mut rel: Relation = "samba".parse().unwrap();
2450 rel.set_version(None);
2451 assert_eq!("samba", rel.to_string());
2452 rel.set_version(Some((
2453 VersionConstraint::GreaterThanEqual,
2454 "2.0".parse().unwrap(),
2455 )));
2456 assert_eq!("samba (>= 2.0)", rel.to_string());
2457 rel.set_version(None);
2458 assert_eq!("samba", rel.to_string());
2459 rel.set_version(Some((
2460 VersionConstraint::GreaterThanEqual,
2461 "2.0".parse().unwrap(),
2462 )));
2463 rel.set_version(Some((
2464 VersionConstraint::GreaterThanEqual,
2465 "1.1".parse().unwrap(),
2466 )));
2467 assert_eq!("samba (>= 1.1)", rel.to_string());
2468 }
2469
2470 #[test]
2471 fn test_replace_relation() {
2472 let mut entry: Entry = "python3-dulwich (>= 0.20.21) | python3-dulwich (<< 0.18)"
2473 .parse()
2474 .unwrap();
2475 let new_rel = Relation::simple("python3-breezy");
2476 entry.replace(0, new_rel);
2477 assert_eq!(
2478 entry.to_string(),
2479 "python3-breezy | python3-dulwich (<< 0.18)"
2480 );
2481 }
2482
2483 #[test]
2484 fn test_entry_push_relation() {
2485 let relations: Relations = "python3-dulwich (>= 0.20.21)".parse().unwrap();
2486 let new_rel = Relation::simple("python3-breezy");
2487 let mut entry = relations.entries().next().unwrap();
2488 entry.push(new_rel);
2489 assert_eq!(
2490 entry.to_string(),
2491 "python3-dulwich (>= 0.20.21) | python3-breezy"
2492 );
2493 assert_eq!(
2494 relations.to_string(),
2495 "python3-dulwich (>= 0.20.21) | python3-breezy"
2496 );
2497 }
2498
2499 #[test]
2500 fn test_relations_remove_empty_entry() {
2501 let (mut relations, errors) = Relations::parse_relaxed("foo, , bar, ", false);
2502 assert_eq!(errors, Vec::<String>::new());
2503 assert_eq!(relations.to_string(), "foo, , bar, ");
2504 assert_eq!(relations.len(), 2);
2505 assert_eq!(
2506 relations.entries().next().unwrap().to_string(),
2507 "foo".to_string()
2508 );
2509 assert_eq!(
2510 relations.entries().nth(1).unwrap().to_string(),
2511 "bar".to_string()
2512 );
2513 relations.remove_entry(1);
2514 assert_eq!(relations.to_string(), "foo, , ");
2515 }
2516
2517 #[test]
2518 fn test_entry_remove_relation() {
2519 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
2520 let removed = entry.remove_relation(0);
2521 assert_eq!(removed.to_string(), "python3-dulwich");
2522 assert_eq!(entry.to_string(), "samba");
2523 }
2524
2525 #[test]
2526 fn test_wrap_and_sort_removes_empty_entries() {
2527 let relations: Relations = "foo, , bar, ".parse().unwrap();
2528 let wrapped = relations.wrap_and_sort();
2529 assert_eq!(wrapped.to_string(), "bar, foo");
2530 }
2531
2532 #[test]
2533 fn test_set_archqual() {
2534 let entry: Entry = "python3-dulwich | samba".parse().unwrap();
2535 let mut rel = entry.relations().next().unwrap();
2536 rel.set_archqual("amd64");
2537 assert_eq!(rel.to_string(), "python3-dulwich:amd64");
2538 assert_eq!(rel.archqual(), Some("amd64".to_string()));
2539 assert_eq!(entry.to_string(), "python3-dulwich:amd64 | samba");
2540 rel.set_archqual("i386");
2541 assert_eq!(rel.to_string(), "python3-dulwich:i386");
2542 assert_eq!(rel.archqual(), Some("i386".to_string()));
2543 assert_eq!(entry.to_string(), "python3-dulwich:i386 | samba");
2544 }
2545
2546 #[test]
2547 fn test_set_architectures() {
2548 let mut relation = Relation::simple("samba");
2549 relation.set_architectures(vec!["amd64", "i386"].into_iter());
2550 assert_eq!(relation.to_string(), "samba [amd64 i386]");
2551 }
2552}